refactor: 统一项目名称从OpenFang到ZCLAW
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
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
import { create } from 'zustand';
|
||||
import type { GatewayClient } from '../lib/gateway-client';
|
||||
import { useChatStore } from './chatStore';
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -209,14 +210,25 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
||||
},
|
||||
|
||||
loadUsageStats: async () => {
|
||||
const client = getClient();
|
||||
if (!client) {
|
||||
console.warn('[AgentStore] Client not initialized, skipping loadUsageStats');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = await client.getUsageStats();
|
||||
const { conversations } = useChatStore.getState();
|
||||
|
||||
let totalMessages = 0;
|
||||
for (const conversation of conversations) {
|
||||
for (const message of conversation.messages) {
|
||||
if (message.role === 'user' || message.role === 'assistant') {
|
||||
totalMessages += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stats: UsageStats = {
|
||||
totalSessions: conversations.length,
|
||||
totalMessages,
|
||||
totalTokens: 0,
|
||||
byModel: {},
|
||||
};
|
||||
|
||||
set({ usageStats: stats });
|
||||
} catch {
|
||||
// Usage stats are non-critical, ignore errors silently
|
||||
|
||||
@@ -330,53 +330,28 @@ export const useChatStore = create<ChatState>()(
|
||||
return;
|
||||
}
|
||||
|
||||
// Check context compaction threshold before adding new message
|
||||
try {
|
||||
const messages = get().messages.map(m => ({ role: m.role, content: m.content }));
|
||||
const check = await intelligenceClient.compactor.checkThreshold(messages);
|
||||
if (check.should_compact) {
|
||||
log.debug(`Context compaction triggered (${check.urgency}): ${check.current_tokens} tokens`);
|
||||
const result = await intelligenceClient.compactor.compact(
|
||||
get().messages.map(m => ({
|
||||
role: m.role,
|
||||
content: m.content,
|
||||
id: m.id,
|
||||
timestamp: m.timestamp instanceof Date ? m.timestamp.toISOString() : m.timestamp
|
||||
})),
|
||||
agentId,
|
||||
get().currentConversationId ?? undefined
|
||||
);
|
||||
// Replace messages with compacted version
|
||||
const compactedMsgs: Message[] = result.compacted_messages.map((m, i) => ({
|
||||
id: m.id || `compacted_${i}_${Date.now()}`,
|
||||
role: m.role as Message['role'],
|
||||
content: m.content,
|
||||
timestamp: m.timestamp ? new Date(m.timestamp) : new Date(),
|
||||
}));
|
||||
set({ messages: compactedMsgs });
|
||||
}
|
||||
} catch (err) {
|
||||
log.warn('Context compaction check failed:', err);
|
||||
}
|
||||
// Context compaction is handled by the kernel (AgentLoop with_compaction_threshold).
|
||||
// Frontend no longer performs duplicate compaction — see crates/zclaw-runtime/src/compaction.rs.
|
||||
|
||||
// Build memory-enhanced content
|
||||
// Build memory-enhanced content using layered context (L0/L1/L2)
|
||||
let enhancedContent = content;
|
||||
try {
|
||||
const relevantMemories = await intelligenceClient.memory.search({
|
||||
const contextResult = await intelligenceClient.memory.buildContext(
|
||||
agentId,
|
||||
query: content,
|
||||
limit: 8,
|
||||
minImportance: 3,
|
||||
});
|
||||
const memoryContext = relevantMemories.length > 0
|
||||
? `\n\n## 相关记忆\n${relevantMemories.map(m => `- [${m.type}] ${m.content}`).join('\n')}`
|
||||
: '';
|
||||
const systemPrompt = await intelligenceClient.identity.buildPrompt(agentId, memoryContext);
|
||||
if (systemPrompt) {
|
||||
enhancedContent = `<context>\n${systemPrompt}\n</context>\n\n${content}`;
|
||||
content,
|
||||
500, // token budget for memory context
|
||||
);
|
||||
if (contextResult.systemPromptAddition) {
|
||||
const systemPrompt = await intelligenceClient.identity.buildPrompt(
|
||||
agentId,
|
||||
contextResult.systemPromptAddition,
|
||||
);
|
||||
if (systemPrompt) {
|
||||
enhancedContent = `<context>\n${systemPrompt}\n</context>\n\n${content}`;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log.warn('Memory enhancement failed, proceeding without:', err);
|
||||
log.warn('Memory context build failed, proceeding without:', err);
|
||||
}
|
||||
|
||||
// Add user message (original content for display)
|
||||
@@ -415,7 +390,7 @@ export const useChatStore = create<ChatState>()(
|
||||
// Declare runId before chatStream so callbacks can access it
|
||||
let runId = `run_${Date.now()}`;
|
||||
|
||||
// Try streaming first (OpenFang WebSocket)
|
||||
// Try streaming first (ZCLAW WebSocket)
|
||||
const result = await client.chatStream(
|
||||
enhancedContent,
|
||||
{
|
||||
@@ -571,7 +546,7 @@ export const useChatStore = create<ChatState>()(
|
||||
&& m.streaming
|
||||
&& (
|
||||
(delta.runId && m.runId === delta.runId)
|
||||
|| (!delta.runId && m.runId == null)
|
||||
|| (!delta.runId && m.runId === null)
|
||||
)
|
||||
))
|
||||
|| [...state.messages]
|
||||
@@ -616,7 +591,7 @@ export const useChatStore = create<ChatState>()(
|
||||
}));
|
||||
}
|
||||
} else if (delta.stream === 'hand') {
|
||||
// Handle Hand trigger events from OpenFang
|
||||
// Handle Hand trigger events from ZCLAW
|
||||
const handMsg: Message = {
|
||||
id: `hand_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
||||
role: 'hand',
|
||||
@@ -631,7 +606,7 @@ export const useChatStore = create<ChatState>()(
|
||||
};
|
||||
set((s) => ({ messages: [...s.messages, handMsg] }));
|
||||
} else if (delta.stream === 'workflow') {
|
||||
// Handle Workflow execution events from OpenFang
|
||||
// Handle Workflow execution events from ZCLAW
|
||||
const workflowMsg: Message = {
|
||||
id: `workflow_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
||||
role: 'workflow',
|
||||
|
||||
@@ -8,6 +8,7 @@ import { create } from 'zustand';
|
||||
import type { GatewayModelChoice } from '../lib/gateway-config';
|
||||
import { setStoredGatewayUrl, setStoredGatewayToken } from '../lib/gateway-client';
|
||||
import type { GatewayClient } from '../lib/gateway-client';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -654,9 +655,20 @@ function createConfigClientFromKernel(client: KernelClient): ConfigStoreClient {
|
||||
createChannel: async () => null,
|
||||
updateChannel: async () => null,
|
||||
deleteChannel: async () => {},
|
||||
listScheduledTasks: async () => ({ tasks: [] }),
|
||||
createScheduledTask: async () => {
|
||||
throw new Error('Scheduled tasks not supported in KernelClient');
|
||||
listScheduledTasks: async () => {
|
||||
try {
|
||||
const tasks = await invoke<ScheduledTask[]>('scheduled_task_list');
|
||||
return { tasks };
|
||||
} catch {
|
||||
return { tasks: [] };
|
||||
}
|
||||
},
|
||||
createScheduledTask: async (task) => {
|
||||
const result = await invoke<{ id: string; name: string; schedule: string; status: string }>(
|
||||
'scheduled_task_create',
|
||||
{ request: task },
|
||||
);
|
||||
return { ...result, status: result.status as 'active' | 'paused' | 'completed' | 'error' };
|
||||
},
|
||||
listModels: async () => {
|
||||
try {
|
||||
|
||||
@@ -249,7 +249,7 @@ interface GatewayFacade {
|
||||
modelsLoading: boolean;
|
||||
modelsError: string | null;
|
||||
|
||||
// OpenFang Data
|
||||
// ZCLAW Data
|
||||
hands: Hand[];
|
||||
handRuns: Record<string, HandRun[]>;
|
||||
workflows: Workflow[];
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* handStore.ts - Hand, Trigger, and Approval management store
|
||||
*
|
||||
* Extracted from gatewayStore.ts for Phase 11 Store Refactoring.
|
||||
* Manages OpenFang Hands, Triggers, and Approval workflows.
|
||||
* Manages ZCLAW Hands, Triggers, and Approval workflows.
|
||||
*/
|
||||
import { create } from 'zustand';
|
||||
import type { GatewayClient } from '../lib/gateway-client';
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* securityStore.ts - Security Status and Audit Log Management
|
||||
*
|
||||
* Extracted from gatewayStore.ts for Store Refactoring.
|
||||
* Manages OpenFang security layers, security status, and audit logs.
|
||||
* Manages ZCLAW security layers, security status, and audit logs.
|
||||
* Uses local security checks (security-index.ts + Tauri commands) instead of REST API.
|
||||
*/
|
||||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { GatewayClient } from '../lib/gateway-client';
|
||||
|
||||
// === Types ===
|
||||
@@ -29,7 +30,7 @@ export interface AuditLogEntry {
|
||||
actor?: string;
|
||||
result?: 'success' | 'failure';
|
||||
details?: Record<string, unknown>;
|
||||
// Merkle hash chain fields (OpenFang)
|
||||
// Merkle hash chain fields
|
||||
hash?: string;
|
||||
previousHash?: string;
|
||||
}
|
||||
@@ -45,6 +46,160 @@ function calculateSecurityLevel(enabledCount: number, totalCount: number): 'crit
|
||||
return 'low'; // 0-5 layers
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if OS Keyring (secure store) is available via Tauri command.
|
||||
* Returns false if not in Tauri environment or if keyring is unavailable.
|
||||
*/
|
||||
async function checkKeyringAvailable(): Promise<boolean> {
|
||||
try {
|
||||
return await invoke<boolean>('secure_store_is_available');
|
||||
} catch {
|
||||
// Not in Tauri environment or command failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the ZCLAW Kernel is initialized via Tauri command.
|
||||
*/
|
||||
async function checkKernelInitialized(): Promise<boolean> {
|
||||
try {
|
||||
const status = await invoke<{ initialized: boolean }>('kernel_status');
|
||||
return status.initialized;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the 16-layer security model from local security checks.
|
||||
*/
|
||||
async function buildLocalSecurityLayers(): Promise<SecurityLayer[]> {
|
||||
// Gather local security status
|
||||
let auditEnabled = false;
|
||||
let keychainAvailable = false;
|
||||
let chatStorageInitialized = false;
|
||||
|
||||
try {
|
||||
const { getSecurityStatus } = await import('../lib/security-index');
|
||||
const status = await getSecurityStatus();
|
||||
auditEnabled = status.auditEnabled;
|
||||
keychainAvailable = status.keychainAvailable;
|
||||
chatStorageInitialized = status.chatStorageInitialized;
|
||||
} catch {
|
||||
// Security module not available - use defaults
|
||||
}
|
||||
|
||||
// Check OS Keyring availability directly via Tauri
|
||||
const keyringAvailable = await checkKeyringAvailable();
|
||||
const kernelInitialized = await checkKernelInitialized();
|
||||
|
||||
// Use keychainAvailable from security-index as primary, keyringAvailable as fallback
|
||||
const hasSecureStorage = keychainAvailable || keyringAvailable;
|
||||
|
||||
// Map local security capabilities to the 16-layer security model
|
||||
const layers: SecurityLayer[] = [
|
||||
{
|
||||
name: 'input.validation',
|
||||
enabled: true,
|
||||
description: 'security-utils.ts provides input validation and sanitization',
|
||||
},
|
||||
{
|
||||
name: 'output.filter',
|
||||
enabled: true,
|
||||
description: 'security-utils.ts provides output sanitization and content filtering',
|
||||
},
|
||||
{
|
||||
name: 'rate.limit',
|
||||
enabled: true,
|
||||
description: 'security-utils.ts provides rate limiting',
|
||||
},
|
||||
{
|
||||
name: 'auth.identity',
|
||||
enabled: hasSecureStorage,
|
||||
description: hasSecureStorage
|
||||
? 'OS Keyring available for secure identity storage'
|
||||
: 'OS Keyring not available',
|
||||
},
|
||||
{
|
||||
name: 'incident.response',
|
||||
enabled: auditEnabled,
|
||||
description: auditEnabled
|
||||
? 'Automated incident detection and alerting via audit events'
|
||||
: 'Requires audit logging for incident response',
|
||||
},
|
||||
{
|
||||
name: 'session.management',
|
||||
enabled: true,
|
||||
description: 'Session management is always active',
|
||||
},
|
||||
{
|
||||
name: 'auth.rbac',
|
||||
enabled: hasSecureStorage,
|
||||
description: hasSecureStorage
|
||||
? 'Device authentication and role-based access available'
|
||||
: 'Requires OS Keyring for device authentication',
|
||||
},
|
||||
{
|
||||
name: 'encryption',
|
||||
enabled: chatStorageInitialized,
|
||||
description: chatStorageInitialized
|
||||
? 'Encrypted chat storage is initialized (AES-256-GCM)'
|
||||
: 'Encrypted storage not yet initialized',
|
||||
},
|
||||
{
|
||||
name: 'audit.logging',
|
||||
enabled: auditEnabled,
|
||||
description: auditEnabled
|
||||
? 'Security audit logging is active'
|
||||
: 'Audit logging is disabled',
|
||||
},
|
||||
{
|
||||
name: 'integrity',
|
||||
enabled: auditEnabled,
|
||||
description: auditEnabled
|
||||
? 'Integrity verification enabled via audit log'
|
||||
: 'Requires audit logging for integrity verification',
|
||||
},
|
||||
{
|
||||
name: 'sandbox',
|
||||
enabled: true,
|
||||
description: 'Tauri sandbox provides process isolation',
|
||||
},
|
||||
{
|
||||
name: 'network.security',
|
||||
enabled: true,
|
||||
description: 'WSS enforced, CSP headers active',
|
||||
},
|
||||
{
|
||||
name: 'resource.limits',
|
||||
enabled: true,
|
||||
description: 'Path validation and timeout limits active',
|
||||
},
|
||||
{
|
||||
name: 'capability.gates',
|
||||
enabled: kernelInitialized,
|
||||
description: kernelInitialized
|
||||
? 'Kernel capability gates active'
|
||||
: 'Kernel not yet initialized',
|
||||
},
|
||||
{
|
||||
name: 'prompt.defense',
|
||||
enabled: true,
|
||||
description: 'Input sanitization includes prompt injection defense',
|
||||
},
|
||||
{
|
||||
name: 'anomaly.detection',
|
||||
enabled: auditEnabled,
|
||||
description: auditEnabled
|
||||
? 'Anomaly detection via security audit events'
|
||||
: 'Requires audit logging for anomaly detection',
|
||||
},
|
||||
];
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
// === Client Interface ===
|
||||
|
||||
interface SecurityClient {
|
||||
@@ -81,32 +236,22 @@ export const useSecurityStore = create<SecurityStore>((set, get) => ({
|
||||
client: null,
|
||||
|
||||
loadSecurityStatus: async () => {
|
||||
const client = get().client;
|
||||
if (!client) return;
|
||||
|
||||
set({ securityStatusLoading: true, securityStatusError: null });
|
||||
try {
|
||||
const result = await client.getSecurityStatus();
|
||||
if (result?.layers) {
|
||||
const layers = result.layers as SecurityLayer[];
|
||||
const enabledCount = layers.filter(l => l.enabled).length;
|
||||
const totalCount = layers.length;
|
||||
const securityLevel = calculateSecurityLevel(enabledCount, totalCount);
|
||||
set({
|
||||
securityStatus: { layers, enabledCount, totalCount, securityLevel },
|
||||
securityStatusLoading: false,
|
||||
securityStatusError: null,
|
||||
});
|
||||
} else {
|
||||
set({
|
||||
securityStatusLoading: false,
|
||||
securityStatusError: 'API returned no data',
|
||||
});
|
||||
}
|
||||
const layers = await buildLocalSecurityLayers();
|
||||
const enabledCount = layers.filter(l => l.enabled).length;
|
||||
const totalCount = layers.length;
|
||||
const securityLevel = calculateSecurityLevel(enabledCount, totalCount);
|
||||
set({
|
||||
securityStatus: { layers, enabledCount, totalCount, securityLevel },
|
||||
securityStatusLoading: false,
|
||||
securityStatusError: null,
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
set({
|
||||
securityStatusLoading: false,
|
||||
securityStatusError: (err instanceof Error ? err.message : String(err)) || 'Security API not available',
|
||||
securityStatusError: message || 'Failed to detect security status',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user