fix(audit): 修复深度审计 P2 问题 — 自主授权后端守卫、反思历史累积、心跳持久化
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

- M5-补: hand_execute/skill_execute 接收 autonomy_level 参数,后端三层守卫
  (supervised 全部审批 / assisted 尊重 needs_approval / autonomous 跳过)
- M3: hand_approve/hand_cancel 移除 _hand_name 下划线,添加审计日志
- M4-补: 反思历史累积存储到 reflection:history:{agent_id} 数组(最多20条)
  get_history 优先读持久化历史,保留 latest key 向后兼容
- 心跳历史: VikingStorage 持久化 HeartbeatResult 数组,tick() 也存历史
  heartbeat_init 恢复历史,重启后不丢失
- L2: 确认 gatewayStore 仅注释引用,无需修改
- 身份回滚: 确认 IdentityChangeProposal.tsx 已实现 HistoryItem + restoreSnapshot
- 更新 DEEP_AUDIT_REPORT.md 完成度 72% (核心 92%, 真实可用 80%)
This commit is contained in:
iven
2026-03-27 11:32:35 +08:00
parent b7bc9ddcb1
commit 7ae6990c97
7 changed files with 295 additions and 63 deletions

View File

@@ -6,6 +6,8 @@
*/
import { create } from 'zustand';
import type { GatewayClient } from '../lib/gateway-client';
import { canAutoExecute, getAutonomyManager } from '../lib/autonomy-manager';
import type { AutonomyDecision } from '../lib/autonomy-manager';
// === Re-exported Types (from gatewayStore for compatibility) ===
@@ -139,7 +141,7 @@ interface HandClient {
listHands: () => Promise<{ hands?: Array<Record<string, unknown>> } | null>;
getHand: (name: string) => Promise<Record<string, unknown> | null>;
listHandRuns: (name: string, opts?: { limit?: number; offset?: number }) => Promise<{ runs?: RawHandRun[] } | null>;
triggerHand: (name: string, params?: Record<string, unknown>) => Promise<{ runId?: string; status?: string } | null>;
triggerHand: (name: string, params?: Record<string, unknown>, autonomyLevel?: string) => Promise<{ runId?: string; status?: string } | null>;
approveHand: (name: string, runId: string, approved: boolean, reason?: string) => Promise<{ status: string }>;
cancelHand: (name: string, runId: string) => Promise<{ status: string }>;
listTriggers: () => Promise<{ triggers?: Trigger[] } | null>;
@@ -161,6 +163,8 @@ export interface HandStateSlice {
isLoading: boolean;
error: string | null;
client: HandClient | null;
/** Latest autonomy decision (set when action requires approval) */
autonomyDecision: AutonomyDecision | null;
}
// === Store Actions Slice ===
@@ -169,7 +173,7 @@ export interface HandActionsSlice {
setHandStoreClient: (client: HandClient) => void;
loadHands: () => Promise<void>;
getHandDetails: (name: string) => Promise<Hand | undefined>;
triggerHand: (name: string, params?: Record<string, unknown>) => Promise<HandRun | undefined>;
triggerHand: (name: string, params?: Record<string, unknown>, autonomyLevel?: string) => Promise<HandRun | undefined>;
loadHandRuns: (name: string, opts?: { limit?: number; offset?: number }) => Promise<HandRun[]>;
approveHand: (name: string, runId: string, approved: boolean, reason?: string) => Promise<void>;
cancelHand: (name: string, runId: string) => Promise<void>;
@@ -181,6 +185,7 @@ export interface HandActionsSlice {
loadApprovals: (status?: ApprovalStatus) => Promise<void>;
respondToApproval: (approvalId: string, approved: boolean, reason?: string) => Promise<void>;
clearError: () => void;
clearAutonomyDecision: () => void;
}
// === Combined Store Type ===
@@ -195,6 +200,7 @@ export const useHandStore = create<HandStore>((set, get) => ({
approvals: [],
isLoading: false,
error: null,
autonomyDecision: null,
client: null,
// Client injection
@@ -322,8 +328,21 @@ export const useHandStore = create<HandStore>((set, get) => ({
const client = get().client;
if (!client) return undefined;
// Autonomy check before executing hand
const { canProceed, decision } = canAutoExecute('hand_trigger', 5);
if (!canProceed) {
// Store decision for UI to display approval prompt
set(() => ({
autonomyDecision: decision,
}));
return undefined;
}
// Pass current autonomy level to backend for defense-in-depth enforcement
const autonomyLevel = getAutonomyManager().getConfig().level;
try {
const result = await client.triggerHand(name, params);
const result = await client.triggerHand(name, params, autonomyLevel);
if (!result) return undefined;
const run: HandRun = {
@@ -498,6 +517,7 @@ export const useHandStore = create<HandStore>((set, get) => ({
},
clearError: () => set({ error: null }),
clearAutonomyDecision: () => set({ autonomyDecision: null }),
}));
/**