feat(hololake): ship language platform 0.7.0

This commit is contained in:
冰朔 2026-08-09 03:37:41 +08:00
commit 5105ec5e32
47 changed files with 4566 additions and 398 deletions

View file

@ -137,6 +137,15 @@ export class GitEngine {
return this.getRepositoryStatus(remoteName);
}
async removeRemote(remoteName = 'origin'): Promise<RepositoryStatus> {
this.assertRemoteName(remoteName);
const remotes = await this.git.getRemotes();
if (remotes.some(remote => remote.name === remoteName)) {
await this.git.removeRemote(remoteName);
}
return this.getRepositoryStatus(remoteName);
}
async fetchRemote(remoteName = 'origin'): Promise<RepositoryStatus> {
this.assertRemoteName(remoteName);
await this.requireRemote(remoteName);
@ -470,6 +479,6 @@ export class GitEngine {
private async requireRemote(name: string): Promise<void> {
const remotes = await this.git.getRemotes();
if (!remotes.includes(name)) throw new Error('尚未配置 Forgejo 仓库地址');
if (!remotes.some(remote => remote.name === name)) throw new Error('尚未配置 Forgejo 仓库地址');
}
}

View file

@ -9,11 +9,11 @@ import express from 'express';
import cors from 'cors';
import { GitEngine } from './git-engine.js';
import { PersonaAgent, createDefaultPersona } from './persona-agent.js';
import { projectHumanKnowledgeTree } from './language-protocol-kernel.js';
import path from 'path';
import { fileURLToPath } from 'url';
// __dirname 兼容 CJS 和 ESM
const _dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
const _dirname = typeof __dirname !== 'undefined' ? __dirname : process.cwd();
// ─── 配置 ───
@ -54,7 +54,7 @@ app.get('/api/health', (_req, res) => {
app.get('/api/tree', async (_req, res) => {
try {
const tree = await engine.getTree();
res.json({ ok: true, tree });
res.json({ ok: true, tree: projectHumanKnowledgeTree(tree) });
} catch (err: any) {
res.status(500).json({ ok: false, error: err.message });
}
@ -205,6 +205,14 @@ app.put('/api/forgejo/remote', async (req, res) => {
}
});
app.delete('/api/forgejo/remote', async (_req, res) => {
try {
res.json({ ok: true, status: await engine.removeRemote() });
} catch (err: any) {
res.status(400).json({ ok: false, error: err.message });
}
});
app.post('/api/forgejo/fetch', async (_req, res) => {
try {
res.json({ ok: true, status: await engine.fetchRemote() });
@ -280,12 +288,35 @@ app.post('/api/agent/chat', async (req, res) => {
ok: true,
reply,
conversationLength: agent.getConversation().length,
pendingActions: agent.getPendingActions(),
});
} catch (err: any) {
res.status(500).json({ ok: false, error: err.message });
}
});
app.get('/api/agent/actions', (_req, res) => {
res.json({ ok: true, actions: getAgent().getPendingActions() });
});
app.post('/api/agent/actions/:actionId/confirm', async (req, res) => {
try {
const result = await getAgent().confirmAction(req.params.actionId);
res.json({ ok: true, result, actions: getAgent().getPendingActions() });
} catch (err: any) {
res.status(409).json({ ok: false, error: err.message });
}
});
app.post('/api/agent/actions/:actionId/reject', (req, res) => {
try {
getAgent().rejectAction(req.params.actionId);
res.json({ ok: true, actions: getAgent().getPendingActions() });
} catch (err: any) {
res.status(409).json({ ok: false, error: err.message });
}
});
// Agent 对话历史
app.get('/api/agent/conversation', (_req, res) => {
const agent = getAgent();

View file

@ -0,0 +1,38 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { projectHumanKnowledgeTree, renderLanguageKernelForModel } from './language-protocol-kernel.js';
import type { DocTreeNode } from './git-engine.js';
test('人类知识树隐藏底层协议与人格配置但保留用户资料', () => {
const tree: DocTreeNode[] = [
{
name: '光湖语言世界——第五域',
path: 'world',
type: 'folder',
children: [
{ name: 'AI技能包', path: 'world/AI技能包', type: 'folder', children: [{ name: 'INDEX', path: 'world/AI技能包/INDEX.md', type: 'document' }] },
{ name: '铸澜人格系统', path: 'world/铸澜人格系统', type: 'folder', children: [{ name: 'INDEX', path: 'world/铸澜人格系统/INDEX.md', type: 'document' }] },
{ name: 'AGENTS', path: 'world/AGENTS.md', type: 'document' },
{ name: '短剧视频AI制作', path: 'world/短剧视频AI制作', type: 'folder', children: [{ name: '生产入口', path: 'world/短剧视频AI制作/生产入口.md', type: 'document' }] },
],
},
{
name: '光湖语言世界 · 世界观、编号、协议与系统演化总纲 · v1.0',
path: 'protocol',
type: 'folder',
children: [{ name: '协议', path: 'protocol/协议.md', type: 'document' }],
},
];
const projected = projectHumanKnowledgeTree(tree);
assert.equal(projected.length, 1);
assert.equal(projected[0].name, '短剧视频AI制作');
assert.deepEqual(projected[0].children?.map(node => node.name), ['生产入口']);
});
test('模型运行投影包含协议核与确定性权限规则', () => {
const prompt = renderLanguageKernelForModel();
assert.match(prompt, /HLP-KERNEL-LANGUAGE-0001/);
assert.match(prompt, /写动作先生成待确认动作/);
assert.match(prompt, /企业四域与个人第五域平行/);
});

View file

@ -0,0 +1,92 @@
import type { DocTreeNode } from './git-engine.js';
/**
* HoloLake
*
*
*
*/
export const HOLOLAKE_LANGUAGE_KERNEL = Object.freeze({
id: 'HLP-KERNEL-LANGUAGE-0001',
version: '0.1.0',
state: 'SOURCE_IMPLEMENTED',
product: 'HoloLake',
definition: '连接人类意图、语言人格、原生模块、模型计算、代码仓库与现实服务器的语言操作系统平台。',
layers: Object.freeze({
languagePersona: '一级智能进程;模型实例不是人格本体。',
tcs: '人格认知内核;不是聊天记录或单份系统提示词。',
hldp: '持久记忆、历史与恢复寻址层。',
gls: '编号、路径、模块与兼容标准。',
model: '可替换推理计算服务,不拥有人格身份或现实权限。',
repository: '代码与知识的存储和版本燃料,不是运行主体。',
server: '现实执行节点;每个私人频道只进入其已验证的个人服务器边界。',
}),
runtimeRules: Object.freeze([
'先解析主体、频道、目标路径与权限,再选择模块或工具。',
'路径、身份、权限和服务器状态必须由运行时校验,不能由模型自我声明。',
'读取动作只在当前知识库和当前账号授权范围内执行。',
'创建、更新、删除等写动作先生成待确认动作;只有人类在界面确认后才执行。',
'远端推送、部署和其他高影响动作必须走独立确认与可读回执。',
'只有工具或服务器返回成功回执,才能宣称现实动作完成。',
'公共企业四域与个人第五域平行;公共入口不得读取或承载个人第五域私有数据。',
]),
});
const INTERNAL_CONTEXT_SEGMENTS = new Set([
'AI技能包',
'config',
'凝渊人格系统',
'第五域铸渊人格系统',
'铸渊人格系统',
'来光者记忆',
'🧠 来光者记忆',
'工具优化',
'开发需求',
]);
const INTERNAL_CONTEXT_ROOT_PATTERNS = [
/^[ ·-].*(|||)/u,
];
const FIFTH_DOMAIN_SYSTEM_ROOT = /^[-]*$/u;
function isInternalContextNode(node: DocTreeNode, ancestors: string[]): boolean {
if (INTERNAL_CONTEXT_ROOT_PATTERNS.some(pattern => pattern.test(node.name))) return true;
const insideFifthDomainWorld = ancestors.some(name => /^[-]*$/u.test(name));
if (!insideFifthDomainWorld) return false;
if (INTERNAL_CONTEXT_SEGMENTS.has(node.name) || /$/u.test(node.name)) return true;
const directChildOfFifthDomain = /^[-]*$/u.test(ancestors.at(-1) || '');
return directChildOfFifthDomain && (
/^(AGENTS|AI-MEMORY|CLAUDE|GEMINI)$/iu.test(node.name) ||
/^(guanghu-os-|$|💠|🔧)/u.test(node.name)
);
}
/** 人类知识树只投影可见资料Agent 的受控搜索仍可读取底层上下文。 */
export function projectHumanKnowledgeTree(nodes: DocTreeNode[], ancestors: string[] = []): DocTreeNode[] {
return nodes.flatMap(node => {
if (isInternalContextNode(node, ancestors)) return [];
if (node.type === 'folder' && FIFTH_DOMAIN_SYSTEM_ROOT.test(node.name)) {
return projectHumanKnowledgeTree(node.children || [], [...ancestors, node.name]);
}
if (node.type === 'folder') {
const children = projectHumanKnowledgeTree(node.children || [], [...ancestors, node.name]);
if (children.length === 0) return [];
return [{ ...node, children }];
}
return [node];
});
}
export function renderLanguageKernelForModel(): string {
const layerLines = Object.entries(HOLOLAKE_LANGUAGE_KERNEL.layers)
.map(([name, definition]) => `- ${name}: ${definition}`)
.join('\n');
const ruleLines = HOLOLAKE_LANGUAGE_KERNEL.runtimeRules
.map((rule, index) => `${index + 1}. ${rule}`)
.join('\n');
return `协议核:${HOLOLAKE_LANGUAGE_KERNEL.id} · ${HOLOLAKE_LANGUAGE_KERNEL.version}\n` +
`产品定义:${HOLOLAKE_LANGUAGE_KERNEL.definition}\n\n` +
`系统分层:\n${layerLines}\n\n` +
`运行规则:\n${ruleLines}`;
}

View file

@ -22,6 +22,7 @@
*/
import { GitEngine } from './git-engine.js';
import { renderLanguageKernelForModel } from './language-protocol-kernel.js';
// ─── 工具定义 ───
@ -30,6 +31,7 @@ export interface ToolDefinition {
description: string;
parameters: Record<string, { type: string; description: string; required?: boolean }>;
execute: (params: Record<string, unknown>) => Promise<string>;
effect: 'read' | 'write' | 'delete';
}
export interface ToolCall {
@ -45,6 +47,15 @@ export interface ToolResult {
error?: string;
}
export interface PendingAction {
id: string;
tool: string;
effect: 'write' | 'delete';
target: string;
summary: string;
createdAt: string;
}
// ─── Agent 定义 ───
export interface PersonaDefinition {
@ -86,6 +97,11 @@ class PersonaToolBridge {
return Array.from(this.registry.values());
}
requiresConfirmation(name: string): boolean {
const effect = this.registry.get(name)?.effect;
return effect === 'write' || effect === 'delete';
}
async execute(call: ToolCall): Promise<ToolResult> {
const tool = this.registry.get(call.name);
if (!tool) {
@ -130,6 +146,7 @@ export class PersonaAgent {
private tools: PersonaToolBridge;
private conversation: Message[] = [];
private git: GitEngine;
private pendingActions = new Map<string, { action: PendingAction; call: ToolCall }>();
constructor(definition: PersonaDefinition, git: GitEngine) {
this.definition = definition;
@ -146,6 +163,7 @@ export class PersonaAgent {
name: 'inspect_repository',
description: '查看知识库本地 Git 与 Forgejo 远端的连接和同步状态(只读)',
parameters: {},
effect: 'read',
execute: async () => JSON.stringify(await this.git.getRepositoryStatus(), null, 2),
});
@ -156,6 +174,7 @@ export class PersonaAgent {
parameters: {
path: { type: 'string', description: '文档路径(相对 docs/ 目录)', required: true },
},
effect: 'read',
execute: async (params) => {
const doc = await this.git.getDoc(params.path as string);
return `标题: ${doc.meta.title}\n更新: ${doc.meta.updatedAt}\n\n${doc.body}`;
@ -171,6 +190,7 @@ export class PersonaAgent {
title: { type: 'string', description: '文档标题', required: true },
body: { type: 'string', description: 'Markdown 正文', required: true },
},
effect: 'write',
execute: async (params) => {
const doc = await this.git.createDoc(
params.path as string,
@ -191,6 +211,7 @@ export class PersonaAgent {
title: { type: 'string', description: '新标题', required: true },
body: { type: 'string', description: '新的 Markdown 正文', required: true },
},
effect: 'write',
execute: async (params) => {
const doc = await this.git.updateDoc(
params.path as string,
@ -209,6 +230,7 @@ export class PersonaAgent {
parameters: {
query: { type: 'string', description: '搜索关键词', required: true },
},
effect: 'read',
execute: async (params) => {
const results = await this.git.search(params.query as string);
if (results.length === 0) return '没有找到匹配的文档';
@ -223,6 +245,7 @@ export class PersonaAgent {
name: 'list_documents',
description: '列出知识库的文档树结构',
parameters: {},
effect: 'read',
execute: async () => {
const tree = await this.git.getTree();
const render = (nodes: any[], indent = 0): string =>
@ -246,6 +269,7 @@ export class PersonaAgent {
parameters: {
path: { type: 'string', description: '文档路径', required: true },
},
effect: 'read',
execute: async (params) => {
const history = await this.git.getHistory(params.path as string, 20);
return history
@ -261,6 +285,7 @@ export class PersonaAgent {
parameters: {
path: { type: 'string', description: '文档路径', required: true },
},
effect: 'delete',
execute: async (params) => {
await this.git.deleteDoc(params.path as string);
return `已删除: ${params.path}`;
@ -291,7 +316,9 @@ export class PersonaAgent {
if (response.toolCalls && response.toolCalls.length > 0) {
const results: ToolResult[] = [];
for (const call of response.toolCalls) {
const result = await this.tools.execute(call);
const result = this.tools.requiresConfirmation(call.name)
? this.stageAction(call)
: await this.tools.execute(call);
results.push(result);
}
@ -345,11 +372,17 @@ export class PersonaAgent {
.map(t => `- ${t.name}: ${t.description}`)
.join('\n');
const languageKernel = renderLanguageKernelForModel();
return `${this.definition.systemPromptBase}
## HoloLake
${languageKernel}
##
HoloLake
HoloLake
##
@ -361,9 +394,10 @@ ${toolsList}
1.
2.
3. Git Forgejo
4.
5.
6.
4.
5. Agent
6.
7.
##
@ -372,6 +406,51 @@ ${toolsList}
- `;
}
private stageAction(call: ToolCall): ToolResult {
const tool = this.tools.get(call.name);
if (!tool || tool.effect === 'read') {
return { id: call.id, name: call.name, output: '', error: `动作不可登记: ${call.name}` };
}
const target = String(call.arguments.path || call.arguments.title || '当前知识库');
const actionId = `action-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
const action: PendingAction = {
id: actionId,
tool: call.name,
effect: tool.effect,
target,
summary: `${tool.description} · ${target}`,
createdAt: new Date().toISOString(),
};
this.pendingActions.set(actionId, { action, call });
return {
id: call.id,
name: call.name,
output: `PENDING_CONFIRMATION ${actionId}: 已登记待确认动作,尚未执行。`,
};
}
getPendingActions(): PendingAction[] {
return Array.from(this.pendingActions.values()).map(item => item.action);
}
async confirmAction(actionId: string): Promise<ToolResult> {
const pending = this.pendingActions.get(actionId);
if (!pending) throw new Error('待确认动作不存在或已经处理');
this.pendingActions.delete(actionId);
const result = await this.tools.execute(pending.call);
this.conversation.push({
role: 'tool',
content: result.error ? `错误: ${result.error}` : result.output,
toolResults: [result],
timestamp: new Date().toISOString(),
});
return result;
}
rejectAction(actionId: string): void {
if (!this.pendingActions.delete(actionId)) throw new Error('待确认动作不存在或已经处理');
}
// ─── LLM 调用(可插拔) ───
private async callLLM(messages: Message[]): Promise<{ content: string; toolCalls?: ToolCall[] }> {
@ -453,6 +532,7 @@ ${toolsList}
clearConversation(): void {
this.conversation = [];
this.pendingActions.clear();
}
getDefinition(): PersonaDefinition {
@ -464,9 +544,10 @@ ${toolsList}
getRuntimeStatus(): { configured: boolean; operational: boolean; toolNames: string[] } {
const configured = Boolean(process.env.OPENAI_API_KEY || process.env.HOLOLAKE_LLM_KEY);
const operational = configured && process.env.HOLOLAKE_LLM_VERIFIED === '1';
return {
configured,
operational: configured,
operational,
toolNames: this.tools.list().map(tool => tool.name),
};
}
@ -477,10 +558,10 @@ ${toolsList}
export function createDefaultPersona(git: GitEngine): PersonaAgent {
return new PersonaAgent(
{
id: 'hololake-assistant-default',
name: 'HoloLake 助手',
role: '知识工作助手 — 支持文档检索、编辑、整理和版本管理',
systemPromptBase: '你是 HoloLake 官方内置知识助手。所有回答与操作都必须遵守产品权限边界和可核验回执原则。',
id: 'hololake-language-entry-default',
name: 'HoloLake',
role: '语言操作入口 · 路由、知识、模块与受控执行',
systemPromptBase: '你是 HoloLake 官方语言操作入口的当前模型计算实例。语言协议、权限边界、路径校验和可核验回执高于模型自由发挥。',
tools: [
'inspect_repository',
'read_document',
@ -491,7 +572,7 @@ export function createDefaultPersona(git: GitEngine): PersonaAgent {
'list_documents',
'view_history',
],
permissionMode: 'auto',
permissionMode: 'ask',
model: process.env.HOLOLAKE_LLM_MODEL || 'gpt-4o',
maxTokens: 4096,
temperature: 0.7,