feat: P0 KernelClient功能修复 + P1/P2/P3质量改进
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
P0 KernelClient 功能断裂修复: - Skill CUD: registry.rs create/update/delete + serialize_skill_md + kernel proxy - Workflow CUD: pipeline_commands.rs create/update/delete + serde_yaml依赖 - Agent更新: registry update方法 + AgentConfigUpdated事件 + agent_update命令 - Hand流式事件: HandStart/HandEnd变体替换ToolStart/ToolEnd - 后端验证: hand_get/hand_run_status/hand_run_list确认实现完整 - Approval闭环: respond_to_approval后台spawn+5分钟超时轮询 P2/P3 质量改进: - Browser WebDriver: TCP探测ChromeDriver/GeckoDriver/Edge端口替换硬编码true - api-fallbacks: 移除假技能和16个捏造安全层,替换为真实能力映射 - dead_code清理: 移除5个模块级#![allow(dead_code)],删除3个真正死方法, 删除未注册的compactor_compact_llm命令,warnings从8降到3 - 所有变更通过cargo check + tsc --noEmit验证
This commit is contained in:
@@ -182,12 +182,8 @@ export function getUsageStatsFallback(sessions: SessionForStats[] = []): UsageSt
|
||||
*/
|
||||
export function getPluginStatusFallback(skills: SkillForPlugins[] = []): PluginStatusFallback[] {
|
||||
if (skills.length === 0) {
|
||||
// Return default built-in skills if none provided
|
||||
return [
|
||||
{ id: 'builtin-chat', name: 'Chat', status: 'active', description: '基础对话能力' },
|
||||
{ id: 'builtin-code', name: 'Code', status: 'active', description: '代码生成与分析' },
|
||||
{ id: 'builtin-file', name: 'File', status: 'active', description: '文件操作能力' },
|
||||
];
|
||||
// No skills loaded — return empty rather than fabricating fake builtins
|
||||
return [];
|
||||
}
|
||||
|
||||
return skills.map((skill) => ({
|
||||
@@ -215,26 +211,17 @@ export function getScheduledTasksFallback(triggers: TriggerForTasks[] = []): Sch
|
||||
|
||||
/**
|
||||
* Default security status when /api/security/status returns 404.
|
||||
* ZCLAW has 16 security layers - show them with conservative defaults.
|
||||
* Returns honest minimal response — only includes layers that correspond
|
||||
* to real ZCLAW capabilities, no fabricated layers.
|
||||
*/
|
||||
export function getSecurityStatusFallback(): SecurityStatusFallback {
|
||||
const layers: SecurityLayerFallback[] = [
|
||||
{ name: 'Input Validation', enabled: true, description: '输入验证' },
|
||||
{ name: 'Output Sanitization', enabled: true, description: '输出净化' },
|
||||
{ name: 'Rate Limiting', enabled: true, description: '速率限制' },
|
||||
{ name: 'Authentication', enabled: true, description: '身份认证' },
|
||||
{ name: 'Authorization', enabled: true, description: '权限控制' },
|
||||
{ name: 'Encryption', enabled: true, description: '数据加密' },
|
||||
{ name: 'Audit Logging', enabled: true, description: '审计日志' },
|
||||
{ name: 'Sandboxing', enabled: false, description: '沙箱隔离' },
|
||||
{ name: 'Network Isolation', enabled: false, description: '网络隔离' },
|
||||
{ name: 'Resource Limits', enabled: true, description: '资源限制' },
|
||||
{ name: 'Secret Management', enabled: true, description: '密钥管理' },
|
||||
{ name: 'Certificate Pinning', enabled: false, description: '证书固定' },
|
||||
{ name: 'Code Signing', enabled: false, description: '代码签名' },
|
||||
{ name: 'Secure Boot', enabled: false, description: '安全启动' },
|
||||
{ name: 'TPM Integration', enabled: false, description: 'TPM 集成' },
|
||||
{ name: 'Zero Trust', enabled: false, description: '零信任' },
|
||||
{ name: 'device_auth', enabled: true, description: '设备认证' },
|
||||
{ name: 'rbac', enabled: true, description: '角色权限控制' },
|
||||
{ name: 'audit_log', enabled: true, description: '审计日志' },
|
||||
{ name: 'approval_gate', enabled: true, description: '操作审批门' },
|
||||
{ name: 'input_validation', enabled: true, description: '输入验证' },
|
||||
{ name: 'secret_storage', enabled: true, description: '密钥安全存储 (OS keyring)' },
|
||||
];
|
||||
|
||||
const enabledCount = layers.filter((l) => l.enabled).length;
|
||||
|
||||
@@ -107,11 +107,25 @@ export interface StreamEventError {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface StreamEventHandStart {
|
||||
type: 'handStart';
|
||||
name: string;
|
||||
params: unknown;
|
||||
}
|
||||
|
||||
export interface StreamEventHandEnd {
|
||||
type: 'handEnd';
|
||||
name: string;
|
||||
result: unknown;
|
||||
}
|
||||
|
||||
export type StreamChatEvent =
|
||||
| StreamEventDelta
|
||||
| StreamEventToolStart
|
||||
| StreamEventToolEnd
|
||||
| StreamEventIterationStart
|
||||
| StreamEventHandStart
|
||||
| StreamEventHandEnd
|
||||
| StreamEventComplete
|
||||
| StreamEventError;
|
||||
|
||||
@@ -415,10 +429,33 @@ export class KernelClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update clone — not supported in KernelClient mode
|
||||
* Update clone — maps to kernel agent_update
|
||||
*/
|
||||
async updateClone(_id: string, _updates: Record<string, unknown>): Promise<{ clone: unknown }> {
|
||||
throw new Error('Agent update is not supported in local kernel mode');
|
||||
async updateClone(id: string, updates: Record<string, unknown>): Promise<{ clone: unknown }> {
|
||||
await invoke('agent_update', {
|
||||
agentId: id,
|
||||
updates: {
|
||||
name: updates.name as string | undefined,
|
||||
description: updates.description as string | undefined,
|
||||
systemPrompt: updates.systemPrompt as string | undefined,
|
||||
model: updates.model as string | undefined,
|
||||
provider: updates.provider as string | undefined,
|
||||
maxTokens: updates.maxTokens as number | undefined,
|
||||
temperature: updates.temperature as number | undefined,
|
||||
},
|
||||
});
|
||||
|
||||
// Return updated clone representation
|
||||
const clone = {
|
||||
id,
|
||||
name: updates.name,
|
||||
role: updates.description || updates.role,
|
||||
model: updates.model,
|
||||
personality: updates.personality,
|
||||
communicationStyle: updates.communicationStyle,
|
||||
systemPrompt: updates.systemPrompt,
|
||||
};
|
||||
return { clone };
|
||||
}
|
||||
|
||||
// === Chat ===
|
||||
@@ -514,6 +551,20 @@ export class KernelClient {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'handStart':
|
||||
log.debug('Hand started:', streamEvent.name, streamEvent.params);
|
||||
if (callbacks.onHand) {
|
||||
callbacks.onHand(streamEvent.name, 'running', undefined);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'handEnd':
|
||||
log.debug('Hand ended:', streamEvent.name, streamEvent.result);
|
||||
if (callbacks.onHand) {
|
||||
callbacks.onHand(streamEvent.name, 'completed', streamEvent.result);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'iteration_start':
|
||||
log.debug('Iteration started:', streamEvent.iteration, '/', streamEvent.maxIterations);
|
||||
// Don't need to notify user about iterations
|
||||
@@ -676,7 +727,7 @@ export class KernelClient {
|
||||
try {
|
||||
return await invoke('hand_get', { name });
|
||||
} catch {
|
||||
// hand_get not yet implemented in backend
|
||||
// Hand not found or kernel not initialized
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -735,7 +786,7 @@ export class KernelClient {
|
||||
error?: string;
|
||||
}[]
|
||||
}> {
|
||||
// Hand run history API may not exist yet, return empty array
|
||||
// Hand run history
|
||||
try {
|
||||
return await invoke('hand_run_list', { handName: name, ...opts });
|
||||
} catch {
|
||||
@@ -810,10 +861,102 @@ export class KernelClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a skill
|
||||
* Checks autonomy authorization before execution and passes the autonomy
|
||||
* level to the backend for defense-in-depth enforcement.
|
||||
* Create a new skill
|
||||
*/
|
||||
async createSkill(skill: {
|
||||
name: string;
|
||||
description?: string;
|
||||
triggers: Array<{ type: string; pattern?: string }>;
|
||||
actions: Array<{ type: string; params?: Record<string, unknown> }>;
|
||||
enabled?: boolean;
|
||||
}): Promise<{ skill?: {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
capabilities: string[];
|
||||
tags: string[];
|
||||
mode: string;
|
||||
enabled: boolean;
|
||||
triggers: string[];
|
||||
category?: string;
|
||||
} }> {
|
||||
const result = await invoke<{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
capabilities: string[];
|
||||
tags: string[];
|
||||
mode: string;
|
||||
enabled: boolean;
|
||||
triggers: string[];
|
||||
category?: string;
|
||||
}>('skill_create', {
|
||||
request: {
|
||||
name: skill.name,
|
||||
description: skill.description,
|
||||
triggers: skill.triggers.map(t => t.pattern || t.type),
|
||||
actions: skill.actions.map(a => a.type),
|
||||
enabled: skill.enabled,
|
||||
},
|
||||
});
|
||||
return { skill: result };
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing skill
|
||||
*/
|
||||
async updateSkill(id: string, updates: {
|
||||
name?: string;
|
||||
description?: string;
|
||||
triggers?: Array<{ type: string; pattern?: string }>;
|
||||
actions?: Array<{ type: string; params?: Record<string, unknown> }>;
|
||||
enabled?: boolean;
|
||||
}): Promise<{ skill?: {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
capabilities: string[];
|
||||
tags: string[];
|
||||
mode: string;
|
||||
enabled: boolean;
|
||||
triggers: string[];
|
||||
category?: string;
|
||||
} }> {
|
||||
const result = await invoke<{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
capabilities: string[];
|
||||
tags: string[];
|
||||
mode: string;
|
||||
enabled: boolean;
|
||||
triggers: string[];
|
||||
category?: string;
|
||||
}>('skill_update', {
|
||||
id,
|
||||
request: {
|
||||
name: updates.name,
|
||||
description: updates.description,
|
||||
triggers: updates.triggers?.map(t => t.pattern || t.type),
|
||||
actions: updates.actions?.map(a => a.type),
|
||||
enabled: updates.enabled,
|
||||
},
|
||||
});
|
||||
return { skill: result };
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a skill
|
||||
*/
|
||||
async deleteSkill(id: string): Promise<void> {
|
||||
await invoke('skill_delete', { id });
|
||||
}
|
||||
|
||||
/**
|
||||
async executeSkill(id: string, input?: Record<string, unknown>): Promise<{
|
||||
success: boolean;
|
||||
output?: unknown;
|
||||
|
||||
@@ -667,14 +667,60 @@ function createConfigClientFromKernel(client: KernelClient): ConfigStoreClient {
|
||||
getSkill: async (id: string) => {
|
||||
return { skill: { id, name: id, description: '' } };
|
||||
},
|
||||
createSkill: async () => {
|
||||
throw new Error('Skill creation not supported in KernelClient');
|
||||
createSkill: async (skill) => {
|
||||
try {
|
||||
const result = await client.createSkill(skill);
|
||||
if (result?.skill) {
|
||||
return {
|
||||
skill: {
|
||||
id: result.skill.id,
|
||||
name: result.skill.name,
|
||||
description: result.skill.description,
|
||||
version: result.skill.version,
|
||||
capabilities: result.skill.capabilities,
|
||||
tags: result.skill.tags,
|
||||
mode: result.skill.mode,
|
||||
enabled: result.skill.enabled,
|
||||
triggers: (result.skill.triggers || []).map((t: string) => ({ type: 'keyword', pattern: t })),
|
||||
category: result.skill.category,
|
||||
} as SkillInfo,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
updateSkill: async () => {
|
||||
throw new Error('Skill update not supported in KernelClient');
|
||||
updateSkill: async (id, updates) => {
|
||||
try {
|
||||
const result = await client.updateSkill(id, updates);
|
||||
if (result?.skill) {
|
||||
return {
|
||||
skill: {
|
||||
id: result.skill.id,
|
||||
name: result.skill.name,
|
||||
description: result.skill.description,
|
||||
version: result.skill.version,
|
||||
capabilities: result.skill.capabilities,
|
||||
tags: result.skill.tags,
|
||||
mode: result.skill.mode,
|
||||
enabled: result.skill.enabled,
|
||||
triggers: (result.skill.triggers || []).map((t: string) => ({ type: 'keyword', pattern: t })),
|
||||
category: result.skill.category,
|
||||
} as SkillInfo,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
deleteSkill: async () => {
|
||||
throw new Error('Skill deletion not supported in KernelClient');
|
||||
deleteSkill: async (id) => {
|
||||
try {
|
||||
await client.deleteSkill(id);
|
||||
} catch {
|
||||
// Ignore deletion errors
|
||||
}
|
||||
},
|
||||
listChannels: async () => ({ channels: [] }),
|
||||
getChannel: async () => null,
|
||||
|
||||
@@ -413,14 +413,52 @@ function createWorkflowClientFromKernel(_client: KernelClient): WorkflowClient {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
createWorkflow: async () => {
|
||||
throw new Error('Workflow creation not supported in KernelClient mode. Pipelines are file-based YAML definitions.');
|
||||
createWorkflow: async (workflow) => {
|
||||
try {
|
||||
const result = await invoke<{ id: string; name: string }>('pipeline_create', {
|
||||
request: {
|
||||
name: workflow.name,
|
||||
description: workflow.description,
|
||||
steps: workflow.steps.map((s, i) => ({
|
||||
handName: s.handName,
|
||||
name: s.name || `Step ${i + 1}`,
|
||||
params: s.params,
|
||||
condition: s.condition,
|
||||
})),
|
||||
},
|
||||
});
|
||||
return result;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
updateWorkflow: async () => {
|
||||
throw new Error('Workflow update not supported in KernelClient mode. Pipelines are file-based YAML definitions.');
|
||||
updateWorkflow: async (id, updates) => {
|
||||
try {
|
||||
const result = await invoke<{ id: string; name: string }>('pipeline_update', {
|
||||
pipelineId: id,
|
||||
request: {
|
||||
name: updates.name,
|
||||
description: updates.description,
|
||||
steps: updates.steps?.map((s, i) => ({
|
||||
handName: s.handName,
|
||||
name: s.name || `Step ${i + 1}`,
|
||||
params: s.params,
|
||||
condition: s.condition,
|
||||
})),
|
||||
},
|
||||
});
|
||||
return result;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
deleteWorkflow: async () => {
|
||||
throw new Error('Workflow deletion not supported in KernelClient mode. Pipelines are file-based YAML definitions.');
|
||||
deleteWorkflow: async (id) => {
|
||||
try {
|
||||
await invoke('pipeline_delete', { pipelineId: id });
|
||||
return { status: 'deleted' };
|
||||
} catch {
|
||||
return { status: 'error' };
|
||||
}
|
||||
},
|
||||
executeWorkflow: async (id: string, input?: Record<string, unknown>) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user