feat(hermes): add approval safety settings

This commit is contained in:
晴天
2026-05-25 02:26:50 +08:00
parent ae1208d856
commit e74df5f288
8 changed files with 583 additions and 21 deletions

View File

@@ -136,6 +136,14 @@ const CHECKPOINTS_DEFAULTS = {
checkpointMinIntervalHours: 24,
}
const APPROVALS_DEFAULTS = {
approvalMode: 'manual',
approvalTimeout: 60,
approvalCronMode: 'deny',
approvalMcpReloadConfirm: true,
approvalDestructiveSlashConfirm: true,
}
const PRIVACY_DEFAULTS = {
redactPii: false,
}
@@ -171,6 +179,8 @@ const DISPLAY_TOOL_PROGRESS_VALUES = ['off', 'new', 'all', 'verbose']
const DISPLAY_LANGUAGE_VALUES = ['en', 'zh', 'zh-hant', 'ja', 'de', 'es', 'fr', 'tr', 'uk', 'af', 'ko', 'it', 'ga', 'pt', 'ru', 'hu']
const DISPLAY_RESUME_VALUES = ['full', 'minimal']
const HUMAN_DELAY_MODES = ['off', 'natural', 'custom']
const APPROVAL_MODES = ['manual', 'smart', 'off']
const APPROVAL_CRON_MODES = ['deny', 'approve']
export function render() {
const el = document.createElement('div')
@@ -193,6 +203,7 @@ export function render() {
let executionLimitsValues = { ...EXECUTION_LIMITS_DEFAULTS }
let ioSafetyValues = { ...IO_SAFETY_DEFAULTS }
let checkpointsValues = { ...CHECKPOINTS_DEFAULTS }
let approvalsValues = { ...APPROVALS_DEFAULTS }
let privacyValues = { ...PRIVACY_DEFAULTS }
let browserValues = { ...BROWSER_DEFAULTS }
let terminalValues = { ...TERMINAL_DEFAULTS }
@@ -213,6 +224,7 @@ export function render() {
let executionLimitsLoading = true
let ioSafetyLoading = true
let checkpointsLoading = true
let approvalsLoading = true
let privacyLoading = true
let browserLoading = true
let terminalLoading = true
@@ -233,6 +245,7 @@ export function render() {
let executionLimitsSaving = false
let ioSafetySaving = false
let checkpointsSaving = false
let approvalsSaving = false
let privacySaving = false
let browserSaving = false
let terminalSaving = false
@@ -253,6 +266,7 @@ export function render() {
let executionLimitsError = null
let ioSafetyError = null
let checkpointsError = null
let approvalsError = null
let privacyError = null
let browserError = null
let terminalError = null
@@ -266,7 +280,7 @@ export function render() {
}
function isBusy() {
return loading || runtimeLoading || compressionLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || quickCommandsLoading || agentToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || privacyLoading || browserLoading || terminalLoading || saving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || privacySaving || browserSaving || terminalSaving
return loading || runtimeLoading || compressionLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || quickCommandsLoading || agentToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || approvalsLoading || privacyLoading || browserLoading || terminalLoading || saving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving
}
function option(labelKey, value, selected) {
@@ -283,7 +297,7 @@ export function render() {
}
function renderRuntimePanel() {
const disabled = loading || saving || runtimeLoading || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || runtimeLoading || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel">
<div class="hm-panel-header">
@@ -331,7 +345,7 @@ export function render() {
}
function renderCompressionPanel() {
const disabled = loading || saving || compressionLoading || compressionSaving || runtimeSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || compressionLoading || compressionSaving || runtimeSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-compression-panel">
<div class="hm-panel-header">
@@ -381,7 +395,7 @@ export function render() {
}
function renderToolGuardrailsPanel() {
const disabled = loading || saving || toolGuardrailsLoading || toolGuardrailsSaving || runtimeSaving || compressionSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || toolGuardrailsLoading || toolGuardrailsSaving || runtimeSaving || compressionSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-guardrails-panel">
<div class="hm-panel-header">
@@ -443,7 +457,7 @@ export function render() {
}
function renderMemoryPanel() {
const disabled = loading || saving || memoryLoading || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || memoryLoading || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-memory-panel">
<div class="hm-panel-header">
@@ -493,7 +507,7 @@ export function render() {
}
function renderSkillsConfigPanel() {
const disabled = loading || saving || skillsLoading || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || skillsLoading || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-skills-panel">
<div class="hm-panel-header">
@@ -525,7 +539,7 @@ export function render() {
}
function renderQuickCommandsConfigPanel() {
const disabled = loading || saving || quickCommandsLoading || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || quickCommandsLoading || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-quick-commands-panel">
<div class="hm-panel-header">
@@ -551,7 +565,7 @@ export function render() {
}
function renderAgentToolsetsConfigPanel() {
const disabled = loading || saving || agentToolsetsLoading || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || agentToolsetsLoading || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-agent-toolsets-panel">
<div class="hm-panel-header">
@@ -577,7 +591,7 @@ export function render() {
}
function renderAgentRuntimeConfigPanel() {
const disabled = loading || saving || agentRuntimeLoading || agentRuntimeSaving || agentToolsetsSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || privacySaving || browserSaving || terminalSaving
const disabled = loading || saving || agentRuntimeLoading || agentRuntimeSaving || agentToolsetsSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-agent-runtime-panel">
<div class="hm-panel-header">
@@ -639,7 +653,7 @@ export function render() {
}
function renderUnauthorizedDmConfigPanel() {
const disabled = loading || saving || unauthorizedDmLoading || unauthorizedDmSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || unauthorizedDmLoading || unauthorizedDmSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-unauthorized-dm-panel">
<div class="hm-panel-header">
@@ -669,7 +683,7 @@ export function render() {
}
function renderSecurityConfigPanel() {
const disabled = loading || saving || securityLoading || securitySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || securityLoading || securitySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-security-panel">
<div class="hm-panel-header">
@@ -711,7 +725,7 @@ export function render() {
}
function renderDisplayConfigPanel() {
const disabled = loading || saving || displayLoading || displaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || displayLoading || displaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-display-panel">
<div class="hm-panel-header">
@@ -775,7 +789,7 @@ export function render() {
}
function renderHumanDelayConfigPanel() {
const disabled = loading || saving || humanDelayLoading || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || humanDelayLoading || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-human-delay-panel">
<div class="hm-panel-header">
@@ -813,7 +827,7 @@ export function render() {
}
function renderStreamingPanel() {
const disabled = loading || saving || streamingLoading || streamingSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || executionLimitsSaving || checkpointsSaving || terminalSaving
const disabled = loading || saving || streamingLoading || streamingSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || executionLimitsSaving || checkpointsSaving || approvalsSaving || terminalSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-streaming-panel">
<div class="hm-panel-header">
@@ -865,7 +879,7 @@ export function render() {
}
function renderExecutionLimitsPanel() {
const disabled = loading || saving || executionLimitsLoading || executionLimitsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || checkpointsSaving
const disabled = loading || saving || executionLimitsLoading || executionLimitsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || checkpointsSaving || approvalsSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-execution-limits-panel">
<div class="hm-panel-header">
@@ -937,7 +951,7 @@ export function render() {
}
function renderIoSafetyPanel() {
const disabled = loading || saving || ioSafetyLoading || ioSafetySaving || checkpointsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
const disabled = loading || saving || ioSafetyLoading || ioSafetySaving || checkpointsSaving || approvalsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-io-safety-panel">
<div class="hm-panel-header">
@@ -977,7 +991,7 @@ export function render() {
}
function renderCheckpointsPanel() {
const disabled = loading || saving || checkpointsLoading || checkpointsSaving || ioSafetySaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
const disabled = loading || saving || checkpointsLoading || checkpointsSaving || ioSafetySaving || approvalsSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-checkpoints-panel">
<div class="hm-panel-header">
@@ -1034,8 +1048,58 @@ export function render() {
`
}
function renderApprovalsPanel() {
const disabled = loading || saving || approvalsLoading || approvalsSaving || checkpointsSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-approvals-panel">
<div class="hm-panel-header">
<div>
<div class="hm-panel-title">${t('engine.hermesApprovalsConfigTitle')}</div>
<div class="hm-channel-panel-desc">${t('engine.hermesApprovalsConfigDesc')}</div>
</div>
<div class="hm-panel-actions">
<span class="hm-muted">${approvalsSaving ? t('engine.hermesConfigStatusSaving') : approvalsLoading ? t('engine.hermesConfigStatusLoading') : t('engine.hermesApprovalsConfigStatusReady')}</span>
<button class="hm-btn hm-btn--cta hm-btn--sm" id="hm-approvals-save" ${disabled ? 'disabled' : ''}>${t('engine.hermesApprovalsConfigSave')}</button>
</div>
</div>
<div class="hm-panel-body">
${renderError(approvalsError)}
<div class="hm-config-runtime-grid hm-config-approvals-grid">
<label class="hm-field">
<span class="hm-field-label">${t('engine.hermesApprovalsConfigMode')}</span>
<select id="hm-approval-mode" class="hm-input" ${disabled ? 'disabled' : ''}>
${APPROVAL_MODES.map(mode => option(`engine.hermesApprovalsConfigMode_${mode}`, mode, approvalsValues.approvalMode)).join('')}
</select>
</label>
<label class="hm-field">
<span class="hm-field-label">${t('engine.hermesApprovalsConfigTimeout')}</span>
<input id="hm-approval-timeout" class="hm-input" type="number" inputmode="numeric" min="1" max="86400" step="1" value="${esc(approvalsValues.approvalTimeout)}" ${disabled ? 'disabled' : ''}>
</label>
<label class="hm-field">
<span class="hm-field-label">${t('engine.hermesApprovalsConfigCronMode')}</span>
<select id="hm-approval-cron-mode" class="hm-input" ${disabled ? 'disabled' : ''}>
${APPROVAL_CRON_MODES.map(mode => option(`engine.hermesApprovalsConfigCronMode_${mode}`, mode, approvalsValues.approvalCronMode)).join('')}
</select>
</label>
</div>
<div class="hm-config-check-grid">
<label class="hm-channel-check">
<input id="hm-approval-mcp-reload-confirm" type="checkbox" ${approvalsValues.approvalMcpReloadConfirm ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
<span>${t('engine.hermesApprovalsConfigMcpReloadConfirm')}</span>
</label>
<label class="hm-channel-check hm-channel-check--danger">
<input id="hm-approval-destructive-slash-confirm" type="checkbox" ${approvalsValues.approvalDestructiveSlashConfirm ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
<span>${t('engine.hermesApprovalsConfigDestructiveSlashConfirm')}</span>
</label>
</div>
<div class="hm-channel-footnote">${t('engine.hermesApprovalsConfigFootnote')}</div>
</div>
</div>
`
}
function renderPrivacyPanel() {
const disabled = loading || saving || privacyLoading || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
const disabled = loading || saving || privacyLoading || privacySaving || approvalsSaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-privacy-panel">
<div class="hm-panel-header">
@@ -1063,7 +1127,7 @@ export function render() {
}
function renderBrowserPanel() {
const disabled = loading || saving || browserLoading || browserSaving || privacySaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
const disabled = loading || saving || browserLoading || browserSaving || approvalsSaving || privacySaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-browser-panel">
<div class="hm-panel-header">
@@ -1107,7 +1171,7 @@ export function render() {
}
function renderTerminalPanel() {
const disabled = loading || saving || terminalLoading || terminalSaving || browserSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving
const disabled = loading || saving || terminalLoading || terminalSaving || approvalsSaving || browserSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving
return `
<div class="hm-panel hm-config-runtime-panel hm-config-terminal-panel">
<div class="hm-panel-header">
@@ -1197,6 +1261,7 @@ export function render() {
${renderExecutionLimitsPanel()}
${renderIoSafetyPanel()}
${renderCheckpointsPanel()}
${renderApprovalsPanel()}
${renderPrivacyPanel()}
${renderBrowserPanel()}
${renderCompressionPanel()}
@@ -1245,6 +1310,7 @@ export function render() {
el.querySelector('#hm-execution-limits-save')?.addEventListener('click', saveExecutionLimits)
el.querySelector('#hm-io-safety-save')?.addEventListener('click', saveIoSafety)
el.querySelector('#hm-checkpoints-save')?.addEventListener('click', saveCheckpoints)
el.querySelector('#hm-approvals-save')?.addEventListener('click', saveApprovalsConfig)
el.querySelector('#hm-privacy-save')?.addEventListener('click', savePrivacyConfig)
el.querySelector('#hm-browser-save')?.addEventListener('click', saveBrowserConfig)
el.querySelector('#hm-terminal-save')?.addEventListener('click', saveTerminal)
@@ -1335,6 +1401,11 @@ export function render() {
checkpointsValues = { ...CHECKPOINTS_DEFAULTS, ...(data?.values || {}) }
}
async function loadApprovalsConfig() {
const data = await api.hermesApprovalsConfigRead()
approvalsValues = { ...APPROVALS_DEFAULTS, ...(data?.values || {}) }
}
async function loadPrivacyConfig() {
const data = await api.hermesPrivacyConfigRead()
privacyValues = { ...PRIVACY_DEFAULTS, ...(data?.values || {}) }
@@ -1368,6 +1439,7 @@ export function render() {
executionLimitsLoading = true
ioSafetyLoading = true
checkpointsLoading = true
approvalsLoading = true
privacyLoading = true
browserLoading = true
terminalLoading = true
@@ -1388,6 +1460,7 @@ export function render() {
executionLimitsError = null
ioSafetyError = null
checkpointsError = null
approvalsError = null
privacyError = null
browserError = null
terminalError = null
@@ -1455,6 +1528,14 @@ export function render() {
checkpointsLoading = false
draw()
}
try {
await loadApprovalsConfig()
} catch (err) {
approvalsError = humanizeError(err, t('engine.hermesApprovalsConfigLoadFailed') || 'Load approvals config failed')
} finally {
approvalsLoading = false
draw()
}
try {
await loadPrivacyConfig()
} catch (err) {
@@ -1620,6 +1701,9 @@ export function render() {
try {
await loadCheckpoints()
} catch {}
try {
await loadApprovalsConfig()
} catch {}
try {
await loadPrivacyConfig()
} catch {}
@@ -2104,6 +2188,35 @@ export function render() {
}
}
async function saveApprovalsConfig() {
const form = {
approvalMode: el.querySelector('#hm-approval-mode')?.value || 'manual',
approvalTimeout: el.querySelector('#hm-approval-timeout')?.value || '60',
approvalCronMode: el.querySelector('#hm-approval-cron-mode')?.value || 'deny',
approvalMcpReloadConfirm: !!el.querySelector('#hm-approval-mcp-reload-confirm')?.checked,
approvalDestructiveSlashConfirm: !!el.querySelector('#hm-approval-destructive-slash-confirm')?.checked,
}
approvalsSaving = true
approvalsError = null
draw()
try {
const result = await api.hermesApprovalsConfigSave(form)
approvalsValues = { ...APPROVALS_DEFAULTS, ...(result?.values || form) }
await refreshRawAfterStructuredSave()
const backup = result?.backup || ''
toast({
message: t('engine.hermesApprovalsConfigSaveSuccess'),
hint: backup ? t('engine.hermesConfigBackupHint', { path: backup }) : '',
}, 'success')
} catch (err) {
approvalsError = humanizeError(err, t('engine.hermesApprovalsConfigSaveFailed') || 'Save approvals config failed')
toast(approvalsError, 'error')
} finally {
approvalsSaving = false
draw()
}
}
async function savePrivacyConfig() {
const form = {
redactPii: !!el.querySelector('#hm-privacy-redact-pii')?.checked,

View File

@@ -541,6 +541,8 @@ export const api = {
hermesIoSafetyConfigSave: (form) => invoke('hermes_io_safety_config_save', { form }),
hermesCheckpointsConfigRead: () => invoke('hermes_checkpoints_config_read'),
hermesCheckpointsConfigSave: (form) => invoke('hermes_checkpoints_config_save', { form }),
hermesApprovalsConfigRead: () => invoke('hermes_approvals_config_read'),
hermesApprovalsConfigSave: (form) => invoke('hermes_approvals_config_save', { form }),
hermesPrivacyConfigRead: () => invoke('hermes_privacy_config_read'),
hermesPrivacyConfigSave: (form) => invoke('hermes_privacy_config_save', { form }),
hermesBrowserConfigRead: () => invoke('hermes_browser_config_read'),

View File

@@ -592,6 +592,24 @@ export default {
hermesCheckpointsConfigRetentionDays: _('保留天数', 'Retention days', '保留天數'),
hermesCheckpointsConfigMinIntervalHours: _('自动清理最小间隔小时', 'Minimum auto-prune interval hours', '自動清理最小間隔小時'),
hermesCheckpointsConfigFootnote: _('这里写入 checkpoints.*。容量上限或单文件上限设为 0 表示关闭对应限制;其他 checkpoints 高级字段会保留在 raw YAML 中。', 'This writes checkpoints.*. Set total size cap or single-file cap to 0 to disable that limit. Other advanced checkpoint fields stay in raw YAML.', '這裡寫入 checkpoints.*。容量上限或單檔上限設為 0 表示關閉對應限制;其他 checkpoints 進階欄位會保留在 raw YAML 中。'),
hermesApprovalsConfigTitle: _('审批安全', 'Approval safety', '審批安全'),
hermesApprovalsConfigDesc: _('控制危险命令、Cron 任务和破坏性 slash 命令的审批策略,避免无人值守长跑任务误放行高风险操作。', 'Control approval policy for dangerous commands, cron jobs, and destructive slash commands so unattended long runs do not approve risky operations by mistake.', '控制危險命令、Cron 任務和破壞性 slash 命令的審批策略,避免無人值守長跑任務誤放行高風險操作。'),
hermesApprovalsConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
hermesApprovalsConfigSave: _('保存审批配置', 'Save approval settings', '儲存審批設定'),
hermesApprovalsConfigSaveSuccess: _('审批安全配置已保存,建议重启 Hermes Gateway 生效', 'Approval safety settings saved. Restart Hermes Gateway to take effect.', '審批安全設定已儲存,建議重啟 Hermes Gateway 生效'),
hermesApprovalsConfigLoadFailed: _('加载审批安全配置失败', 'Load approval safety settings failed', '載入審批安全設定失敗'),
hermesApprovalsConfigSaveFailed: _('保存审批安全配置失败', 'Save approval safety settings failed', '儲存審批安全設定失敗'),
hermesApprovalsConfigMode: _('交互审批模式', 'Interactive approval mode', '互動審批模式'),
hermesApprovalsConfigMode_manual: _('手动确认', 'Manual confirmation', '手動確認'),
hermesApprovalsConfigMode_smart: _('智能判断', 'Smart decision', '智慧判斷'),
hermesApprovalsConfigMode_off: _('关闭审批', 'Disable approvals', '關閉審批'),
hermesApprovalsConfigTimeout: _('等待审批超时秒数', 'Approval timeout seconds', '等待審批逾時秒數'),
hermesApprovalsConfigCronMode: _('Cron 任务默认策略', 'Default cron-job policy', 'Cron 任務預設策略'),
hermesApprovalsConfigCronMode_deny: _('默认拒绝', 'Deny by default', '預設拒絕'),
hermesApprovalsConfigCronMode_approve: _('默认批准', 'Approve by default', '預設批准'),
hermesApprovalsConfigMcpReloadConfirm: _('重载 MCP 工具前要求确认', 'Confirm before reloading MCP tools', '重載 MCP 工具前要求確認'),
hermesApprovalsConfigDestructiveSlashConfirm: _('破坏性 slash 命令要求确认', 'Confirm destructive slash commands', '破壞性 slash 命令要求確認'),
hermesApprovalsConfigFootnote: _('推荐保持交互审批为手动或智能,并让 Cron 默认拒绝危险命令;只有在完全可信的无人值守环境中才使用默认批准。其他 approvals 高级字段会保留在 raw YAML 中。', 'Manual or smart interactive approval is recommended, with cron jobs denying dangerous commands by default. Use default approval only in fully trusted unattended environments. Other advanced approvals fields stay in raw YAML.', '建議保持互動審批為手動或智慧,並讓 Cron 預設拒絕危險命令;只有在完全可信的無人值守環境中才使用預設批准。其他 approvals 進階欄位會保留在 raw YAML 中。'),
hermesPrivacyConfigTitle: _('隐私脱敏', 'Privacy redaction', '隱私脫敏'),
hermesPrivacyConfigDesc: _('对支持的 Gateway 渠道在送入模型前脱敏用户 ID、手机号和会话标识降低公网渠道泄露风险。', 'Redact user IDs, phone numbers, and chat identifiers before supported Gateway channels send context to the model, reducing public-channel exposure risk.', '對支援的 Gateway 渠道在送入模型前脫敏使用者 ID、電話號碼和會話識別降低公開渠道外洩風險。'),
hermesPrivacyConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),