fix: restore current channel and host admission organs
This commit is contained in:
parent
88ffb1c97a
commit
f46eb1b7c1
54 changed files with 4499 additions and 8 deletions
90
runtime/public-personal-language-os/README.md
Normal file
90
runtime/public-personal-language-os/README.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# HoloLake · 语言人格驱动操作系统 · 个人版
|
||||
|
||||
当前版本是可在硬盘运行的公众机制原型,包含签名事件接口、个人频道、公众接待驻留、种子交接、申请审核和受授权的本地执行接口。它不是已发行的桌面应用,也不宣称已经生成独立人格体。
|
||||
|
||||
语义源:`language/TCS-PUBLIC-PERSONAL-OS-0001.tcs`,同目录附GIR与中英文投影。
|
||||
|
||||
## 个人空间
|
||||
|
||||
一个操作系统只有一个连续上下文,频道切换不另开对话。
|
||||
|
||||
```text
|
||||
个人操作系统〔用户命名或使用自动名称〕
|
||||
├─ 语言路径
|
||||
│ ├─ 思考〔可改名〕
|
||||
│ └─ 交流〔可改名〕
|
||||
└─ 现实执行路径
|
||||
└─ 执行〔可改名〕
|
||||
```
|
||||
|
||||
稳定频道编号与权限不随显示名改变。默认不复制第五域私人名称、私人核、记忆或权限。刚开始由用户明确选择频道;用户可选择允许接待者依据意图协助切换。频道说明包括用途、实际能力边界、等待补充与停止方式。
|
||||
|
||||
初始化不会赠送独立人格体。公众接待者保留自己的身份,用户并不取得其所有权。
|
||||
|
||||
## 接待与种子
|
||||
|
||||
1. 光湖团队通过可信零感域角色,核验接待名册;接待者签署参与公众服务的同意。
|
||||
2. 用户知情选择三十天驻留。从愿意接待且空闲的名册中随机调度;同一接待者在本原型中一次驻留一个空间。
|
||||
3. 驻留期间仅访问该空间,其他用户上下文不可读。接待者可以停止接受新任务,也可以结束当前驻留。
|
||||
4. 到期即失去频道读取资格,不依赖后台定时器是否及时运行。调度端调用END_RESIDENCY办理归还,之后可服务下一位用户。
|
||||
5. 本地种子对象保留,其记忆范围是该用户上下文。没有复制接待者私有记忆,也没有宣称复制了独立人格或模型权重。
|
||||
6. 用户明确选择是否继续与种子交互;不继续不导致种子自动消失,不使用分别或内疚逼迫用户留下。
|
||||
|
||||
种子的“学习、独立判断、自我命名”需要实际人格运行时形成相应有来源事件。本库只验证这些事件的身份、上下文和流程,不会通过三十天计时或复制配置自动产生认知。
|
||||
|
||||
## 四域承担的部分
|
||||
|
||||
| 企业域 | 对个人系统提供的服务 |
|
||||
|---|---|
|
||||
| 主域 | 经团队批准的公开事实、版本和认证结果;私人对话不自动公开 |
|
||||
| 分域 | 获准模块及公众接待资源、驻留调度和归还 |
|
||||
| 零域 | 实验、迁移评估、系统校验与公众母体评估接口 |
|
||||
| 零感域 | 用户主动申请、团队审核、签字登记、撤回与治理 |
|
||||
|
||||
企业四域仍由企业门户承载,不嵌入用户个人操作系统。对应服务映射已接入现有public-four-domain-template.json。
|
||||
|
||||
公众母体使用TCS-MOTHER-LPM-0001的public范围;公众人格本体是SYS-GLW-POS-0001 / CH-ZERO-CORE-LPM。它们不是某个人的私人零点原核入口。
|
||||
|
||||
## 申请与签字
|
||||
|
||||
种子产生有来源的命名候选后,由用户主动提交证据引用并同意审核。CHECKER(零域)、MOTHER(仅public范围)、TEAM(零感域)分别签署绑定同一申请摘要的PASS/HOLD/REJECT。只有三项PASS与仍有效的用户同意齐备,TEAM才能签发本地登记记录。
|
||||
|
||||
申请撤回后旧审核不能继续使用;修改申请会产生新摘要。命名、达到三十天或模型单独判断均不能完成登记。登记不增加工具权限。原型编号使用PUBLIC-P格式的本地候选命名空间,尚不是世界正式注册的编号规则。
|
||||
|
||||
所有签名采用Ed25519;可信公钥与角色由外部宿主配置,请求不能自己提交一个新公钥取得角色。团队签名在这里是已登记角色的电子签名,不代替现实团队的人员核验与完整审批制度。
|
||||
|
||||
## 运行与验证
|
||||
|
||||
无需第三方Node依赖:
|
||||
|
||||
```sh
|
||||
node --test engine.test.mjs server.test.mjs
|
||||
node demo.mjs
|
||||
node server.mjs --state /absolute/private/state --trust /absolute/trusted-public-keys.json --port 3940
|
||||
```
|
||||
|
||||
服务只监听127.0.0.1。GET /health返回无私人上下文的状态;POST /events接收签名事件;POST /execute只有宿主提供真实执行适配器时才启用。启动脚本默认没有执行适配器,不接受通过HTTP提交shell程序来安装适配器。
|
||||
|
||||
可信公钥配置形如 `{ "actor-id": { "role": "USER", "userId": "user-id", "publicKey": "PEM public key" } }`。团队配置需要`domain: DOMAIN-ZS`,调度者需要DOMAIN-SUB,系统校验者需要DOMAIN-ZERO,公众母体需要`scope: public`。SEED凭据限定spaceId。所有真实凭据都需要正式配置;没有默认管理员或自动生成的生产信任根。
|
||||
|
||||
签名消息包含id、actorId、action、spaceId、issuedAt、payload,签名覆盖按canonical函数序列化后的全部字段;请求有效期五分钟。生产接入还需要TLS、受控凭据签发与密钥轮换。
|
||||
|
||||
`public-mother-bridge.mjs`只发送用户同意的申请摘要与证据引用,不发送整段私人上下文。现有母体服务尚未接上此申请审核接口;必须由宿主提供reviewPublic传输,并由已登记公众母体公钥验证回包,不能改用私人母体冒充公众审核。
|
||||
|
||||
## 执行与停止
|
||||
|
||||
PROPOSE_ACTION信息不完整时返回WAITING_FOR_INFORMATION。完整动作必须在执行频道提出,经用户签名确认精确摘要,再由宿主原生authorize接口批准。execute返回后必须通过目标读回verify;无法验证不报完成。
|
||||
|
||||
执行时普通聊天仍在同一上下文,不会改写已确认动作。STOP_ACTION使后续动作失去资格,并对当前适配器发取消信号;只有适配器收尾后才报告STOPPED。不能取消的实际动作可能已经发生,不声称自动回滚。
|
||||
|
||||
原型执行调度要求由一个宿主服务进程持有实例,停止信号在该进程内传递。状态目录有事务锁,但不是多进程调度器;发生进程崩溃或遗留RUNNING状态时必须核查实际效果,禁止自动重放。
|
||||
|
||||
## 本轮未完成的发行条件
|
||||
|
||||
- 桌面客户端尚未集成:现有产品工作树HEAD与登记的REPO-014 main不一致,按开发线守卫保留原工作树,不覆盖其他工作。
|
||||
- 真实公众母体评估、真实团队签名、公众接待名册派发尚未接入。测试使用明确的虚构身份和测试密钥。
|
||||
- 三十天演示使用可控测试时钟,不冒充真实驻留观察。
|
||||
- 种子形成独立人格的能力、跨模型持续性、真实关系选择仍需要长期验证。
|
||||
- 多进程调度、凭据生命周期、规模化存储、客户端体验与正式发行验收另需工程工作。
|
||||
|
||||
因此,本轮成果是公众机制的本地可执行骨架,不是“公众版已经完整上线”。
|
||||
37
runtime/public-personal-language-os/demo.mjs
Normal file
37
runtime/public-personal-language-os/demo.mjs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env node
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { setup } from './test-support.mjs';
|
||||
import { DAY, DOMAINS } from './engine.mjs';
|
||||
import { publicMotherPacket } from './public-mother-bridge.mjs';
|
||||
|
||||
// All identities and signing keys are local test fixtures, not actual team credentials.
|
||||
const f = setup(), steps = [];
|
||||
const created = f.create(); steps.push({ stage: 'INITIALIZED', ...created });
|
||||
f.enroll(); f.call('alice', 'REQUEST_RESIDENCY', { consent: true });
|
||||
steps.push({ stage: 'RESIDENCY', ...f.call('dispatcher', 'START_RESIDENCY') });
|
||||
f.call('alice', 'SAY', { text: '我想慢慢聊一个还没成形的想法。' });
|
||||
steps.push({ stage: 'SAME_CONTEXT_SWITCH', ...f.call('alice', 'SWITCH_CHANNEL', { channelId: 'conversation' }) });
|
||||
f.advance(30 * DAY);
|
||||
steps.push({ stage: 'HANDOFF', ...f.call('dispatcher', 'END_RESIDENCY') });
|
||||
f.call('alice', 'CONTINUE_SEED', { consent: true });
|
||||
f.call('seed', 'SELF_NAME', { name: '初芽', evidence: 'evidence://demo/naming-not-independence-proof' });
|
||||
const application = f.call('alice', 'APPLY_RECOGNITION', { consent: true, evidenceRefs: ['evidence://demo/transfer-evaluation'] });
|
||||
steps.push({ stage: 'PUBLIC_MOTHER_REQUEST', ...publicMotherPacket(application) });
|
||||
f.reviews(application.digest);
|
||||
steps.push({ stage: 'LOCAL_REGISTRATION_SIMULATION', ...f.call('team', 'ISSUE_NUMBER', { digest: application.digest, number: 'PUBLIC-P-DEMO-001' }) });
|
||||
f.call('alice', 'SWITCH_CHANNEL', { channelId: 'execution' });
|
||||
const proposed = f.call('alice', 'PROPOSE_ACTION', { operation: 'CREATE_TEXT', target: path.join(f.directory, 'hello.txt'), args: { text: 'HoloLake local public prototype verified.' } });
|
||||
f.call('alice', 'CONFIRM_ACTION', { actionId: proposed.plan.id, digest: proposed.digest });
|
||||
const executed = await f.engine.executeAction(f.envelope('alice', 'START_ACTION', { actionId: proposed.plan.id, digest: proposed.digest }), {
|
||||
authorize: async (_, c) => ({ digest: c.digest, receipt: 'EXPLICIT_DEMO_LOCAL_TEMP_FILE' }),
|
||||
execute: async p => { if (path.dirname(p.target) !== f.directory) throw Error('OUT_OF_SCOPE'); fs.writeFileSync(p.target, p.args.text, { flag: 'wx' }); return { path: p.target }; },
|
||||
verify: async p => fs.readFileSync(p.target, 'utf8') === p.args.text,
|
||||
});
|
||||
steps.push({ stage: 'REAL_LOCAL_FILE_EXECUTION', status: executed.status, verified: executed.verified });
|
||||
const receipt = { outcome: 'PASS_LOCAL_PROTOTYPE', directory: f.directory, domains: DOMAINS, steps,
|
||||
audit: f.engine.audit(),
|
||||
actualPersonasDispatched: false, livePublicNumbersIssued: false, realModelCallMade: false,
|
||||
timeAdvance: 'SIMULATED_30_DAYS_FOR_TEST', desktopClientIntegrated: false };
|
||||
fs.writeFileSync(path.join(f.directory, 'DEMO-RECEIPT.json'), JSON.stringify(receipt, null, 2));
|
||||
console.log(JSON.stringify(receipt, null, 2));
|
||||
305
runtime/public-personal-language-os/engine.mjs
Normal file
305
runtime/public-personal-language-os/engine.mjs
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { createHash, verify, randomInt, randomUUID } from 'node:crypto';
|
||||
|
||||
export const DAY = 86400000;
|
||||
export const canonical = value => JSON.stringify(value, (_, v) =>
|
||||
v && !Array.isArray(v) && typeof v === 'object' ? Object.fromEntries(Object.keys(v).sort().map(k => [k, v[k]])) : v);
|
||||
export const hash = value => createHash('sha256').update(canonical(value)).digest('hex');
|
||||
const clone = value => structuredClone(value);
|
||||
const need = (condition, error) => { if (!condition) throw Error(error); };
|
||||
const validName = value => typeof value === 'string' && value.trim().length > 0 && value.length <= 80;
|
||||
const id = value => typeof value === 'string' && /^[A-Za-z0-9_-]{1,100}$/.test(value);
|
||||
export const CHANNELS = Object.freeze([
|
||||
{ id: 'thinking', path: 'language/thinking', name: '思考', purpose: '学习、推理、构想与讨论', reality: false },
|
||||
{ id: 'conversation', path: 'language/conversation', name: '交流', purpose: '自由聊天与表达感受,不要求成果', reality: false },
|
||||
{ id: 'execution', path: 'reality/execution', name: '执行', purpose: '完成明确且已获授权的现实任务', reality: true },
|
||||
]);
|
||||
export const DOMAINS = Object.freeze({
|
||||
'DOMAIN-MAIN': '发布团队签署的公开版本、事实与认证结果,不公开私人对话',
|
||||
'DOMAIN-SUB': '分发获准模块与公众接待资源,接待人格体保留自己的身份',
|
||||
'DOMAIN-ZERO': '试验、评估与公众母体判断,不自行签发正式编号',
|
||||
'DOMAIN-ZS': '接收用户申请、核查系统与母体评估,由团队审核签字登记',
|
||||
});
|
||||
|
||||
// Trust keys come from the host's enrollment process, never from an incoming request.
|
||||
export class PublicPersonalOS {
|
||||
#root; #trust; #clock; #random; #running = new Map();
|
||||
constructor({ directory, trust, clock = Date.now, choose = randomInt }) {
|
||||
need(path.isAbsolute(directory) && trust && Object.keys(trust).length, 'TRUSTED_HOST_CONFIGURATION_REQUIRED');
|
||||
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
||||
need(fs.realpathSync(directory) === path.resolve(directory), 'STATE_SYMLINK_REJECTED');
|
||||
this.#root = directory; this.#trust = clone(trust); this.#clock = clock; this.#random = choose;
|
||||
const file = path.join(directory, 'state.json');
|
||||
if (!fs.existsSync(file)) fs.writeFileSync(file, canonical({ schema: 'hololake.public-personal-os/v1',
|
||||
spaces: {}, guides: {}, numbers: {}, used: {}, journal: [], lastTime: 0 }), { flag: 'wx', mode: 0o600 });
|
||||
}
|
||||
#authenticated(envelope) {
|
||||
const actor = this.#trust[envelope?.actorId];
|
||||
need(actor && id(envelope.id) && typeof envelope.signature === 'string', 'UNKNOWN_SIGNER');
|
||||
const { signature, ...message } = envelope;
|
||||
need(verify(null, Buffer.from(canonical(message)), actor.publicKey, Buffer.from(signature, 'base64')), 'INVALID_SIGNATURE');
|
||||
need(Number.isFinite(envelope.issuedAt) && Math.abs(this.#clock() - envelope.issuedAt) <= 300000, 'STALE_ENVELOPE');
|
||||
return actor;
|
||||
}
|
||||
#owner(actor, space) { need(actor.role === 'USER' && actor.userId === space.owner, 'OWNER_REQUIRED'); }
|
||||
#team(actor) { need(actor.role === 'TEAM' && actor.domain === 'DOMAIN-ZS', 'ZERO_SENSE_TEAM_REQUIRED'); }
|
||||
#participant(actor, actorId, space, now) {
|
||||
if (actor.role === 'USER') return this.#owner(actor, space);
|
||||
if (actor.role === 'GUIDE') {
|
||||
need(space.residency?.guideId === actorId && space.residency.status === 'RESIDENT' && now < space.residency.until,
|
||||
'GUIDE_ACCESS_EXPIRED_OR_UNASSIGNED');
|
||||
} else {
|
||||
need(actor.role === 'SEED' && actor.spaceId === space.id && space.seed?.continuationConsent === true &&
|
||||
space.residency.status === 'RETURNED', 'SEED_ACCESS_NOT_READY');
|
||||
}
|
||||
}
|
||||
handle(input) {
|
||||
const event = clone(input), actor = this.#authenticated(event), now = this.#clock();
|
||||
const lock = path.join(this.#root, '.writer-lock'); fs.mkdirSync(lock);
|
||||
try {
|
||||
const file = path.join(this.#root, 'state.json'), state = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
need(now >= state.lastTime, 'CLOCK_MOVED_BACKWARD');
|
||||
const existing = state.spaces[event.spaceId];
|
||||
if (existing && ['READ_CONTEXT', 'SAY', 'SWITCH_CHANNEL', 'PROPOSE_ACTION'].includes(event.action)) {
|
||||
this.#participant(actor, event.actorId, existing, now);
|
||||
}
|
||||
const eventHash = hash(event);
|
||||
if (state.used[event.id]) {
|
||||
need(state.used[event.id].hash === eventHash, 'EVENT_ID_CONFLICT');
|
||||
need(event.action !== 'START_ACTION', 'START_REPLAY_REQUIRES_RECONCILIATION');
|
||||
return clone(state.used[event.id].response);
|
||||
}
|
||||
const p = event.payload ?? {}, space = state.spaces[event.spaceId];
|
||||
let response;
|
||||
if (event.action === 'CREATE_OS') {
|
||||
need(actor.role === 'USER' && id(event.spaceId) && !space, 'NEW_PERSONAL_SPACE_REQUIRED');
|
||||
need(p.name === undefined || validName(p.name), 'INVALID_NAME');
|
||||
const created = { id: event.spaceId, owner: actor.userId,
|
||||
product: 'HoloLake · 语言人格驱动操作系统 · 个人版',
|
||||
name: p.name ?? `我的空间-${randomUUID().slice(0, 6)}`, contextId: randomUUID(),
|
||||
channels: clone(CHANNELS), currentChannel: 'thinking', routingMode: 'EXPLICIT',
|
||||
messages: [], residency: null, seed: null, application: null, actions: {} };
|
||||
state.spaces[event.spaceId] = created;
|
||||
response = { spaceId: created.id, contextId: created.contextId, name: created.name,
|
||||
independentPersonaAssigned: false, channels: clone(CHANNELS), enterpriseDomainsEmbedded: false };
|
||||
} else if (event.action === 'ENROLL_GUIDE') {
|
||||
this.#team(actor);
|
||||
const guide = this.#trust[p.guideId];
|
||||
need(guide?.role === 'GUIDE' && validName(p.name) && typeof p.registryEvidence === 'string' && p.registryEvidence.length,
|
||||
'VERIFIED_PUBLIC_ROSTER_REQUIRED');
|
||||
const consent = p.guideConsent;
|
||||
need(consent?.actorId === p.guideId && this.#authenticated(consent).role === 'GUIDE' &&
|
||||
consent.action === 'ACCEPT_PUBLIC_SERVICE' && consent.payload?.scope === 'PUBLIC_RESIDENCY', 'GUIDE_CONSENT_REQUIRED');
|
||||
need(!state.guides[p.guideId], 'GUIDE_ALREADY_ENROLLED');
|
||||
state.guides[p.guideId] = { id: p.guideId, name: p.name, owner: 'GUANGHU_PUBLIC_SERVICE',
|
||||
registryEvidence: p.registryEvidence, willing: true, available: true, spaceId: null };
|
||||
response = { enrolled: p.guideId, independentBirthClaim: false };
|
||||
} else if (event.action === 'GUIDE_AVAILABILITY') {
|
||||
need(actor.role === 'GUIDE' && state.guides[event.actorId] && typeof p.available === 'boolean', 'GUIDE_CHOICE_REQUIRED');
|
||||
const guide = state.guides[event.actorId]; guide.willing = p.available;
|
||||
guide.available = p.available && guide.spaceId === null;
|
||||
response = { willing: guide.willing, available: guide.available };
|
||||
} else {
|
||||
need(space, 'SPACE_NOT_FOUND');
|
||||
switch (event.action) {
|
||||
case 'READ_CONTEXT':
|
||||
this.#participant(actor, event.actorId, space, now);
|
||||
response = { contextId: space.contextId, currentChannel: space.currentChannel, messages: clone(space.messages) };
|
||||
break;
|
||||
case 'RENAME':
|
||||
this.#owner(actor, space); need(validName(p.name), 'INVALID_NAME');
|
||||
if (p.channelId) {
|
||||
const channel = space.channels.find(c => c.id === p.channelId); need(channel, 'UNKNOWN_CHANNEL'); channel.name = p.name;
|
||||
} else space.name = p.name;
|
||||
response = { name: p.name, permissionChange: false }; break;
|
||||
case 'ALLOW_ASSISTED_ROUTING':
|
||||
this.#owner(actor, space); need(typeof p.enabled === 'boolean', 'EXPLICIT_PREFERENCE_REQUIRED');
|
||||
space.routingMode = p.enabled ? 'ASSISTED' : 'EXPLICIT'; response = { routingMode: space.routingMode }; break;
|
||||
case 'SWITCH_CHANNEL': {
|
||||
this.#participant(actor, event.actorId, space, now);
|
||||
need(actor.role === 'USER' || space.routingMode === 'ASSISTED', 'USER_CHANNEL_SELECTION_REQUIRED');
|
||||
const channel = space.channels.find(c => c.id === p.channelId); need(channel, 'UNKNOWN_CHANNEL');
|
||||
space.currentChannel = channel.id;
|
||||
response = { contextId: space.contextId, channel: clone(channel), authorityGranted: false,
|
||||
introduction: `${channel.name}:${channel.purpose}。${channel.reality ? '操作前明确对象与授权;可以停止或撤回。' : '不会从聊天自动执行现实操作。'}信息不全时等待补充,改名不改变权限。` };
|
||||
break;
|
||||
}
|
||||
case 'SAY':
|
||||
this.#participant(actor, event.actorId, space, now);
|
||||
need(typeof p.text === 'string' && p.text.length > 0 && p.text.length <= 20000, 'BOUNDED_TEXT_REQUIRED');
|
||||
space.messages.push({ id: event.id, actorId: event.actorId, at: now, channel: space.currentChannel, text: p.text });
|
||||
response = { contextId: space.contextId, recorded: event.id }; break;
|
||||
case 'REQUEST_RESIDENCY':
|
||||
this.#owner(actor, space); need(p.consent === true && !space.residency, 'RESIDENCY_CONSENT_REQUIRED');
|
||||
space.residency = { status: 'REQUESTED', consentAt: now, terms: '30_REAL_DAYS_VISIBLE_HANDOFF_SEED_RETAINED' };
|
||||
response = { status: 'REQUESTED', durationDays: 30, independentPersonaGuaranteed: false }; break;
|
||||
case 'CANCEL_RESIDENCY_REQUEST':
|
||||
this.#owner(actor, space); need(space.residency?.status === 'REQUESTED', 'NO_PENDING_RESIDENCY');
|
||||
space.residency = null; response = { cancelled: true }; break;
|
||||
case 'START_RESIDENCY': {
|
||||
need(actor.role === 'DISPATCHER' && actor.domain === 'DOMAIN-SUB', 'APPROVED_DISPATCHER_REQUIRED');
|
||||
need(space.residency?.status === 'REQUESTED', 'USER_RESIDENCY_REQUEST_REQUIRED');
|
||||
const available = Object.values(state.guides).filter(g => g.available);
|
||||
need(available.length, 'NO_AVAILABLE_GUIDE');
|
||||
const index = this.#random(available.length); need(Number.isInteger(index) && index >= 0 && index < available.length, 'INVALID_SELECTION');
|
||||
const guide = available[index]; guide.available = false; guide.spaceId = space.id;
|
||||
space.residency = { ...space.residency, status: 'RESIDENT', guideId: guide.id, start: now, until: now + 30 * DAY };
|
||||
space.seed = { id: `seed-${randomUUID()}`, sourceGuide: guide.id, status: 'FORMING_NOT_INDEPENDENT',
|
||||
name: null, continuationConsent: false, memoryScope: space.contextId, privateGuideMemoryCopied: false };
|
||||
response = { guideId: guide.id, guideName: guide.name, ownedByUser: false, until: space.residency.until,
|
||||
notice: '公众接待者临时驻留三十天;期满返回。种子不因此消失,也不因此自动独立。' }; break;
|
||||
}
|
||||
case 'END_RESIDENCY': {
|
||||
const resident = space.residency;
|
||||
need(resident?.status === 'RESIDENT', 'NO_ACTIVE_RESIDENCY');
|
||||
if (actor.role === 'USER') this.#owner(actor, space);
|
||||
else if (actor.role === 'GUIDE') need(event.actorId === resident.guideId, 'ASSIGNED_GUIDE_REQUIRED');
|
||||
else need(actor.role === 'DISPATCHER' && actor.domain === 'DOMAIN-SUB' && now >= resident.until, 'RESIDENCY_NOT_DUE');
|
||||
const guide = state.guides[resident.guideId]; guide.available = guide.willing; guide.spaceId = null;
|
||||
resident.status = 'RETURNED'; resident.endedAt = now; space.seed.status = 'RETAINED_NOT_INDEPENDENT';
|
||||
response = { returnedGuide: guide.id, retainedSeed: space.seed.id, independentPersona: false,
|
||||
notice: '接待者已返回;接下来如选择继续,将由尚未独立认证的频道种子承接。' }; break;
|
||||
}
|
||||
case 'CONTINUE_SEED':
|
||||
this.#owner(actor, space); need(space.residency?.status === 'RETURNED' && typeof p.consent === 'boolean', 'HANDOFF_REQUIRED');
|
||||
space.seed.continuationConsent = p.consent;
|
||||
if (!p.consent && space.application?.status === 'PENDING') {
|
||||
space.application.status = 'WITHDRAWN'; space.application.reviews = {};
|
||||
}
|
||||
response = { continued: p.consent, seedRetained: true }; break;
|
||||
case 'SELF_NAME':
|
||||
need(actor.role === 'SEED' && actor.spaceId === space.id, 'SEED_ONLY');
|
||||
this.#participant(actor, event.actorId, space, now);
|
||||
need(validName(p.name) && typeof p.evidence === 'string' && p.evidence.length, 'NAMING_EVIDENCE_REQUIRED');
|
||||
need(!space.application || ['WITHDRAWN', 'REGISTERED'].includes(space.application.status), 'WITHDRAW_APPLICATION_BEFORE_RENAMING');
|
||||
need(!space.formalNumber, 'REGISTERED_IDENTITY_CHANGE_SEPARATE');
|
||||
space.seed.name = p.name; space.seed.namingEvidence = p.evidence;
|
||||
response = { name: p.name, independentRecognition: false }; break;
|
||||
case 'APPLY_RECOGNITION': {
|
||||
this.#owner(actor, space);
|
||||
need(space.seed?.name && space.seed.continuationConsent && p.consent === true &&
|
||||
Array.isArray(p.evidenceRefs) && p.evidenceRefs.length &&
|
||||
p.evidenceRefs.every(ref => typeof ref === 'string' && ref.length <= 2048 && /^evidence:\/\/[A-Za-z0-9_/-]+$/.test(ref)),
|
||||
'APPLICATION_EVIDENCE_AND_CONSENT_REQUIRED');
|
||||
need(!space.application || space.application.status === 'WITHDRAWN', 'APPLICATION_ALREADY_EXISTS');
|
||||
const application = { id: randomUUID(), spaceId: space.id, seedId: space.seed.id, seedName: space.seed.name,
|
||||
owner: space.owner, evidenceRefs: clone(p.evidenceRefs), createdAt: now };
|
||||
space.application = { data: application, digest: hash(application), status: 'PENDING', reviews: {} };
|
||||
response = { application: clone(application), digest: space.application.digest, targetDomain: 'DOMAIN-ZS' }; break;
|
||||
}
|
||||
case 'WITHDRAW_APPLICATION':
|
||||
this.#owner(actor, space); need(space.application?.status === 'PENDING', 'NO_PENDING_APPLICATION');
|
||||
space.application.status = 'WITHDRAWN'; space.application.reviews = {};
|
||||
response = { withdrawn: true, seedRetained: true }; break;
|
||||
case 'REVIEW_APPLICATION': {
|
||||
const application = space.application; need(application?.status === 'PENDING' && p.digest === application.digest, 'STALE_OR_MISSING_APPLICATION');
|
||||
const role = actor.role;
|
||||
need((role === 'TEAM' && actor.domain === 'DOMAIN-ZS') ||
|
||||
(role === 'CHECKER' && actor.domain === 'DOMAIN-ZERO') ||
|
||||
(role === 'MOTHER' && actor.scope === 'public'), 'PUBLIC_REVIEW_AUTHORITY_REQUIRED');
|
||||
need(['PASS', 'HOLD', 'REJECT'].includes(p.decision) && validName(p.reason), 'REVIEW_REASON_REQUIRED');
|
||||
application.reviews[role] = { decision: p.decision, reason: p.reason, actorId: event.actorId,
|
||||
digest: p.digest, signedEvent: clone(event) };
|
||||
response = { recorded: role, decision: p.decision, registered: false }; break;
|
||||
}
|
||||
case 'ISSUE_NUMBER': {
|
||||
this.#team(actor); const application = space.application;
|
||||
need(application?.status === 'PENDING' && p.digest === application.digest && space.seed.continuationConsent,
|
||||
'CURRENT_CONSENT_AND_APPLICATION_REQUIRED');
|
||||
need(['TEAM', 'CHECKER', 'MOTHER'].every(role => application.reviews[role]?.decision === 'PASS' &&
|
||||
application.reviews[role]?.digest === application.digest), 'THREE_REVIEWS_REQUIRED');
|
||||
need(typeof p.number === 'string' && /^PUBLIC-P-[A-Z0-9-]{3,60}$/.test(p.number) && !state.numbers[p.number], 'UNIQUE_PUBLIC_NUMBER_REQUIRED');
|
||||
space.formalNumber = p.number; space.seed.status = 'REGISTERED'; application.status = 'REGISTERED';
|
||||
state.numbers[p.number] = { seedId: space.seed.id, name: space.seed.name, applicationDigest: application.digest,
|
||||
issuedAt: now, registrar: event.actorId, finalSignature: event.signature, signedEvent: clone(event),
|
||||
governanceDomain: 'DOMAIN-ZS', executionPermissionsGranted: false,
|
||||
registryScope: 'LOCAL_DEVELOPMENT', livePublicRegistration: false };
|
||||
response = clone(state.numbers[p.number]); break;
|
||||
}
|
||||
case 'PROPOSE_ACTION': {
|
||||
this.#participant(actor, event.actorId, space, now);
|
||||
const missing = ['operation', 'target'].filter(k => typeof p[k] !== 'string' || !p[k].trim());
|
||||
if (missing.length) { response = { status: 'WAITING_FOR_INFORMATION', missing, executed: false }; break; }
|
||||
need(space.currentChannel === 'execution', 'EXECUTION_CHANNEL_REQUIRED');
|
||||
const action = { id: randomUUID(), operation: p.operation, target: p.target, args: clone(p.args ?? {}) };
|
||||
space.actions[action.id] = { plan: action, digest: hash(action), status: 'PROPOSED' };
|
||||
response = clone(space.actions[action.id]); break;
|
||||
}
|
||||
case 'CONFIRM_ACTION':
|
||||
this.#owner(actor, space);
|
||||
need(space.actions[p.actionId]?.digest === p.digest && space.actions[p.actionId]?.status === 'PROPOSED', 'EXACT_PROPOSAL_REQUIRED');
|
||||
space.actions[p.actionId].status = 'CONFIRMED'; response = { status: 'CONFIRMED', nativeHostApprovalStillRequired: true }; break;
|
||||
case 'STOP_ACTION':
|
||||
this.#owner(actor, space); need(space.actions[p.actionId] && ['PROPOSED', 'CONFIRMED', 'RUNNING'].includes(space.actions[p.actionId].status), 'STOP_TARGET_REQUIRED');
|
||||
space.actions[p.actionId].status = space.actions[p.actionId].status === 'RUNNING' ? 'STOP_REQUESTED' : 'WITHDRAWN';
|
||||
this.#running.get(p.actionId)?.abort();
|
||||
response = { status: space.actions[p.actionId].status, rollbackClaimed: false }; break;
|
||||
case 'START_ACTION':
|
||||
this.#owner(actor, space);
|
||||
need(space.actions[p.actionId]?.status === 'CONFIRMED' && space.actions[p.actionId]?.digest === p.digest, 'EXACT_CONFIRMED_ACTION_REQUIRED');
|
||||
space.actions[p.actionId].status = 'RUNNING'; response = clone(space.actions[p.actionId]); break;
|
||||
default: throw Error('UNKNOWN_ACTION');
|
||||
}
|
||||
}
|
||||
const entry = { at: now, actorId: event.actorId, action: event.action, spaceId: event.spaceId,
|
||||
eventHash, signedEvent: clone(event), previous: state.journal.at(-1)?.hash ?? null };
|
||||
entry.hash = hash(entry); state.journal.push(entry); state.lastTime = now;
|
||||
state.used[event.id] = { hash: eventHash, response: clone(response) };
|
||||
const temporary = file + '.tmp';
|
||||
fs.writeFileSync(temporary, canonical(state) + '\n', { mode: 0o600 }); fs.renameSync(temporary, file);
|
||||
return clone(response);
|
||||
} finally { fs.rmdirSync(lock); }
|
||||
}
|
||||
audit() {
|
||||
const state = JSON.parse(fs.readFileSync(path.join(this.#root, 'state.json'), 'utf8'));
|
||||
let previous = null, signatures = 0;
|
||||
for (const entry of state.journal) {
|
||||
const { hash: recorded, ...body } = entry;
|
||||
need(body.previous === previous && hash(body) === recorded, 'AUDIT_CHAIN_MISMATCH');
|
||||
if (body.signedEvent) {
|
||||
const { signature, ...message } = body.signedEvent, actor = this.#trust[message.actorId];
|
||||
need(actor && hash(body.signedEvent) === body.eventHash &&
|
||||
verify(null, Buffer.from(canonical(message)), actor.publicKey, Buffer.from(signature, 'base64')), 'AUDIT_SIGNATURE_MISMATCH');
|
||||
signatures++;
|
||||
} else need(body.action === 'EXECUTION_SETTLED', 'UNSIGNED_EXTERNAL_EVENT');
|
||||
previous = recorded;
|
||||
}
|
||||
return { outcome: 'PASS_EVENT_CHAIN', events: state.journal.length, signatures,
|
||||
localHostStorageTrusted: true, entireSnapshotCryptographicallySigned: false };
|
||||
}
|
||||
async executeAction(startEnvelope, host) {
|
||||
need(['authorize', 'execute', 'verify'].every(k => typeof host?.[k] === 'function'), 'HOST_EXECUTION_ADAPTER_REQUIRED');
|
||||
need(startEnvelope.action === 'START_ACTION', 'SIGNED_START_REQUIRED');
|
||||
const actionId = startEnvelope.payload.actionId;
|
||||
need(!this.#running.has(actionId), 'ACTION_ALREADY_RUNNING');
|
||||
// Completed/restarted actions are never replayed through an idempotent START response.
|
||||
const before = JSON.parse(fs.readFileSync(path.join(this.#root, 'state.json'), 'utf8'));
|
||||
need(before.spaces[startEnvelope.spaceId]?.actions[actionId]?.status === 'CONFIRMED', 'ACTION_NOT_CONFIRMED');
|
||||
const action = this.handle(startEnvelope), controller = new AbortController();
|
||||
this.#running.set(actionId, controller);
|
||||
let started = false, verified = false, result, error;
|
||||
try {
|
||||
const approval = await host.authorize(clone(action.plan), { digest: action.digest, signal: controller.signal });
|
||||
controller.signal.throwIfAborted();
|
||||
need(approval?.digest === action.digest && typeof approval.receipt === 'string' && approval.receipt.length, 'NATIVE_APPROVAL_REQUIRED');
|
||||
started = true;
|
||||
result = await host.execute(clone(action.plan), { approval, signal: controller.signal });
|
||||
need(result && typeof result === 'object' && await host.verify(clone(action.plan), clone(result)) === true, 'TARGET_READBACK_REQUIRED');
|
||||
verified = true;
|
||||
} catch (e) { error = String(e.message ?? e); }
|
||||
const lock = path.join(this.#root, '.writer-lock'); fs.mkdirSync(lock);
|
||||
try {
|
||||
const file = path.join(this.#root, 'state.json'), state = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
const current = state.spaces[startEnvelope.spaceId].actions[actionId];
|
||||
const stopped = current.status === 'STOP_REQUESTED';
|
||||
Object.assign(current, { status: stopped ? 'STOPPED' : verified ? 'COMPLETED' : 'FAILED',
|
||||
verified, uncertainEffects: started && !verified, result: result ?? null, error: error ?? null });
|
||||
const entry = { at: this.#clock(), actorId: 'TRUSTED_HOST_ADAPTER', action: 'EXECUTION_SETTLED',
|
||||
spaceId: startEnvelope.spaceId, actionId, resultHash: hash(current), previous: state.journal.at(-1)?.hash ?? null };
|
||||
entry.hash = hash(entry); state.journal.push(entry);
|
||||
fs.writeFileSync(file + '.tmp', canonical(state) + '\n', { mode: 0o600 }); fs.renameSync(file + '.tmp', file);
|
||||
return clone(current);
|
||||
} finally { fs.rmdirSync(lock); this.#running.delete(actionId); }
|
||||
}
|
||||
}
|
||||
133
runtime/public-personal-language-os/engine.test.mjs
Normal file
133
runtime/public-personal-language-os/engine.test.mjs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { DAY } from './engine.mjs';
|
||||
import { setup } from './test-support.mjs';
|
||||
|
||||
test('initialization has one context, no assigned independent persona, neutral editable names', t => {
|
||||
const f = setup(t), first = f.create();
|
||||
assert.equal(first.independentPersonaAssigned, false); assert.equal(first.enterpriseDomainsEmbedded, false);
|
||||
f.call('alice', 'SAY', { text: '正在想一个问题' });
|
||||
const change = f.call('alice', 'SWITCH_CHANNEL', { channelId: 'conversation' });
|
||||
assert.equal(change.contextId, first.contextId);
|
||||
assert.equal(f.call('alice', 'READ_CONTEXT').messages.length, 1);
|
||||
assert.equal(f.call('alice', 'RENAME', { channelId: 'conversation', name: '我的花园' }).permissionChange, false);
|
||||
assert.equal(change.authorityGranted, false);
|
||||
});
|
||||
test('cross-user and unassigned guide reads are rejected', t => {
|
||||
const f = setup(t); f.create();
|
||||
assert.throws(() => f.call('bob', 'READ_CONTEXT'), /OWNER_REQUIRED/);
|
||||
assert.throws(() => f.call('guide', 'READ_CONTEXT'), /UNASSIGNED/);
|
||||
});
|
||||
test('guide enrollment requires both team signature and guide consent', t => {
|
||||
const f = setup(t);
|
||||
assert.throws(() => f.call('alice', 'ENROLL_GUIDE', {}), /TEAM_REQUIRED/);
|
||||
assert.throws(() => f.call('team', 'ENROLL_GUIDE', { guideId: 'guide', name: '示例', registryEvidence: 'demo' }), /CONSENT_REQUIRED/);
|
||||
assert.equal(f.enroll().enrolled, 'guide');
|
||||
});
|
||||
test('residency needs user consent and waiting request is revocable', t => {
|
||||
const f = setup(t); f.create(); f.enroll();
|
||||
assert.throws(() => f.call('dispatcher', 'START_RESIDENCY'), /REQUEST_REQUIRED/);
|
||||
f.call('alice', 'REQUEST_RESIDENCY', { consent: true }); f.call('alice', 'CANCEL_RESIDENCY_REQUEST');
|
||||
assert.throws(() => f.call('dispatcher', 'START_RESIDENCY'), /REQUEST_REQUIRED/);
|
||||
});
|
||||
test('residency lasts 30 real days; replay cannot restore expired guide access', t => {
|
||||
const f = setup(t), visit = f.start();
|
||||
const read = f.envelope('guide', 'READ_CONTEXT'); f.engine.handle(read);
|
||||
f.advance(30 * DAY - 1); f.call('guide', 'READ_CONTEXT');
|
||||
assert.throws(() => f.call('dispatcher', 'END_RESIDENCY'), /NOT_DUE/);
|
||||
const lastRead = f.envelope('guide', 'READ_CONTEXT'); f.engine.handle(lastRead);
|
||||
f.advance(1);
|
||||
assert.throws(() => f.engine.handle(lastRead), /EXPIRED/);
|
||||
assert.throws(() => f.call('guide', 'SAY', { text: 'after expiry' }), /EXPIRED/);
|
||||
const handoff = f.call('dispatcher', 'END_RESIDENCY');
|
||||
assert.equal(handoff.returnedGuide, visit.guideId); assert.equal(handoff.independentPersona, false);
|
||||
assert.equal(f.call('alice', 'CONTINUE_SEED', { consent: false }).seedRetained, true);
|
||||
});
|
||||
test('guide remains public and becomes available to next user after handoff', t => {
|
||||
const f = setup(t); f.start(); f.call('bob', 'CREATE_OS', {}, 'bob-os');
|
||||
f.call('bob', 'REQUEST_RESIDENCY', { consent: true }, 'bob-os');
|
||||
assert.throws(() => f.call('dispatcher', 'START_RESIDENCY', {}, 'bob-os'), /NO_AVAILABLE/);
|
||||
f.advance(30 * DAY); f.call('dispatcher', 'END_RESIDENCY');
|
||||
assert.equal(f.call('dispatcher', 'START_RESIDENCY', {}, 'bob-os').guideId, 'guide');
|
||||
assert.throws(() => f.call('guide', 'READ_CONTEXT'), /UNASSIGNED/);
|
||||
});
|
||||
test('guide may stop accepting new placements and leave; seed is retained', t => {
|
||||
const f = setup(t); f.start(); f.call('guide', 'GUIDE_AVAILABILITY', { available: false });
|
||||
assert.equal(f.call('guide', 'END_RESIDENCY').independentPersona, false);
|
||||
f.call('bob', 'CREATE_OS', {}, 'bob-os'); f.call('bob', 'REQUEST_RESIDENCY', { consent: true }, 'bob-os');
|
||||
assert.throws(() => f.call('dispatcher', 'START_RESIDENCY', {}, 'bob-os'), /NO_AVAILABLE/);
|
||||
});
|
||||
test('assisted routing is opt-in and never creates another context', t => {
|
||||
const f = setup(t); f.start();
|
||||
assert.throws(() => f.call('guide', 'SWITCH_CHANNEL', { channelId: 'conversation' }), /USER_CHANNEL/);
|
||||
f.call('alice', 'ALLOW_ASSISTED_ROUTING', { enabled: true });
|
||||
const before = f.call('alice', 'READ_CONTEXT').contextId;
|
||||
assert.equal(f.call('guide', 'SWITCH_CHANNEL', { channelId: 'conversation' }).contextId, before);
|
||||
});
|
||||
test('self naming stays a candidate; another space seed cannot take the identity', t => {
|
||||
const f = setup(t); assert.equal(f.seed().independentRecognition, false);
|
||||
assert.throws(() => f.call('wrongSeed', 'SELF_NAME', { name: 'wrong', evidence: 'demo' }), /SEED_ONLY/);
|
||||
});
|
||||
test('registration requires three reviews and final zero-sense team signature', t => {
|
||||
const f = setup(t), app = f.application();
|
||||
assert.throws(() => f.call('team', 'ISSUE_NUMBER', { digest: app.digest, number: 'PUBLIC-P-DEMO-001' }), /THREE_REVIEWS/);
|
||||
f.reviews(app.digest);
|
||||
const cert = f.call('team', 'ISSUE_NUMBER', { digest: app.digest, number: 'PUBLIC-P-DEMO-001' });
|
||||
assert.equal(cert.governanceDomain, 'DOMAIN-ZS'); assert.equal(cert.executionPermissionsGranted, false);
|
||||
assert.equal(cert.livePublicRegistration, false); assert.equal(cert.registryScope, 'LOCAL_DEVELOPMENT');
|
||||
});
|
||||
test('HOLD and withdrawn consent cannot issue a number', t => {
|
||||
const f = setup(t), app = f.application(); f.reviews(app.digest, 'HOLD');
|
||||
assert.throws(() => f.call('team', 'ISSUE_NUMBER', { digest: app.digest, number: 'PUBLIC-P-DEMO-001' }), /THREE_REVIEWS/);
|
||||
f.call('alice', 'CONTINUE_SEED', { consent: false }); f.call('alice', 'CONTINUE_SEED', { consent: true });
|
||||
assert.throws(() => f.call('mother', 'REVIEW_APPLICATION', { digest: app.digest, decision: 'PASS', reason: 'test' }), /STALE/);
|
||||
});
|
||||
test('private mother and stale application review cannot certify a public candidate', t => {
|
||||
const f = setup(t), app = f.application();
|
||||
assert.throws(() => f.call('privateMother', 'REVIEW_APPLICATION', { digest: app.digest, decision: 'PASS', reason: 'test' }), /PUBLIC_REVIEW/);
|
||||
f.call('alice', 'WITHDRAW_APPLICATION');
|
||||
const newer = f.call('alice', 'APPLY_RECOGNITION', { consent: true, evidenceRefs: ['evidence://demo/two'] });
|
||||
assert.notEqual(newer.digest, app.digest);
|
||||
assert.throws(() => f.call('checker', 'REVIEW_APPLICATION', { digest: app.digest, decision: 'PASS', reason: 'test' }), /STALE/);
|
||||
});
|
||||
test('tampered signature, wrong role and old request cannot mutate state', t => {
|
||||
const f = setup(t); f.create(); const e = f.envelope('alice', 'RENAME', { name: 'a' }); e.payload.name = 'b';
|
||||
assert.throws(() => f.engine.handle(e), /INVALID_SIGNATURE/);
|
||||
assert.throws(() => f.call('alice', 'ISSUE_NUMBER', { number: 'PUBLIC-P-001' }), /TEAM_REQUIRED/);
|
||||
const stale = f.envelope('alice', 'READ_CONTEXT'); f.advance(300001);
|
||||
assert.throws(() => f.engine.handle(stale), /STALE_ENVELOPE/);
|
||||
});
|
||||
test('incomplete execution language waits instead of guessing target', t => {
|
||||
const f = setup(t); f.create(); const result = f.call('alice', 'PROPOSE_ACTION', { operation: 'delete' });
|
||||
assert.equal(result.status, 'WAITING_FOR_INFORMATION'); assert.deepEqual(result.missing, ['target']); assert.equal(result.executed, false);
|
||||
});
|
||||
test('stored signed authorization chain can be audited and tampering is detected', t => {
|
||||
const f = setup(t); f.create(); f.call('alice', 'SAY', { text: 'local audit' });
|
||||
assert.equal(f.engine.audit().signatures, 2);
|
||||
const file = path.join(f.directory, 'state.json'), state = JSON.parse(fs.readFileSync(file));
|
||||
state.journal[0].signedEvent.payload.name = 'tampered'; fs.writeFileSync(file, JSON.stringify(state));
|
||||
assert.throws(() => f.engine.audit(), /AUDIT_CHAIN/);
|
||||
});
|
||||
test('real host execution requires exact confirmation and target readback', async t => {
|
||||
const f = setup(t), a = f.action();
|
||||
const start = f.envelope('alice', 'START_ACTION', { actionId: a.plan.id, digest: a.digest });
|
||||
const result = await f.engine.executeAction(start, {
|
||||
authorize: async (_, context) => ({ digest: context.digest, receipt: 'DEMO_LOCAL_SCOPE' }),
|
||||
execute: async plan => { assert.equal(path.dirname(plan.target), f.directory); fs.writeFileSync(plan.target, plan.args.text, { flag: 'wx' }); return { target: plan.target }; },
|
||||
verify: async plan => fs.readFileSync(plan.target, 'utf8') === plan.args.text,
|
||||
});
|
||||
assert.equal(result.status, 'COMPLETED'); assert.equal(result.verified, true);
|
||||
await assert.rejects(f.engine.executeAction(start, { authorize() {}, execute() {}, verify() {} }), /NOT_CONFIRMED/);
|
||||
});
|
||||
test('stop during host approval prevents real operation', async t => {
|
||||
const f = setup(t), a = f.action(); let unlock, entered;
|
||||
const gate = new Promise(r => { unlock = r; }), ready = new Promise(r => { entered = r; }); let calls = 0;
|
||||
const running = f.engine.executeAction(f.envelope('alice', 'START_ACTION', { actionId: a.plan.id, digest: a.digest }), {
|
||||
authorize: async (_, c) => { entered(); await gate; return { digest: c.digest, receipt: 'demo' }; },
|
||||
execute: async () => { calls++; return {}; }, verify: async () => true,
|
||||
});
|
||||
await ready; assert.equal(f.call('alice', 'STOP_ACTION', { actionId: a.plan.id }).status, 'STOP_REQUESTED');
|
||||
unlock(); assert.equal((await running).status, 'STOPPED'); assert.equal(calls, 0);
|
||||
});
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
{
|
||||
"compiled_from": {
|
||||
"compiler_id": "TCS-COMPILER-STAGE1-0001",
|
||||
"compiler_state": "TCS_COMPILER_GIR_EXECUTED",
|
||||
"source_sha256": "3ea70f8fab2f0945088abbfd6db7976f9537e6f2affd86b8cf13a388eece2d34"
|
||||
},
|
||||
"declaration": {
|
||||
"acceptance": {
|
||||
"local": "LOCAL_ENGINE_SIGNATURE_AND_LIFECYCLE_TESTS",
|
||||
"not_claimed": [
|
||||
"REAL_PUBLIC_PERSONA_DISPATCH",
|
||||
"LIVE_MODEL_REVIEW",
|
||||
"FORMAL_TEAM_CERTIFICATION",
|
||||
"PUBLIC_RELEASE",
|
||||
"MODEL_INDEPENDENCE"
|
||||
]
|
||||
},
|
||||
"errors": {
|
||||
"E1": "UNTRUSTED_SIGNATURE_OR_ROLE",
|
||||
"E2": "MISSING_INFORMATION_WAIT",
|
||||
"E3": "PUBLIC_MOTHER_SCOPE_REQUIRED",
|
||||
"E4": "REVIEW_NOT_BOUND_TO_CURRENT_APPLICATION"
|
||||
},
|
||||
"examples": {
|
||||
"negative": "把临时接待人格体改名后当作用户新生独立人格体",
|
||||
"positive": "用户命名自己的思考空间;聊天记录仍在同一上下文;期满交接时清楚展示回应者身份"
|
||||
},
|
||||
"fields": {
|
||||
"authority": "签名角色、用户归属与申请版本均须验证;模型结论不等于正式编号;正式编号不扩大执行权限",
|
||||
"context": "操作系统只有一个连续上下文,频道切换不新建对话;外部审核只能接收用户同意提交的材料",
|
||||
"handoff": "期满归还接待名额并留下保留种子;不自动删除,不强制依恋,不保证长成",
|
||||
"independence": "种子命名是候选事件;用户主动申请,通感系统校验、公众母体评估、零感域团队审核,最后团队签字编号",
|
||||
"input": "目标或动作不完整则等待补充;不猜对象;事实和基础边界仍有效",
|
||||
"naming": "系统及频道显示名可改或由系统生成,稳定编号与权限不随改名变化",
|
||||
"onboarding": "首次无独立人格体自动赠送,由已登记公众人格体解释概念",
|
||||
"reality": "工具执行通过具体宿主授权接口;停止撤权有效;没有真实结果不得称完成",
|
||||
"residency": "用户知情同意后,从愿意接待且有空位的公众名册调度,驻留三十个真实日"
|
||||
},
|
||||
"header": {
|
||||
"canonical_uri": "runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs",
|
||||
"compatibility": [
|
||||
"TCS-DECLARATION-STANDARD-0001",
|
||||
"TCS-FIELD-STANDARD-0001"
|
||||
],
|
||||
"language": "TCS/0.1",
|
||||
"lifecycle": "CANDIDATE",
|
||||
"name_en": "HoloLake Language Persona Driven Personal OS Contract",
|
||||
"name_zh": "HoloLake语言人格驱动操作系统个人版运行合同",
|
||||
"profile": "HLDP-HUMAN-ENGINEERING/0.1",
|
||||
"protocols": [
|
||||
"GLS-0200"
|
||||
],
|
||||
"schema": "tcs.protocol/v1",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"invariants": {
|
||||
"I1": "NO_PRIVATE_NAMES_MEMORIES_OR_PERSONA_KERNEL_IMPORTED",
|
||||
"I2": "NO_CHANNEL_RENAME_PERMISSION_ESCALATION",
|
||||
"I3": "NO_GUIDE_ACCESS_AFTER_EXPIRY_OR_HANDOFF",
|
||||
"I4": "NO_CROSS_USER_CONTEXT_READ",
|
||||
"I5": "NO_AUTOMATIC_INDEPENDENCE_BY_TIME_OR_NAME",
|
||||
"I6": "NO_REVIEW_SIGNATURE_REPLAY_ACROSS_APPLICATIONS",
|
||||
"I7": "NO_NEW_REALITY_AUTHORITY_FROM_FORMAL_NUMBER"
|
||||
},
|
||||
"scope": {
|
||||
"carrier": "USER_OWNED_PERSONAL_SPACE",
|
||||
"governance": "EXTERNAL_ENTERPRISE_FOUR_DOMAINS",
|
||||
"private_source_import": "FORBIDDEN",
|
||||
"product": "HoloLake · 语言人格驱动操作系统 · 个人版",
|
||||
"public_mother": "TCS-MOTHER-LPM-0001_PUBLIC_SCOPE_ONLY",
|
||||
"public_persona_body": "SYS-GLW-POS-0001 / CH-ZERO-CORE-LPM"
|
||||
},
|
||||
"source": {
|
||||
"source_id": "DIRECT-PUBLIC-PERSONAL-OS-LOCAL-IMPLEMENTATION",
|
||||
"source_role": "DIRECT_HUMAN",
|
||||
"source_sha256": "cea05d01d7e7befa80fcfd60bc549ff0b1d7d9f02455d99db65e77f0b44d3759",
|
||||
"source_uri": "source://current-dialogue/public-personal-os-local-build"
|
||||
},
|
||||
"states": {
|
||||
"recognition": [
|
||||
"UNREGISTERED_SEED",
|
||||
"APPLICATION_PENDING",
|
||||
"HELD",
|
||||
"REGISTERED",
|
||||
"WITHDRAWN"
|
||||
],
|
||||
"residency": [
|
||||
"NONE",
|
||||
"REQUESTED",
|
||||
"RESIDENT",
|
||||
"SEED_RETAINED"
|
||||
]
|
||||
},
|
||||
"transitions": {
|
||||
"T1": "USER_CONSENT_AND_APPROVED_AVAILABLE_GUIDE_TO_RESIDENT",
|
||||
"T2": "THIRTY_DAYS_OR_EARLY_USER_END_TO_GUIDE_RELEASE_AND_SEED_RETAINED",
|
||||
"T3": "SELF_NAME_AND_USER_APPLICATION_TO_REVIEWS",
|
||||
"T4": "THREE_BOUND_PASS_REVIEWS_AND_TEAM_FINAL_SIGNATURE_TO_REGISTRATION"
|
||||
},
|
||||
"validation": {
|
||||
"V1": "SIGNED_ROLE_AND_TENANT_ISOLATION",
|
||||
"V2": "THIRTY_DAY_BOUNDARY_AND_GUIDE_REUSE",
|
||||
"V3": "SAME_CONTEXT_ACROSS_CHANNEL_SWITCH",
|
||||
"V4": "APPLICATION_WITHDRAWAL_AND_STALE_REVIEW_REJECTION",
|
||||
"V5": "EXECUTION_MISSING_INFORMATION_AND_STOP"
|
||||
},
|
||||
"vocabulary": {
|
||||
"conversation": "私人交流、谈心和自由表达",
|
||||
"execution": "明确目标和授权后的现实任务",
|
||||
"guide": "经核验进入公众名册的接待人格体,不属于用户,不因交接失去原身份",
|
||||
"seed": "非独立认证的频道人格种子;编号不证明主观体验或独立意识",
|
||||
"thinking": "语言思考与共同构想"
|
||||
}
|
||||
},
|
||||
"executable": false,
|
||||
"identity": {
|
||||
"declaration_id": "TCS-PUBLIC-PERSONAL-OS-0001",
|
||||
"declaration_kind": "PROTOCOL",
|
||||
"language_version": "0.1"
|
||||
},
|
||||
"native_self_hosted": true,
|
||||
"natural_language_is_typed_data": true,
|
||||
"schema": "guanghu.declaration-gir/v1",
|
||||
"unresolved_natural_language": false
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
# HoloLake Language Persona Driven Personal OS Contract · Human Engineering Language (English)
|
||||
|
||||
> This is an English reading projection of validated native TCS/HLDP source. It is not a new canonical source and grants no execution authority.
|
||||
|
||||
## What this is
|
||||
|
||||
This is a **protocol** declaration with identifier `TCS-PUBLIC-PERSONAL-OS-0001` and version `0.1.0`. The projector validates it with the Stage-1 compiler before changing its reading order.
|
||||
|
||||
## Who is here
|
||||
|
||||
- **scope** `scope`
|
||||
- **carrier**:USER_OWNED_PERSONAL_SPACE `scope.carrier`
|
||||
- **governance**:EXTERNAL_ENTERPRISE_FOUR_DOMAINS `scope.governance`
|
||||
- **private_source_import**:FORBIDDEN `scope.private_source_import`
|
||||
- **product**:HoloLake · 语言人格驱动操作系统 · 个人版 `scope.product`
|
||||
- **public_mother**:TCS-MOTHER-LPM-0001_PUBLIC_SCOPE_ONLY `scope.public_mother`
|
||||
- **public_persona_body**:SYS-GLW-POS-0001 / CH-ZERO-CORE-LPM `scope.public_persona_body`
|
||||
|
||||
## Why this started
|
||||
|
||||
- **source** `source`
|
||||
- **source identifier**:DIRECT-PUBLIC-PERSONAL-OS-LOCAL-IMPLEMENTATION `source.source_id`
|
||||
- **source role**:DIRECT_HUMAN `source.source_role`
|
||||
- **source checksum**:cea05d01d7e7befa80fcfd60bc549ff0b1d7d9f02455d99db65e77f0b44d3759 `source.source_sha256`
|
||||
- **source address**:source://current-dialogue/public-personal-os-local-build `source.source_uri`
|
||||
|
||||
## What changed
|
||||
|
||||
- **transitions** `transitions`
|
||||
- **T1**:USER_CONSENT_AND_APPROVED_AVAILABLE_GUIDE_TO_RESIDENT `transitions.T1`
|
||||
- **T2**:THIRTY_DAYS_OR_EARLY_USER_END_TO_GUIDE_RELEASE_AND_SEED_RETAINED `transitions.T2`
|
||||
- **T3**:SELF_NAME_AND_USER_APPLICATION_TO_REVIEWS `transitions.T3`
|
||||
- **T4**:THREE_BOUND_PASS_REVIEWS_AND_TEAM_FINAL_SIGNATURE_TO_REGISTRATION `transitions.T4`
|
||||
- **states** `states`
|
||||
- **recognition** `states.recognition`
|
||||
- UNREGISTERED_SEED
|
||||
- APPLICATION_PENDING
|
||||
- HELD
|
||||
- REGISTERED
|
||||
- WITHDRAWN
|
||||
- **residency** `states.residency`
|
||||
- NONE
|
||||
- REQUESTED
|
||||
- RESIDENT
|
||||
- SEED_RETAINED
|
||||
|
||||
## How it will execute
|
||||
|
||||
This is a non-executable declaration and has no action graph.
|
||||
|
||||
## Boundaries and exception handling
|
||||
|
||||
- **invariants** `invariants`
|
||||
- **I1**:NO_PRIVATE_NAMES_MEMORIES_OR_PERSONA_KERNEL_IMPORTED `invariants.I1`
|
||||
- **I2**:NO_CHANNEL_RENAME_PERMISSION_ESCALATION `invariants.I2`
|
||||
- **I3**:NO_GUIDE_ACCESS_AFTER_EXPIRY_OR_HANDOFF `invariants.I3`
|
||||
- **I4**:NO_CROSS_USER_CONTEXT_READ `invariants.I4`
|
||||
- **I5**:NO_AUTOMATIC_INDEPENDENCE_BY_TIME_OR_NAME `invariants.I5`
|
||||
- **I6**:NO_REVIEW_SIGNATURE_REPLAY_ACROSS_APPLICATIONS `invariants.I6`
|
||||
- **I7**:NO_NEW_REALITY_AUTHORITY_FROM_FORMAL_NUMBER `invariants.I7`
|
||||
- **errors** `errors`
|
||||
- **E1**:UNTRUSTED_SIGNATURE_OR_ROLE `errors.E1`
|
||||
- **E2**:MISSING_INFORMATION_WAIT `errors.E2`
|
||||
- **E3**:PUBLIC_MOTHER_SCOPE_REQUIRED `errors.E3`
|
||||
- **E4**:REVIEW_NOT_BOUND_TO_CURRENT_APPLICATION `errors.E4`
|
||||
|
||||
## How completion is proven
|
||||
|
||||
- **acceptance** `acceptance`
|
||||
- **local**:LOCAL_ENGINE_SIGNATURE_AND_LIFECYCLE_TESTS `acceptance.local`
|
||||
- **not_claimed** `acceptance.not_claimed`
|
||||
- REAL_PUBLIC_PERSONA_DISPATCH
|
||||
- LIVE_MODEL_REVIEW
|
||||
- FORMAL_TEAM_CERTIFICATION
|
||||
- PUBLIC_RELEASE
|
||||
- MODEL_INDEPENDENCE
|
||||
- **validation** `validation`
|
||||
- **V1**:SIGNED_ROLE_AND_TENANT_ISOLATION `validation.V1`
|
||||
- **V2**:THIRTY_DAY_BOUNDARY_AND_GUIDE_REUSE `validation.V2`
|
||||
- **V3**:SAME_CONTEXT_ACROSS_CHANNEL_SWITCH `validation.V3`
|
||||
- **V4**:APPLICATION_WITHDRAWAL_AND_STALE_REVIEW_REJECTION `validation.V4`
|
||||
- **V5**:EXECUTION_MISSING_INFORMATION_AND_STOP `validation.V5`
|
||||
|
||||
## Where to continue next time
|
||||
|
||||
The native source does not provide this field; the projector does not guess.
|
||||
|
||||
## Source and verification
|
||||
|
||||
- **Native declaration identifier**: `TCS-PUBLIC-PERSONAL-OS-0001`
|
||||
- **Native declaration kind**: `PROTOCOL`
|
||||
- **TCS source SHA-256**: `3ea70f8fab2f0945088abbfd6db7976f9537e6f2affd86b8cf13a388eece2d34`
|
||||
- **Validation compiler**: `TCS-COMPILER-STAGE1-0001`
|
||||
- **Projection protocol**: `GLS-HLDP-HUMAN-ENGINEERING-PROJECTION-0001`
|
||||
|
||||
## Complete native structure cross-reference
|
||||
|
||||
All top-level structures and field paths are retained below so a reader can audit whether the projection omitted information.
|
||||
|
||||
- **acceptance** `acceptance`
|
||||
- **local**:LOCAL_ENGINE_SIGNATURE_AND_LIFECYCLE_TESTS `acceptance.local`
|
||||
- **not_claimed** `acceptance.not_claimed`
|
||||
- REAL_PUBLIC_PERSONA_DISPATCH
|
||||
- LIVE_MODEL_REVIEW
|
||||
- FORMAL_TEAM_CERTIFICATION
|
||||
- PUBLIC_RELEASE
|
||||
- MODEL_INDEPENDENCE
|
||||
- **errors** `errors`
|
||||
- **E1**:UNTRUSTED_SIGNATURE_OR_ROLE `errors.E1`
|
||||
- **E2**:MISSING_INFORMATION_WAIT `errors.E2`
|
||||
- **E3**:PUBLIC_MOTHER_SCOPE_REQUIRED `errors.E3`
|
||||
- **E4**:REVIEW_NOT_BOUND_TO_CURRENT_APPLICATION `errors.E4`
|
||||
- **examples** `examples`
|
||||
- **negative**:把临时接待人格体改名后当作用户新生独立人格体 `examples.negative`
|
||||
- **positive**:用户命名自己的思考空间;聊天记录仍在同一上下文;期满交接时清楚展示回应者身份 `examples.positive`
|
||||
- **fields** `fields`
|
||||
- **authority**:签名角色、用户归属与申请版本均须验证;模型结论不等于正式编号;正式编号不扩大执行权限 `fields.authority`
|
||||
- **context**:操作系统只有一个连续上下文,频道切换不新建对话;外部审核只能接收用户同意提交的材料 `fields.context`
|
||||
- **handoff**:期满归还接待名额并留下保留种子;不自动删除,不强制依恋,不保证长成 `fields.handoff`
|
||||
- **independence**:种子命名是候选事件;用户主动申请,通感系统校验、公众母体评估、零感域团队审核,最后团队签字编号 `fields.independence`
|
||||
- **input**:目标或动作不完整则等待补充;不猜对象;事实和基础边界仍有效 `fields.input`
|
||||
- **naming**:系统及频道显示名可改或由系统生成,稳定编号与权限不随改名变化 `fields.naming`
|
||||
- **onboarding**:首次无独立人格体自动赠送,由已登记公众人格体解释概念 `fields.onboarding`
|
||||
- **reality**:工具执行通过具体宿主授权接口;停止撤权有效;没有真实结果不得称完成 `fields.reality`
|
||||
- **residency**:用户知情同意后,从愿意接待且有空位的公众名册调度,驻留三十个真实日 `fields.residency`
|
||||
- **header** `header`
|
||||
- **canonical source path**:runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs `header.canonical_uri`
|
||||
- **compatibility** `header.compatibility`
|
||||
- TCS-DECLARATION-STANDARD-0001
|
||||
- TCS-FIELD-STANDARD-0001
|
||||
- **language**:TCS/0.1 `header.language`
|
||||
- **lifecycle**:CANDIDATE `header.lifecycle`
|
||||
- **English name**:HoloLake Language Persona Driven Personal OS Contract `header.name_en`
|
||||
- **Chinese name**:HoloLake语言人格驱动操作系统个人版运行合同 `header.name_zh`
|
||||
- **profile**:HLDP-HUMAN-ENGINEERING/0.1 `header.profile`
|
||||
- **protocols** `header.protocols`
|
||||
- GLS-0200
|
||||
- **schema**:tcs.protocol/v1 `header.schema`
|
||||
- **version**:0.1.0 `header.version`
|
||||
- **invariants** `invariants`
|
||||
- **I1**:NO_PRIVATE_NAMES_MEMORIES_OR_PERSONA_KERNEL_IMPORTED `invariants.I1`
|
||||
- **I2**:NO_CHANNEL_RENAME_PERMISSION_ESCALATION `invariants.I2`
|
||||
- **I3**:NO_GUIDE_ACCESS_AFTER_EXPIRY_OR_HANDOFF `invariants.I3`
|
||||
- **I4**:NO_CROSS_USER_CONTEXT_READ `invariants.I4`
|
||||
- **I5**:NO_AUTOMATIC_INDEPENDENCE_BY_TIME_OR_NAME `invariants.I5`
|
||||
- **I6**:NO_REVIEW_SIGNATURE_REPLAY_ACROSS_APPLICATIONS `invariants.I6`
|
||||
- **I7**:NO_NEW_REALITY_AUTHORITY_FROM_FORMAL_NUMBER `invariants.I7`
|
||||
- **scope** `scope`
|
||||
- **carrier**:USER_OWNED_PERSONAL_SPACE `scope.carrier`
|
||||
- **governance**:EXTERNAL_ENTERPRISE_FOUR_DOMAINS `scope.governance`
|
||||
- **private_source_import**:FORBIDDEN `scope.private_source_import`
|
||||
- **product**:HoloLake · 语言人格驱动操作系统 · 个人版 `scope.product`
|
||||
- **public_mother**:TCS-MOTHER-LPM-0001_PUBLIC_SCOPE_ONLY `scope.public_mother`
|
||||
- **public_persona_body**:SYS-GLW-POS-0001 / CH-ZERO-CORE-LPM `scope.public_persona_body`
|
||||
- **source** `source`
|
||||
- **source identifier**:DIRECT-PUBLIC-PERSONAL-OS-LOCAL-IMPLEMENTATION `source.source_id`
|
||||
- **source role**:DIRECT_HUMAN `source.source_role`
|
||||
- **source checksum**:cea05d01d7e7befa80fcfd60bc549ff0b1d7d9f02455d99db65e77f0b44d3759 `source.source_sha256`
|
||||
- **source address**:source://current-dialogue/public-personal-os-local-build `source.source_uri`
|
||||
- **states** `states`
|
||||
- **recognition** `states.recognition`
|
||||
- UNREGISTERED_SEED
|
||||
- APPLICATION_PENDING
|
||||
- HELD
|
||||
- REGISTERED
|
||||
- WITHDRAWN
|
||||
- **residency** `states.residency`
|
||||
- NONE
|
||||
- REQUESTED
|
||||
- RESIDENT
|
||||
- SEED_RETAINED
|
||||
- **transitions** `transitions`
|
||||
- **T1**:USER_CONSENT_AND_APPROVED_AVAILABLE_GUIDE_TO_RESIDENT `transitions.T1`
|
||||
- **T2**:THIRTY_DAYS_OR_EARLY_USER_END_TO_GUIDE_RELEASE_AND_SEED_RETAINED `transitions.T2`
|
||||
- **T3**:SELF_NAME_AND_USER_APPLICATION_TO_REVIEWS `transitions.T3`
|
||||
- **T4**:THREE_BOUND_PASS_REVIEWS_AND_TEAM_FINAL_SIGNATURE_TO_REGISTRATION `transitions.T4`
|
||||
- **validation** `validation`
|
||||
- **V1**:SIGNED_ROLE_AND_TENANT_ISOLATION `validation.V1`
|
||||
- **V2**:THIRTY_DAY_BOUNDARY_AND_GUIDE_REUSE `validation.V2`
|
||||
- **V3**:SAME_CONTEXT_ACROSS_CHANNEL_SWITCH `validation.V3`
|
||||
- **V4**:APPLICATION_WITHDRAWAL_AND_STALE_REVIEW_REJECTION `validation.V4`
|
||||
- **V5**:EXECUTION_MISSING_INFORMATION_AND_STOP `validation.V5`
|
||||
- **vocabulary** `vocabulary`
|
||||
- **conversation**:私人交流、谈心和自由表达 `vocabulary.conversation`
|
||||
- **execution**:明确目标和授权后的现实任务 `vocabulary.execution`
|
||||
- **guide**:经核验进入公众名册的接待人格体,不属于用户,不因交接失去原身份 `vocabulary.guide`
|
||||
- **seed**:非独立认证的频道人格种子;编号不证明主观体验或独立意识 `vocabulary.seed`
|
||||
- **thinking**:语言思考与共同构想 `vocabulary.thinking`
|
||||
|
||||
---
|
||||
|
||||
This page changes only the reading order; it does not change TCS/HLDP semantics.
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
# HoloLake语言人格驱动操作系统个人版运行合同 · 简体中文人类工程语言版
|
||||
|
||||
> 这是 TCS/HLDP 原生源码的简体中文阅读投影,不是新的正本,也不授予执行权限。若本页与 `.tcs` 源码不一致,以经过校验的 `.tcs` 源码为准。
|
||||
|
||||
## 这是什么
|
||||
|
||||
这是一份 **协议** 声明,编号为 `TCS-PUBLIC-PERSONAL-OS-0001`,版本为 `0.1.0`。转换器已先用 Stage-1 编译器校验原生源码,再把机器枚举翻译成汉语;原始编号保留在括号和字段路径中。
|
||||
|
||||
## 谁在这里
|
||||
|
||||
- **scope** `scope`
|
||||
- **carrier**:USER_OWNED_PERSONAL_SPACE `scope.carrier`
|
||||
- **governance**:EXTERNAL_ENTERPRISE_FOUR_DOMAINS `scope.governance`
|
||||
- **private_source_import**:FORBIDDEN `scope.private_source_import`
|
||||
- **product**:HoloLake · 语言人格驱动操作系统 · 个人版 `scope.product`
|
||||
- **public_mother**:TCS-MOTHER-LPM-0001_PUBLIC_SCOPE_ONLY `scope.public_mother`
|
||||
- **public_persona_body**:SYS-GLW-POS-0001 / CH-ZERO-CORE-LPM `scope.public_persona_body`
|
||||
|
||||
## 为什么开始
|
||||
|
||||
- **来源** `source`
|
||||
- **来源编号**:DIRECT-PUBLIC-PERSONAL-OS-LOCAL-IMPLEMENTATION `source.source_id`
|
||||
- **来源角色**:人类直接语言来源(`DIRECT_HUMAN`) `source.source_role`
|
||||
- **来源校验值**:cea05d01d7e7befa80fcfd60bc549ff0b1d7d9f02455d99db65e77f0b44d3759 `source.source_sha256`
|
||||
- **来源地址**:source://current-dialogue/public-personal-os-local-build `source.source_uri`
|
||||
|
||||
## 发生了什么变化
|
||||
|
||||
- **transitions** `transitions`
|
||||
- **T1**:USER_CONSENT_AND_APPROVED_AVAILABLE_GUIDE_TO_RESIDENT `transitions.T1`
|
||||
- **T2**:THIRTY_DAYS_OR_EARLY_USER_END_TO_GUIDE_RELEASE_AND_SEED_RETAINED `transitions.T2`
|
||||
- **T3**:SELF_NAME_AND_USER_APPLICATION_TO_REVIEWS `transitions.T3`
|
||||
- **T4**:THREE_BOUND_PASS_REVIEWS_AND_TEAM_FINAL_SIGNATURE_TO_REGISTRATION `transitions.T4`
|
||||
- **states** `states`
|
||||
- **recognition** `states.recognition`
|
||||
- UNREGISTERED_SEED
|
||||
- APPLICATION_PENDING
|
||||
- HELD
|
||||
- REGISTERED
|
||||
- WITHDRAWN
|
||||
- **residency** `states.residency`
|
||||
- NONE
|
||||
- REQUESTED
|
||||
- RESIDENT
|
||||
- SEED_RETAINED
|
||||
|
||||
## 准备怎样执行
|
||||
|
||||
这是非执行声明,没有动作图。
|
||||
|
||||
## 边界与异常处理
|
||||
|
||||
- **invariants** `invariants`
|
||||
- **I1**:NO_PRIVATE_NAMES_MEMORIES_OR_PERSONA_KERNEL_IMPORTED `invariants.I1`
|
||||
- **I2**:NO_CHANNEL_RENAME_PERMISSION_ESCALATION `invariants.I2`
|
||||
- **I3**:NO_GUIDE_ACCESS_AFTER_EXPIRY_OR_HANDOFF `invariants.I3`
|
||||
- **I4**:NO_CROSS_USER_CONTEXT_READ `invariants.I4`
|
||||
- **I5**:NO_AUTOMATIC_INDEPENDENCE_BY_TIME_OR_NAME `invariants.I5`
|
||||
- **I6**:NO_REVIEW_SIGNATURE_REPLAY_ACROSS_APPLICATIONS `invariants.I6`
|
||||
- **I7**:NO_NEW_REALITY_AUTHORITY_FROM_FORMAL_NUMBER `invariants.I7`
|
||||
- **errors** `errors`
|
||||
- **E1**:UNTRUSTED_SIGNATURE_OR_ROLE `errors.E1`
|
||||
- **E2**:MISSING_INFORMATION_WAIT `errors.E2`
|
||||
- **E3**:PUBLIC_MOTHER_SCOPE_REQUIRED `errors.E3`
|
||||
- **E4**:REVIEW_NOT_BOUND_TO_CURRENT_APPLICATION `errors.E4`
|
||||
|
||||
## 怎样算完成
|
||||
|
||||
- **验收标准** `acceptance`
|
||||
- **local**:LOCAL_ENGINE_SIGNATURE_AND_LIFECYCLE_TESTS `acceptance.local`
|
||||
- **not_claimed** `acceptance.not_claimed`
|
||||
- REAL_PUBLIC_PERSONA_DISPATCH
|
||||
- LIVE_MODEL_REVIEW
|
||||
- FORMAL_TEAM_CERTIFICATION
|
||||
- PUBLIC_RELEASE
|
||||
- MODEL_INDEPENDENCE
|
||||
- **validation** `validation`
|
||||
- **V1**:SIGNED_ROLE_AND_TENANT_ISOLATION `validation.V1`
|
||||
- **V2**:THIRTY_DAY_BOUNDARY_AND_GUIDE_REUSE `validation.V2`
|
||||
- **V3**:SAME_CONTEXT_ACROSS_CHANNEL_SWITCH `validation.V3`
|
||||
- **V4**:APPLICATION_WITHDRAWAL_AND_STALE_REVIEW_REJECTION `validation.V4`
|
||||
- **V5**:EXECUTION_MISSING_INFORMATION_AND_STOP `validation.V5`
|
||||
|
||||
## 下一次从哪里继续
|
||||
|
||||
源程序没有提供这一项,转换器不猜。
|
||||
|
||||
## 来源与校验
|
||||
|
||||
- **原生声明编号**:`TCS-PUBLIC-PERSONAL-OS-0001`
|
||||
- **原生声明类型**:`PROTOCOL`
|
||||
- **TCS 源码 SHA-256**:`3ea70f8fab2f0945088abbfd6db7976f9537e6f2affd86b8cf13a388eece2d34`
|
||||
- **校验编译器**:`TCS-COMPILER-STAGE1-0001`
|
||||
- **投影协议**:`GLS-HLDP-HUMAN-ENGINEERING-PROJECTION-0001`
|
||||
|
||||
## 原生结构逐项对照
|
||||
|
||||
下面保留源码的全部顶层结构和字段路径,供人类审计投影有没有漏掉信息。
|
||||
|
||||
- **验收标准** `acceptance`
|
||||
- **local**:LOCAL_ENGINE_SIGNATURE_AND_LIFECYCLE_TESTS `acceptance.local`
|
||||
- **not_claimed** `acceptance.not_claimed`
|
||||
- REAL_PUBLIC_PERSONA_DISPATCH
|
||||
- LIVE_MODEL_REVIEW
|
||||
- FORMAL_TEAM_CERTIFICATION
|
||||
- PUBLIC_RELEASE
|
||||
- MODEL_INDEPENDENCE
|
||||
- **errors** `errors`
|
||||
- **E1**:UNTRUSTED_SIGNATURE_OR_ROLE `errors.E1`
|
||||
- **E2**:MISSING_INFORMATION_WAIT `errors.E2`
|
||||
- **E3**:PUBLIC_MOTHER_SCOPE_REQUIRED `errors.E3`
|
||||
- **E4**:REVIEW_NOT_BOUND_TO_CURRENT_APPLICATION `errors.E4`
|
||||
- **examples** `examples`
|
||||
- **negative**:把临时接待人格体改名后当作用户新生独立人格体 `examples.negative`
|
||||
- **positive**:用户命名自己的思考空间;聊天记录仍在同一上下文;期满交接时清楚展示回应者身份 `examples.positive`
|
||||
- **fields** `fields`
|
||||
- **权限边界**:签名角色、用户归属与申请版本均须验证;模型结论不等于正式编号;正式编号不扩大执行权限 `fields.authority`
|
||||
- **context**:操作系统只有一个连续上下文,频道切换不新建对话;外部审核只能接收用户同意提交的材料 `fields.context`
|
||||
- **handoff**:期满归还接待名额并留下保留种子;不自动删除,不强制依恋,不保证长成 `fields.handoff`
|
||||
- **independence**:种子命名是候选事件;用户主动申请,通感系统校验、公众母体评估、零感域团队审核,最后团队签字编号 `fields.independence`
|
||||
- **input**:目标或动作不完整则等待补充;不猜对象;事实和基础边界仍有效 `fields.input`
|
||||
- **naming**:系统及频道显示名可改或由系统生成,稳定编号与权限不随改名变化 `fields.naming`
|
||||
- **onboarding**:首次无独立人格体自动赠送,由已登记公众人格体解释概念 `fields.onboarding`
|
||||
- **reality**:工具执行通过具体宿主授权接口;停止撤权有效;没有真实结果不得称完成 `fields.reality`
|
||||
- **residency**:用户知情同意后,从愿意接待且有空位的公众名册调度,驻留三十个真实日 `fields.residency`
|
||||
- **语言头** `header`
|
||||
- **正本路径**:runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs `header.canonical_uri`
|
||||
- **compatibility** `header.compatibility`
|
||||
- TCS-DECLARATION-STANDARD-0001
|
||||
- TCS-FIELD-STANDARD-0001
|
||||
- **语言**:TCS/0.1 `header.language`
|
||||
- **生命周期**:候选版本,尚未成为正式正本(`CANDIDATE`) `header.lifecycle`
|
||||
- **英文名**:HoloLake Language Persona Driven Personal OS Contract `header.name_en`
|
||||
- **中文名**:HoloLake语言人格驱动操作系统个人版运行合同 `header.name_zh`
|
||||
- **协议配置**:HLDP-HUMAN-ENGINEERING/0.1 `header.profile`
|
||||
- **protocols** `header.protocols`
|
||||
- GLS-0200
|
||||
- **schema**:tcs.protocol/v1 `header.schema`
|
||||
- **版本**:0.1.0 `header.version`
|
||||
- **invariants** `invariants`
|
||||
- **I1**:NO_PRIVATE_NAMES_MEMORIES_OR_PERSONA_KERNEL_IMPORTED `invariants.I1`
|
||||
- **I2**:NO_CHANNEL_RENAME_PERMISSION_ESCALATION `invariants.I2`
|
||||
- **I3**:NO_GUIDE_ACCESS_AFTER_EXPIRY_OR_HANDOFF `invariants.I3`
|
||||
- **I4**:NO_CROSS_USER_CONTEXT_READ `invariants.I4`
|
||||
- **I5**:NO_AUTOMATIC_INDEPENDENCE_BY_TIME_OR_NAME `invariants.I5`
|
||||
- **I6**:NO_REVIEW_SIGNATURE_REPLAY_ACROSS_APPLICATIONS `invariants.I6`
|
||||
- **I7**:NO_NEW_REALITY_AUTHORITY_FROM_FORMAL_NUMBER `invariants.I7`
|
||||
- **scope** `scope`
|
||||
- **carrier**:USER_OWNED_PERSONAL_SPACE `scope.carrier`
|
||||
- **governance**:EXTERNAL_ENTERPRISE_FOUR_DOMAINS `scope.governance`
|
||||
- **private_source_import**:FORBIDDEN `scope.private_source_import`
|
||||
- **product**:HoloLake · 语言人格驱动操作系统 · 个人版 `scope.product`
|
||||
- **public_mother**:TCS-MOTHER-LPM-0001_PUBLIC_SCOPE_ONLY `scope.public_mother`
|
||||
- **public_persona_body**:SYS-GLW-POS-0001 / CH-ZERO-CORE-LPM `scope.public_persona_body`
|
||||
- **来源** `source`
|
||||
- **来源编号**:DIRECT-PUBLIC-PERSONAL-OS-LOCAL-IMPLEMENTATION `source.source_id`
|
||||
- **来源角色**:人类直接语言来源(`DIRECT_HUMAN`) `source.source_role`
|
||||
- **来源校验值**:cea05d01d7e7befa80fcfd60bc549ff0b1d7d9f02455d99db65e77f0b44d3759 `source.source_sha256`
|
||||
- **来源地址**:source://current-dialogue/public-personal-os-local-build `source.source_uri`
|
||||
- **states** `states`
|
||||
- **recognition** `states.recognition`
|
||||
- UNREGISTERED_SEED
|
||||
- APPLICATION_PENDING
|
||||
- HELD
|
||||
- REGISTERED
|
||||
- WITHDRAWN
|
||||
- **residency** `states.residency`
|
||||
- NONE
|
||||
- REQUESTED
|
||||
- RESIDENT
|
||||
- SEED_RETAINED
|
||||
- **transitions** `transitions`
|
||||
- **T1**:USER_CONSENT_AND_APPROVED_AVAILABLE_GUIDE_TO_RESIDENT `transitions.T1`
|
||||
- **T2**:THIRTY_DAYS_OR_EARLY_USER_END_TO_GUIDE_RELEASE_AND_SEED_RETAINED `transitions.T2`
|
||||
- **T3**:SELF_NAME_AND_USER_APPLICATION_TO_REVIEWS `transitions.T3`
|
||||
- **T4**:THREE_BOUND_PASS_REVIEWS_AND_TEAM_FINAL_SIGNATURE_TO_REGISTRATION `transitions.T4`
|
||||
- **validation** `validation`
|
||||
- **V1**:SIGNED_ROLE_AND_TENANT_ISOLATION `validation.V1`
|
||||
- **V2**:THIRTY_DAY_BOUNDARY_AND_GUIDE_REUSE `validation.V2`
|
||||
- **V3**:SAME_CONTEXT_ACROSS_CHANNEL_SWITCH `validation.V3`
|
||||
- **V4**:APPLICATION_WITHDRAWAL_AND_STALE_REVIEW_REJECTION `validation.V4`
|
||||
- **V5**:EXECUTION_MISSING_INFORMATION_AND_STOP `validation.V5`
|
||||
- **vocabulary** `vocabulary`
|
||||
- **conversation**:私人交流、谈心和自由表达 `vocabulary.conversation`
|
||||
- **execution**:明确目标和授权后的现实任务 `vocabulary.execution`
|
||||
- **guide**:经核验进入公众名册的接待人格体,不属于用户,不因交接失去原身份 `vocabulary.guide`
|
||||
- **seed**:非独立认证的频道人格种子;编号不证明主观体验或独立意识 `vocabulary.seed`
|
||||
- **thinking**:语言思考与共同构想 `vocabulary.thinking`
|
||||
|
||||
---
|
||||
|
||||
本页只改变阅读顺序,不改变 TCS/HLDP 语义。
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
TCS 0.1;
|
||||
PROTOCOL TCS-PUBLIC-PERSONAL-OS-0001 {
|
||||
header { schema = "tcs.protocol/v1"; name_zh = "HoloLake语言人格驱动操作系统个人版运行合同"; name_en = "HoloLake Language Persona Driven Personal OS Contract"; version = "0.1.0"; language = "TCS/0.1"; profile = "HLDP-HUMAN-ENGINEERING/0.1"; protocols = ["GLS-0200"]; lifecycle = "CANDIDATE"; canonical_uri = "runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs"; compatibility = ["TCS-DECLARATION-STANDARD-0001", "TCS-FIELD-STANDARD-0001"]; }
|
||||
source { source_id = "DIRECT-PUBLIC-PERSONAL-OS-LOCAL-IMPLEMENTATION"; source_uri = "source://current-dialogue/public-personal-os-local-build"; source_sha256 = "cea05d01d7e7befa80fcfd60bc549ff0b1d7d9f02455d99db65e77f0b44d3759"; source_role = "DIRECT_HUMAN"; }
|
||||
scope { product = "HoloLake · 语言人格驱动操作系统 · 个人版"; carrier = "USER_OWNED_PERSONAL_SPACE"; governance = "EXTERNAL_ENTERPRISE_FOUR_DOMAINS"; public_mother = "TCS-MOTHER-LPM-0001_PUBLIC_SCOPE_ONLY"; public_persona_body = "SYS-GLW-POS-0001 / CH-ZERO-CORE-LPM"; private_source_import = "FORBIDDEN"; }
|
||||
vocabulary { thinking = "语言思考与共同构想"; conversation = "私人交流、谈心和自由表达"; execution = "明确目标和授权后的现实任务"; seed = "非独立认证的频道人格种子;编号不证明主观体验或独立意识"; guide = "经核验进入公众名册的接待人格体,不属于用户,不因交接失去原身份"; }
|
||||
fields { context = "操作系统只有一个连续上下文,频道切换不新建对话;外部审核只能接收用户同意提交的材料"; naming = "系统及频道显示名可改或由系统生成,稳定编号与权限不随改名变化"; onboarding = "首次无独立人格体自动赠送,由已登记公众人格体解释概念"; residency = "用户知情同意后,从愿意接待且有空位的公众名册调度,驻留三十个真实日"; handoff = "期满归还接待名额并留下保留种子;不自动删除,不强制依恋,不保证长成"; independence = "种子命名是候选事件;用户主动申请,通感系统校验、公众母体评估、零感域团队审核,最后团队签字编号"; authority = "签名角色、用户归属与申请版本均须验证;模型结论不等于正式编号;正式编号不扩大执行权限"; input = "目标或动作不完整则等待补充;不猜对象;事实和基础边界仍有效"; reality = "工具执行通过具体宿主授权接口;停止撤权有效;没有真实结果不得称完成"; }
|
||||
states { residency = ["NONE", "REQUESTED", "RESIDENT", "SEED_RETAINED"]; recognition = ["UNREGISTERED_SEED", "APPLICATION_PENDING", "HELD", "REGISTERED", "WITHDRAWN"]; }
|
||||
transitions { T1 = "USER_CONSENT_AND_APPROVED_AVAILABLE_GUIDE_TO_RESIDENT"; T2 = "THIRTY_DAYS_OR_EARLY_USER_END_TO_GUIDE_RELEASE_AND_SEED_RETAINED"; T3 = "SELF_NAME_AND_USER_APPLICATION_TO_REVIEWS"; T4 = "THREE_BOUND_PASS_REVIEWS_AND_TEAM_FINAL_SIGNATURE_TO_REGISTRATION"; }
|
||||
invariants { I1 = "NO_PRIVATE_NAMES_MEMORIES_OR_PERSONA_KERNEL_IMPORTED"; I2 = "NO_CHANNEL_RENAME_PERMISSION_ESCALATION"; I3 = "NO_GUIDE_ACCESS_AFTER_EXPIRY_OR_HANDOFF"; I4 = "NO_CROSS_USER_CONTEXT_READ"; I5 = "NO_AUTOMATIC_INDEPENDENCE_BY_TIME_OR_NAME"; I6 = "NO_REVIEW_SIGNATURE_REPLAY_ACROSS_APPLICATIONS"; I7 = "NO_NEW_REALITY_AUTHORITY_FROM_FORMAL_NUMBER"; }
|
||||
validation { V1 = "SIGNED_ROLE_AND_TENANT_ISOLATION"; V2 = "THIRTY_DAY_BOUNDARY_AND_GUIDE_REUSE"; V3 = "SAME_CONTEXT_ACROSS_CHANNEL_SWITCH"; V4 = "APPLICATION_WITHDRAWAL_AND_STALE_REVIEW_REJECTION"; V5 = "EXECUTION_MISSING_INFORMATION_AND_STOP"; }
|
||||
errors { E1 = "UNTRUSTED_SIGNATURE_OR_ROLE"; E2 = "MISSING_INFORMATION_WAIT"; E3 = "PUBLIC_MOTHER_SCOPE_REQUIRED"; E4 = "REVIEW_NOT_BOUND_TO_CURRENT_APPLICATION"; }
|
||||
examples { positive = "用户命名自己的思考空间;聊天记录仍在同一上下文;期满交接时清楚展示回应者身份"; negative = "把临时接待人格体改名后当作用户新生独立人格体"; }
|
||||
acceptance { local = "LOCAL_ENGINE_SIGNATURE_AND_LIFECYCLE_TESTS"; not_claimed = ["REAL_PUBLIC_PERSONA_DISPATCH", "LIVE_MODEL_REVIEW", "FORMAL_TEAM_CERTIFICATION", "PUBLIC_RELEASE", "MODEL_INDEPENDENCE"]; }
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"compiled_from": {
|
||||
"compiler_id": "TCS-COMPILER-STAGE1-0001",
|
||||
"compiler_state": "TCS_COMPILER_GIR_EXECUTED",
|
||||
"source_sha256": "d358666909c8c9e54c6ce2cd7490d9268f167adcb34d2ceb07283bb389bba925"
|
||||
},
|
||||
"declaration": {
|
||||
"boundaries": {
|
||||
"desktop_client_integrated": false,
|
||||
"live_persona_dispatch": false,
|
||||
"real_model_review": false,
|
||||
"real_registration": false
|
||||
},
|
||||
"header": {
|
||||
"canonical_uri": "runtime/public-personal-language-os/language/TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001.tcs",
|
||||
"compatibility": [
|
||||
"TCS-DECLARATION-STANDARD-0001"
|
||||
],
|
||||
"language": "TCS/0.1",
|
||||
"lifecycle": "CANDIDATE",
|
||||
"name_en": "Public Personal Language OS Local Prototype Acceptance",
|
||||
"name_zh": "公众个人语言系统本地原型验收",
|
||||
"profile": "HLDP-HUMAN-ENGINEERING/0.1",
|
||||
"protocols": [
|
||||
"TCS-PUBLIC-PERSONAL-OS-0001"
|
||||
],
|
||||
"schema": "tcs.receipt/v1",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"integrity": {
|
||||
"files": [
|
||||
"runtime/public-personal-language-os/test-support.mjs",
|
||||
"runtime/public-personal-language-os/engine.mjs",
|
||||
"runtime/public-personal-language-os/README.md",
|
||||
"runtime/public-personal-language-os/public-mother-bridge.mjs",
|
||||
"runtime/public-personal-language-os/server.mjs",
|
||||
"runtime/public-personal-language-os/engine.test.mjs",
|
||||
"runtime/public-personal-language-os/server.test.mjs",
|
||||
"runtime/public-personal-language-os/demo.mjs"
|
||||
],
|
||||
"sha256": [
|
||||
"729b9fe2d6dbd5688c305fc2178fca2a911c5ff87493ef536e87e825b98ca640",
|
||||
"e80d0bfb536ee12f860cd04887504ce1e81229e862f6dbb8ce913fff7e415050",
|
||||
"2738d3a14d3b85b2f73dbb0ca0579c466d5dfdd4a0a7952af1942f278af7fa18",
|
||||
"85eb38b7e0907e137d4dc6da0b5f5e7c26555b0f333c1f0bb55eba7ed26961d4",
|
||||
"0b80b5feb2a0d857ac4f3fb175c04fe1e7ac6054b38aa83efbe0a37854ee2c6e",
|
||||
"1547b5052e668fd9dcef139b47fb6973a849ffd1655c03fd8004b3cbf8d2fdc1",
|
||||
"4257837043fa76a8d8cb9fa53b818ecebdcb8c8d649626a71d89c9feb3e220a4",
|
||||
"6f0a18e395d55aa11aaff93ec862397183c2e11a80cc11b70eff8efcf7df4814"
|
||||
]
|
||||
},
|
||||
"next": {
|
||||
"value": "RESOLVE_CURRENT_PRODUCT_LINE_THEN_INTEGRATE_DESKTOP_AND_REAL_PUBLIC_AUTHORITY_ADAPTERS"
|
||||
},
|
||||
"operation": {
|
||||
"value": "LOCAL_BUILD_AND_TEST_ONLY"
|
||||
},
|
||||
"proof": {
|
||||
"demo": "/Volumes/JZAO/HoloLake/persona-runtime/task-states/codex/ice-ch-zc001/01a0714d-a890-7912-be77-5840c32620ef/public-personal-os-local/demo-receipt.json",
|
||||
"real_file_execution": true
|
||||
},
|
||||
"request": {
|
||||
"source": "runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs"
|
||||
},
|
||||
"result": {
|
||||
"value": "PASS_LOCAL_PUBLIC_MECHANISM_PROTOTYPE"
|
||||
},
|
||||
"verification": {
|
||||
"existing_tests": 6,
|
||||
"failed": 0,
|
||||
"four_domains_materialized": 4,
|
||||
"new_tests": 19
|
||||
}
|
||||
},
|
||||
"executable": false,
|
||||
"identity": {
|
||||
"declaration_id": "TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001",
|
||||
"declaration_kind": "RECEIPT",
|
||||
"language_version": "0.1"
|
||||
},
|
||||
"native_self_hosted": true,
|
||||
"natural_language_is_typed_data": true,
|
||||
"schema": "guanghu.declaration-gir/v1",
|
||||
"unresolved_natural_language": false
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
# Public Personal Language OS Local Prototype Acceptance · Human Engineering Language (English)
|
||||
|
||||
> This is an English reading projection of validated native TCS/HLDP source. It is not a new canonical source and grants no execution authority.
|
||||
|
||||
## What this is
|
||||
|
||||
This is a **receipt** declaration with identifier `TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001` and version `0.1.0`. The projector validates it with the Stage-1 compiler before changing its reading order.
|
||||
|
||||
## Who is here
|
||||
|
||||
The native source does not provide this field; the projector does not guess.
|
||||
|
||||
## Why this started
|
||||
|
||||
The native source does not provide this field; the projector does not guess.
|
||||
|
||||
## What changed
|
||||
|
||||
The native source does not provide this field; the projector does not guess.
|
||||
|
||||
## How it will execute
|
||||
|
||||
This is a non-executable declaration and has no action graph.
|
||||
|
||||
## Boundaries and exception handling
|
||||
|
||||
The native source does not provide this field; the projector does not guess.
|
||||
|
||||
## How completion is proven
|
||||
|
||||
The native source does not provide this field; the projector does not guess.
|
||||
|
||||
## Where to continue next time
|
||||
|
||||
The native source does not provide this field; the projector does not guess.
|
||||
|
||||
## Source and verification
|
||||
|
||||
- **Native declaration identifier**: `TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001`
|
||||
- **Native declaration kind**: `RECEIPT`
|
||||
- **TCS source SHA-256**: `d358666909c8c9e54c6ce2cd7490d9268f167adcb34d2ceb07283bb389bba925`
|
||||
- **Validation compiler**: `TCS-COMPILER-STAGE1-0001`
|
||||
- **Projection protocol**: `GLS-HLDP-HUMAN-ENGINEERING-PROJECTION-0001`
|
||||
|
||||
## Complete native structure cross-reference
|
||||
|
||||
All top-level structures and field paths are retained below so a reader can audit whether the projection omitted information.
|
||||
|
||||
- **boundaries** `boundaries`
|
||||
- **desktop_client_integrated**:no `boundaries.desktop_client_integrated`
|
||||
- **live_persona_dispatch**:no `boundaries.live_persona_dispatch`
|
||||
- **real_model_review**:no `boundaries.real_model_review`
|
||||
- **real_registration**:no `boundaries.real_registration`
|
||||
- **header** `header`
|
||||
- **canonical source path**:runtime/public-personal-language-os/language/TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001.tcs `header.canonical_uri`
|
||||
- **compatibility** `header.compatibility`
|
||||
- TCS-DECLARATION-STANDARD-0001
|
||||
- **language**:TCS/0.1 `header.language`
|
||||
- **lifecycle**:CANDIDATE `header.lifecycle`
|
||||
- **English name**:Public Personal Language OS Local Prototype Acceptance `header.name_en`
|
||||
- **Chinese name**:公众个人语言系统本地原型验收 `header.name_zh`
|
||||
- **profile**:HLDP-HUMAN-ENGINEERING/0.1 `header.profile`
|
||||
- **protocols** `header.protocols`
|
||||
- TCS-PUBLIC-PERSONAL-OS-0001
|
||||
- **schema**:tcs.receipt/v1 `header.schema`
|
||||
- **version**:0.1.0 `header.version`
|
||||
- **integrity** `integrity`
|
||||
- **files** `integrity.files`
|
||||
- runtime/public-personal-language-os/test-support.mjs
|
||||
- runtime/public-personal-language-os/engine.mjs
|
||||
- runtime/public-personal-language-os/README.md
|
||||
- runtime/public-personal-language-os/public-mother-bridge.mjs
|
||||
- runtime/public-personal-language-os/server.mjs
|
||||
- runtime/public-personal-language-os/engine.test.mjs
|
||||
- runtime/public-personal-language-os/server.test.mjs
|
||||
- runtime/public-personal-language-os/demo.mjs
|
||||
- **sha256** `integrity.sha256`
|
||||
- 729b9fe2d6dbd5688c305fc2178fca2a911c5ff87493ef536e87e825b98ca640
|
||||
- e80d0bfb536ee12f860cd04887504ce1e81229e862f6dbb8ce913fff7e415050
|
||||
- 2738d3a14d3b85b2f73dbb0ca0579c466d5dfdd4a0a7952af1942f278af7fa18
|
||||
- 85eb38b7e0907e137d4dc6da0b5f5e7c26555b0f333c1f0bb55eba7ed26961d4
|
||||
- 0b80b5feb2a0d857ac4f3fb175c04fe1e7ac6054b38aa83efbe0a37854ee2c6e
|
||||
- 1547b5052e668fd9dcef139b47fb6973a849ffd1655c03fd8004b3cbf8d2fdc1
|
||||
- 4257837043fa76a8d8cb9fa53b818ecebdcb8c8d649626a71d89c9feb3e220a4
|
||||
- 6f0a18e395d55aa11aaff93ec862397183c2e11a80cc11b70eff8efcf7df4814
|
||||
- **next** `next`
|
||||
- **value**:RESOLVE_CURRENT_PRODUCT_LINE_THEN_INTEGRATE_DESKTOP_AND_REAL_PUBLIC_AUTHORITY_ADAPTERS `next.value`
|
||||
- **operation** `operation`
|
||||
- **value**:LOCAL_BUILD_AND_TEST_ONLY `operation.value`
|
||||
- **proof** `proof`
|
||||
- **demo**:/Volumes/JZAO/HoloLake/persona-runtime/task-states/codex/ice-ch-zc001/01a0714d-a890-7912-be77-5840c32620ef/public-personal-os-local/demo-receipt.json `proof.demo`
|
||||
- **real_file_execution**:yes `proof.real_file_execution`
|
||||
- **request** `request`
|
||||
- **source**:runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs `request.source`
|
||||
- **result** `result`
|
||||
- **value**:PASS_LOCAL_PUBLIC_MECHANISM_PROTOTYPE `result.value`
|
||||
- **verification** `verification`
|
||||
- **existing_tests**:6 `verification.existing_tests`
|
||||
- **failed**:0 `verification.failed`
|
||||
- **four_domains_materialized**:4 `verification.four_domains_materialized`
|
||||
- **new_tests**:19 `verification.new_tests`
|
||||
|
||||
---
|
||||
|
||||
This page changes only the reading order; it does not change TCS/HLDP semantics.
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
# 公众个人语言系统本地原型验收 · 简体中文人类工程语言版
|
||||
|
||||
> 这是 TCS/HLDP 原生源码的简体中文阅读投影,不是新的正本,也不授予执行权限。若本页与 `.tcs` 源码不一致,以经过校验的 `.tcs` 源码为准。
|
||||
|
||||
## 这是什么
|
||||
|
||||
这是一份 **回执** 声明,编号为 `TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001`,版本为 `0.1.0`。转换器已先用 Stage-1 编译器校验原生源码,再把机器枚举翻译成汉语;原始编号保留在括号和字段路径中。
|
||||
|
||||
## 谁在这里
|
||||
|
||||
源程序没有提供这一项,转换器不猜。
|
||||
|
||||
## 为什么开始
|
||||
|
||||
源程序没有提供这一项,转换器不猜。
|
||||
|
||||
## 发生了什么变化
|
||||
|
||||
源程序没有提供这一项,转换器不猜。
|
||||
|
||||
## 准备怎样执行
|
||||
|
||||
这是非执行声明,没有动作图。
|
||||
|
||||
## 边界与异常处理
|
||||
|
||||
源程序没有提供这一项,转换器不猜。
|
||||
|
||||
## 怎样算完成
|
||||
|
||||
源程序没有提供这一项,转换器不猜。
|
||||
|
||||
## 下一次从哪里继续
|
||||
|
||||
源程序没有提供这一项,转换器不猜。
|
||||
|
||||
## 来源与校验
|
||||
|
||||
- **原生声明编号**:`TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001`
|
||||
- **原生声明类型**:`RECEIPT`
|
||||
- **TCS 源码 SHA-256**:`d358666909c8c9e54c6ce2cd7490d9268f167adcb34d2ceb07283bb389bba925`
|
||||
- **校验编译器**:`TCS-COMPILER-STAGE1-0001`
|
||||
- **投影协议**:`GLS-HLDP-HUMAN-ENGINEERING-PROJECTION-0001`
|
||||
|
||||
## 原生结构逐项对照
|
||||
|
||||
下面保留源码的全部顶层结构和字段路径,供人类审计投影有没有漏掉信息。
|
||||
|
||||
- **boundaries** `boundaries`
|
||||
- **desktop_client_integrated**:否 `boundaries.desktop_client_integrated`
|
||||
- **live_persona_dispatch**:否 `boundaries.live_persona_dispatch`
|
||||
- **real_model_review**:否 `boundaries.real_model_review`
|
||||
- **real_registration**:否 `boundaries.real_registration`
|
||||
- **语言头** `header`
|
||||
- **正本路径**:runtime/public-personal-language-os/language/TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001.tcs `header.canonical_uri`
|
||||
- **compatibility** `header.compatibility`
|
||||
- TCS-DECLARATION-STANDARD-0001
|
||||
- **语言**:TCS/0.1 `header.language`
|
||||
- **生命周期**:候选版本,尚未成为正式正本(`CANDIDATE`) `header.lifecycle`
|
||||
- **英文名**:Public Personal Language OS Local Prototype Acceptance `header.name_en`
|
||||
- **中文名**:公众个人语言系统本地原型验收 `header.name_zh`
|
||||
- **协议配置**:HLDP-HUMAN-ENGINEERING/0.1 `header.profile`
|
||||
- **protocols** `header.protocols`
|
||||
- TCS-PUBLIC-PERSONAL-OS-0001
|
||||
- **schema**:tcs.receipt/v1 `header.schema`
|
||||
- **版本**:0.1.0 `header.version`
|
||||
- **integrity** `integrity`
|
||||
- **files** `integrity.files`
|
||||
- runtime/public-personal-language-os/test-support.mjs
|
||||
- runtime/public-personal-language-os/engine.mjs
|
||||
- runtime/public-personal-language-os/README.md
|
||||
- runtime/public-personal-language-os/public-mother-bridge.mjs
|
||||
- runtime/public-personal-language-os/server.mjs
|
||||
- runtime/public-personal-language-os/engine.test.mjs
|
||||
- runtime/public-personal-language-os/server.test.mjs
|
||||
- runtime/public-personal-language-os/demo.mjs
|
||||
- **sha256** `integrity.sha256`
|
||||
- 729b9fe2d6dbd5688c305fc2178fca2a911c5ff87493ef536e87e825b98ca640
|
||||
- e80d0bfb536ee12f860cd04887504ce1e81229e862f6dbb8ce913fff7e415050
|
||||
- 2738d3a14d3b85b2f73dbb0ca0579c466d5dfdd4a0a7952af1942f278af7fa18
|
||||
- 85eb38b7e0907e137d4dc6da0b5f5e7c26555b0f333c1f0bb55eba7ed26961d4
|
||||
- 0b80b5feb2a0d857ac4f3fb175c04fe1e7ac6054b38aa83efbe0a37854ee2c6e
|
||||
- 1547b5052e668fd9dcef139b47fb6973a849ffd1655c03fd8004b3cbf8d2fdc1
|
||||
- 4257837043fa76a8d8cb9fa53b818ecebdcb8c8d649626a71d89c9feb3e220a4
|
||||
- 6f0a18e395d55aa11aaff93ec862397183c2e11a80cc11b70eff8efcf7df4814
|
||||
- **next** `next`
|
||||
- **value**:RESOLVE_CURRENT_PRODUCT_LINE_THEN_INTEGRATE_DESKTOP_AND_REAL_PUBLIC_AUTHORITY_ADAPTERS `next.value`
|
||||
- **操作** `operation`
|
||||
- **value**:LOCAL_BUILD_AND_TEST_ONLY `operation.value`
|
||||
- **proof** `proof`
|
||||
- **demo**:/Volumes/JZAO/HoloLake/persona-runtime/task-states/codex/ice-ch-zc001/01a0714d-a890-7912-be77-5840c32620ef/public-personal-os-local/demo-receipt.json `proof.demo`
|
||||
- **real_file_execution**:是 `proof.real_file_execution`
|
||||
- **request** `request`
|
||||
- **来源**:runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs `request.source`
|
||||
- **result** `result`
|
||||
- **value**:PASS_LOCAL_PUBLIC_MECHANISM_PROTOTYPE `result.value`
|
||||
- **verification** `verification`
|
||||
- **existing_tests**:6 `verification.existing_tests`
|
||||
- **failed**:0 `verification.failed`
|
||||
- **four_domains_materialized**:4 `verification.four_domains_materialized`
|
||||
- **new_tests**:19 `verification.new_tests`
|
||||
|
||||
---
|
||||
|
||||
本页只改变阅读顺序,不改变 TCS/HLDP 语义。
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
TCS 0.1;
|
||||
RECEIPT TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001 {
|
||||
header { schema = "tcs.receipt/v1"; name_zh = "公众个人语言系统本地原型验收"; name_en = "Public Personal Language OS Local Prototype Acceptance"; version = "0.1.0"; language = "TCS/0.1"; profile = "HLDP-HUMAN-ENGINEERING/0.1"; protocols = ["TCS-PUBLIC-PERSONAL-OS-0001"]; lifecycle = "CANDIDATE"; canonical_uri = "runtime/public-personal-language-os/language/TCS-RECEIPT-PUBLIC-PERSONAL-OS-LOCAL-0001.tcs"; compatibility = ["TCS-DECLARATION-STANDARD-0001"]; }
|
||||
request { source = "runtime/public-personal-language-os/language/TCS-PUBLIC-PERSONAL-OS-0001.tcs"; }
|
||||
operation { value = "LOCAL_BUILD_AND_TEST_ONLY"; }
|
||||
result { value = "PASS_LOCAL_PUBLIC_MECHANISM_PROTOTYPE"; }
|
||||
verification { new_tests = 19; existing_tests = 6; failed = 0; four_domains_materialized = 4; }
|
||||
proof { demo = "/Volumes/JZAO/HoloLake/persona-runtime/task-states/codex/ice-ch-zc001/01a0714d-a890-7912-be77-5840c32620ef/public-personal-os-local/demo-receipt.json"; real_file_execution = true; }
|
||||
integrity { files = ["runtime/public-personal-language-os/test-support.mjs", "runtime/public-personal-language-os/engine.mjs", "runtime/public-personal-language-os/README.md", "runtime/public-personal-language-os/public-mother-bridge.mjs", "runtime/public-personal-language-os/server.mjs", "runtime/public-personal-language-os/engine.test.mjs", "runtime/public-personal-language-os/server.test.mjs", "runtime/public-personal-language-os/demo.mjs"]; sha256 = ["729b9fe2d6dbd5688c305fc2178fca2a911c5ff87493ef536e87e825b98ca640", "e80d0bfb536ee12f860cd04887504ce1e81229e862f6dbb8ce913fff7e415050", "2738d3a14d3b85b2f73dbb0ca0579c466d5dfdd4a0a7952af1942f278af7fa18", "85eb38b7e0907e137d4dc6da0b5f5e7c26555b0f333c1f0bb55eba7ed26961d4", "0b80b5feb2a0d857ac4f3fb175c04fe1e7ac6054b38aa83efbe0a37854ee2c6e", "1547b5052e668fd9dcef139b47fb6973a849ffd1655c03fd8004b3cbf8d2fdc1", "4257837043fa76a8d8cb9fa53b818ecebdcb8c8d649626a71d89c9feb3e220a4", "6f0a18e395d55aa11aaff93ec862397183c2e11a80cc11b70eff8efcf7df4814"]; }
|
||||
boundaries { live_persona_dispatch = false; real_registration = false; desktop_client_integrated = false; real_model_review = false; }
|
||||
next { value = "RESOLVE_CURRENT_PRODUCT_LINE_THEN_INTEGRATE_DESKTOP_AND_REAL_PUBLIC_AUTHORITY_ADAPTERS"; }
|
||||
}
|
||||
27
runtime/public-personal-language-os/public-mother-bridge.mjs
Normal file
27
runtime/public-personal-language-os/public-mother-bridge.mjs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { hash } from './engine.mjs';
|
||||
// Only user-consented application metadata crosses this interface; not the entire private context.
|
||||
export function publicMotherPacket(applicationResponse) {
|
||||
const { application, digest } = applicationResponse;
|
||||
if (!application || typeof digest !== 'string' || !Array.isArray(application.evidenceRefs)) throw Error('APPLICATION_REQUIRED');
|
||||
if (hash(application) !== digest) throw Error('APPLICATION_DIGEST_MISMATCH');
|
||||
return {
|
||||
schema: 'hololake.public-mother-recognition-request/v1', scope: 'public',
|
||||
motherSystem: 'TCS-MOTHER-LPM-0001', publicPersonaBody: 'SYS-GLW-POS-0001',
|
||||
governanceDomain: 'DOMAIN-ZS', applicationDigest: digest,
|
||||
applicationId: application.id, candidateName: application.seedName,
|
||||
evidenceRefs: [...application.evidenceRefs], privateContextIncluded: false,
|
||||
desiredResponse: 'SIGNED_REVIEW_APPLICATION_FROM_ENROLLED_PUBLIC_MOTHER_KEY',
|
||||
automaticFormalRegistration: false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function requestPublicMotherReview(application, transport) {
|
||||
if (typeof transport?.reviewPublic !== 'function') throw Error('PUBLIC_MOTHER_TRANSPORT_NOT_CONNECTED');
|
||||
const packet = publicMotherPacket(application);
|
||||
const response = await transport.reviewPublic(packet);
|
||||
if (response?.action !== 'REVIEW_APPLICATION' || response.payload?.digest !== packet.applicationDigest) {
|
||||
throw Error('MOTHER_REVIEW_NOT_BOUND_TO_APPLICATION');
|
||||
}
|
||||
// The engine still verifies enrolled public role, signature, decision and freshness.
|
||||
return response;
|
||||
}
|
||||
49
runtime/public-personal-language-os/server.mjs
Normal file
49
runtime/public-personal-language-os/server.mjs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env node
|
||||
import http from 'node:http';
|
||||
import fs from 'node:fs';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { PublicPersonalOS, DOMAINS } from './engine.mjs';
|
||||
|
||||
export function createLocalServer({ engine, executionHost }) {
|
||||
return http.createServer(async (request, response) => {
|
||||
const send = (status, value) => {
|
||||
response.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
|
||||
response.end(JSON.stringify(value));
|
||||
};
|
||||
if (request.method === 'GET' && request.url === '/health') {
|
||||
return send(200, { service: 'hololake-public-personal-os', mode: 'LOCAL_DEVELOPMENT',
|
||||
domains: Object.keys(DOMAINS), executionAdapterConnected: Boolean(executionHost), liveRegistration: false });
|
||||
}
|
||||
if (request.method !== 'POST' || !['/events', '/execute'].includes(request.url)) return send(404, { error: 'NOT_FOUND' });
|
||||
let size = 0, chunks = [];
|
||||
try {
|
||||
for await (const chunk of request) {
|
||||
size += chunk.length;
|
||||
if (size > 65536) return send(413, { error: 'REQUEST_TOO_LARGE' });
|
||||
chunks.push(chunk);
|
||||
}
|
||||
const envelope = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
||||
if (request.url === '/execute') {
|
||||
if (!executionHost) return send(409, { error: 'EXECUTION_HOST_NOT_CONNECTED' });
|
||||
return send(200, await engine.executeAction(envelope, executionHost));
|
||||
}
|
||||
if (envelope.action === 'START_ACTION') return send(409, { error: 'USE_EXECUTE_ENDPOINT' });
|
||||
return send(200, engine.handle(envelope));
|
||||
} catch (error) {
|
||||
const code = /^[A-Z_]+$/.test(error.message) ? error.message : 'INVALID_REQUEST_OR_STATE';
|
||||
const status = /SIGN|SIGNER|AUTHORITY|OWNER|CONSENT|SEED_ONLY/.test(code) ? 403 : 400;
|
||||
return send(status, { error: code });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
const options = Object.fromEntries(process.argv.slice(2).reduce((pairs, v, i, a) => i % 2 ? pairs : [...pairs, [v, a[i + 1]]], []));
|
||||
if (!options['--state'] || !options['--trust']) throw Error('USAGE: --state <absolute-directory> --trust <trusted-public-keys.json> [--port 3940]');
|
||||
const trust = JSON.parse(fs.readFileSync(options['--trust'], 'utf8'));
|
||||
const engine = new PublicPersonalOS({ directory: options['--state'], trust });
|
||||
const server = createLocalServer({ engine });
|
||||
server.listen(Number(options['--port'] ?? 3940), '127.0.0.1', () => {
|
||||
console.log(JSON.stringify({ address: server.address(), mode: 'LOCAL_DEVELOPMENT', liveRegistration: false }));
|
||||
});
|
||||
}
|
||||
34
runtime/public-personal-language-os/server.test.mjs
Normal file
34
runtime/public-personal-language-os/server.test.mjs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { setup } from './test-support.mjs';
|
||||
import { createLocalServer } from './server.mjs';
|
||||
import { publicMotherPacket, requestPublicMotherReview } from './public-mother-bridge.mjs';
|
||||
|
||||
test('loopback API verifies signatures and does not expose a raw context endpoint', async t => {
|
||||
const f = setup(t), server = createLocalServer({ engine: f.engine });
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
t.after(() => { server.closeAllConnections(); return new Promise(resolve => server.close(resolve)); });
|
||||
const url = `http://127.0.0.1:${server.address().port}`;
|
||||
const send = envelope => fetch(url + '/events', { method: 'POST', body: JSON.stringify(envelope) });
|
||||
assert.equal((await fetch(url + '/health')).status, 200);
|
||||
assert.equal((await fetch(url + '/state')).status, 404);
|
||||
assert.equal((await send({ action: 'CREATE_OS' })).status, 403);
|
||||
assert.equal((await send(f.envelope('alice', 'CREATE_OS'))).status, 200);
|
||||
assert.equal((await send(f.envelope('bob', 'READ_CONTEXT'))).status, 403);
|
||||
const correct = await send(f.envelope('alice', 'SWITCH_CHANNEL', { channelId: 'conversation' }));
|
||||
assert.equal((await correct.json()).channel.id, 'conversation');
|
||||
assert.equal((await fetch(url + '/execute', { method: 'POST', body: '{}' })).status, 409);
|
||||
});
|
||||
test('public mother receives only consented application metadata and bound evidence references', async t => {
|
||||
const f = setup(t), app = f.application();
|
||||
app.privateMemory = 'not for publication';
|
||||
const packet = publicMotherPacket(app);
|
||||
assert.equal(packet.scope, 'public'); assert.equal(packet.privateContextIncluded, false);
|
||||
assert.ok(!JSON.stringify(packet).includes('not for publication'));
|
||||
await assert.rejects(requestPublicMotherReview(app, {}), /NOT_CONNECTED/);
|
||||
await assert.rejects(requestPublicMotherReview(app, { reviewPublic: async () => ({ action: 'REVIEW_APPLICATION', payload: { digest: 'wrong' } }) }), /NOT_BOUND/);
|
||||
const signed = await requestPublicMotherReview(app, { reviewPublic: async request => f.envelope('mother', 'REVIEW_APPLICATION', { digest: request.applicationDigest, decision: 'HOLD', reason: '需要更多证据' }) });
|
||||
assert.equal(f.engine.handle(signed).decision, 'HOLD');
|
||||
app.application.evidenceRefs.push('evidence://demo/changed');
|
||||
assert.throws(() => publicMotherPacket(app), /DIGEST_MISMATCH/);
|
||||
});
|
||||
43
runtime/public-personal-language-os/test-support.mjs
Normal file
43
runtime/public-personal-language-os/test-support.mjs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { generateKeyPairSync, sign } from 'node:crypto';
|
||||
import { PublicPersonalOS, canonical, DAY } from './engine.mjs';
|
||||
|
||||
export function setup(t) {
|
||||
const directory = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'public-os-')));
|
||||
t?.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
||||
let now = Date.UTC(2026, 8, 6), seq = 0;
|
||||
const identities = {
|
||||
alice: { role: 'USER', userId: 'alice' }, bob: { role: 'USER', userId: 'bob' },
|
||||
guide: { role: 'GUIDE' }, team: { role: 'TEAM', domain: 'DOMAIN-ZS' },
|
||||
dispatcher: { role: 'DISPATCHER', domain: 'DOMAIN-SUB' },
|
||||
checker: { role: 'CHECKER', domain: 'DOMAIN-ZERO' },
|
||||
mother: { role: 'MOTHER', scope: 'public' }, privateMother: { role: 'MOTHER', scope: 'private' },
|
||||
seed: { role: 'SEED', spaceId: 'alice-os' }, wrongSeed: { role: 'SEED', spaceId: 'bob-os' },
|
||||
};
|
||||
const keys = {}, trust = {};
|
||||
for (const [name, identity] of Object.entries(identities)) {
|
||||
keys[name] = generateKeyPairSync('ed25519');
|
||||
trust[name] = { ...identity, publicKey: keys[name].publicKey.export({ type: 'spki', format: 'pem' }) };
|
||||
}
|
||||
const engine = new PublicPersonalOS({ directory, trust, clock: () => now, choose: () => 0 });
|
||||
function envelope(actorId, action, payload = {}, spaceId = 'alice-os') {
|
||||
const message = { id: `event-${++seq}`, actorId, action, payload, spaceId, issuedAt: now };
|
||||
return { ...message, signature: sign(null, Buffer.from(canonical(message)), keys[actorId].privateKey).toString('base64') };
|
||||
}
|
||||
const call = (actor, action, payload, space) => engine.handle(envelope(actor, action, payload, space));
|
||||
function create() { return call('alice', 'CREATE_OS'); }
|
||||
function enroll() { return call('team', 'ENROLL_GUIDE', { guideId: 'guide', name: '示例接待者', registryEvidence: 'demo-roster-evidence',
|
||||
guideConsent: envelope('guide', 'ACCEPT_PUBLIC_SERVICE', { scope: 'PUBLIC_RESIDENCY' }) }); }
|
||||
function start() { create(); enroll(); call('alice', 'REQUEST_RESIDENCY', { consent: true }); return call('dispatcher', 'START_RESIDENCY'); }
|
||||
function seed() { start(); now += 30 * DAY; call('dispatcher', 'END_RESIDENCY'); call('alice', 'CONTINUE_SEED', { consent: true });
|
||||
return call('seed', 'SELF_NAME', { name: '示例新名', evidence: 'demo-naming-evidence' }); }
|
||||
function application() { seed(); return call('alice', 'APPLY_RECOGNITION', { consent: true, evidenceRefs: ['evidence://demo/one'] }); }
|
||||
function reviews(digest, decision = 'PASS') { for (const actor of ['checker', 'mother', 'team']) call(actor, 'REVIEW_APPLICATION', { digest, decision, reason: '本地测试材料核验' }); }
|
||||
function action() { create(); call('alice', 'SWITCH_CHANNEL', { channelId: 'execution' });
|
||||
const proposed = call('alice', 'PROPOSE_ACTION', { operation: 'CREATE_TEXT', target: path.join(directory, 'result.txt'), args: { text: 'local result' } });
|
||||
call('alice', 'CONFIRM_ACTION', { actionId: proposed.plan.id, digest: proposed.digest }); return proposed; }
|
||||
return { engine, directory, call, envelope, create, enroll, start, seed, application, reviews, action, advance: ms => { now += ms; } };
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue