mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-29 04:10:00 +08:00
feat(hermes): add display run controls
This commit is contained in:
@@ -3332,6 +3332,8 @@ const HERMES_AGENT_IMAGE_INPUT_MODES = new Set(['auto', 'native', 'text'])
|
||||
const HERMES_DISPLAY_TOOL_PROGRESS_VALUES = new Set(['off', 'new', 'all', 'verbose'])
|
||||
const HERMES_DISPLAY_STREAMING_VALUES = new Set(['inherit', 'true', 'false'])
|
||||
const HERMES_DISPLAY_RESUME_VALUES = new Set(['full', 'minimal'])
|
||||
const HERMES_DISPLAY_BUSY_INPUT_MODES = new Set(['interrupt', 'queue', 'steer'])
|
||||
const HERMES_DISPLAY_BACKGROUND_PROCESS_NOTIFICATIONS = new Set(['off', 'result', 'error', 'all'])
|
||||
const HERMES_DISPLAY_LANGUAGE_VALUES = new Set(['en', 'zh', 'zh-hant', 'ja', 'de', 'es', 'fr', 'tr', 'uk', 'af', 'ko', 'it', 'ga', 'pt', 'ru', 'hu'])
|
||||
const HERMES_RUNTIME_FOOTER_FIELDS = new Set(['model', 'context_pct', 'cwd', 'duration', 'tokens', 'cost'])
|
||||
|
||||
@@ -3463,6 +3465,20 @@ function normalizeHermesDisplayResume(value, strict = false) {
|
||||
return 'full'
|
||||
}
|
||||
|
||||
function normalizeHermesDisplayBusyInputMode(value, strict = false) {
|
||||
const mode = String(value ?? '').trim().toLowerCase() || 'interrupt'
|
||||
if (HERMES_DISPLAY_BUSY_INPUT_MODES.has(mode)) return mode
|
||||
if (strict) throw new Error('display.busy_input_mode 必须是 interrupt、queue 或 steer')
|
||||
return 'interrupt'
|
||||
}
|
||||
|
||||
function normalizeHermesDisplayBackgroundProcessNotifications(value, strict = false) {
|
||||
const mode = String(value ?? '').trim().toLowerCase() || 'all'
|
||||
if (HERMES_DISPLAY_BACKGROUND_PROCESS_NOTIFICATIONS.has(mode)) return mode
|
||||
if (strict) throw new Error('display.background_process_notifications 必须是 off、result、error 或 all')
|
||||
return 'all'
|
||||
}
|
||||
|
||||
function normalizeHermesDisplayLanguage(value, strict = false) {
|
||||
const language = String(value ?? '').trim().toLowerCase() || 'en'
|
||||
if (HERMES_DISPLAY_LANGUAGE_VALUES.has(language)) return language
|
||||
@@ -3514,6 +3530,8 @@ export function buildHermesDisplayConfigValues(config = {}) {
|
||||
displayFileMutationVerifier: readHermesBool(display.file_mutation_verifier, true),
|
||||
displayLanguage: normalizeHermesDisplayLanguage(display.language, false),
|
||||
displayResumeDisplay: normalizeHermesDisplayResume(display.resume_display, false),
|
||||
displayBusyInputMode: normalizeHermesDisplayBusyInputMode(display.busy_input_mode, false),
|
||||
displayBackgroundProcessNotifications: normalizeHermesDisplayBackgroundProcessNotifications(display.background_process_notifications, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3536,6 +3554,8 @@ export function mergeHermesDisplayConfig(config = {}, form = {}) {
|
||||
display.file_mutation_verifier = formHermesBool(form, 'displayFileMutationVerifier', currentValues.displayFileMutationVerifier)
|
||||
display.language = normalizeHermesDisplayLanguage(Object.hasOwn(form, 'displayLanguage') ? form.displayLanguage : currentValues.displayLanguage, true)
|
||||
display.resume_display = normalizeHermesDisplayResume(Object.hasOwn(form, 'displayResumeDisplay') ? form.displayResumeDisplay : currentValues.displayResumeDisplay, true)
|
||||
display.busy_input_mode = normalizeHermesDisplayBusyInputMode(Object.hasOwn(form, 'displayBusyInputMode') ? form.displayBusyInputMode : currentValues.displayBusyInputMode, true)
|
||||
display.background_process_notifications = normalizeHermesDisplayBackgroundProcessNotifications(Object.hasOwn(form, 'displayBackgroundProcessNotifications') ? form.displayBackgroundProcessNotifications : currentValues.displayBackgroundProcessNotifications, true)
|
||||
next.display = display
|
||||
return next
|
||||
}
|
||||
|
||||
@@ -4232,6 +4232,9 @@ const HERMES_DISPLAY_LANGUAGE_VALUES: &[&str] = &[
|
||||
"hu",
|
||||
];
|
||||
|
||||
const HERMES_DISPLAY_BUSY_INPUT_MODES: &[&str] = &["interrupt", "queue", "steer"];
|
||||
const HERMES_DISPLAY_BACKGROUND_PROCESS_NOTIFICATIONS: &[&str] = &["off", "result", "error", "all"];
|
||||
|
||||
const HERMES_RUNTIME_FOOTER_FIELDS: &[&str] =
|
||||
&["model", "context_pct", "cwd", "duration", "tokens", "cost"];
|
||||
|
||||
@@ -4270,6 +4273,44 @@ fn normalize_hermes_display_resume(value: Option<String>, strict: bool) -> Resul
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_hermes_display_busy_input_mode(
|
||||
value: Option<String>,
|
||||
strict: bool,
|
||||
) -> Result<String, String> {
|
||||
let mode = value.unwrap_or_default().trim().to_ascii_lowercase();
|
||||
let mode = if mode.is_empty() {
|
||||
"interrupt".to_string()
|
||||
} else {
|
||||
mode
|
||||
};
|
||||
if HERMES_DISPLAY_BUSY_INPUT_MODES.contains(&mode.as_str()) {
|
||||
Ok(mode)
|
||||
} else if strict {
|
||||
Err("display.busy_input_mode 必须是 interrupt、queue 或 steer".to_string())
|
||||
} else {
|
||||
Ok("interrupt".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_hermes_display_background_process_notifications(
|
||||
value: Option<String>,
|
||||
strict: bool,
|
||||
) -> Result<String, String> {
|
||||
let mode = value.unwrap_or_default().trim().to_ascii_lowercase();
|
||||
let mode = if mode.is_empty() {
|
||||
"all".to_string()
|
||||
} else {
|
||||
mode
|
||||
};
|
||||
if HERMES_DISPLAY_BACKGROUND_PROCESS_NOTIFICATIONS.contains(&mode.as_str()) {
|
||||
Ok(mode)
|
||||
} else if strict {
|
||||
Err("display.background_process_notifications 必须是 off、result、error 或 all".to_string())
|
||||
} else {
|
||||
Ok("all".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_hermes_runtime_footer_fields_text(
|
||||
value: Option<String>,
|
||||
strict: bool,
|
||||
@@ -4382,6 +4423,14 @@ fn build_hermes_display_config_values(config: &serde_yaml::Value) -> Value {
|
||||
display.and_then(|map| yaml_string_field(map, "resume_display")),
|
||||
false,
|
||||
).unwrap_or_else(|_| "full".to_string()),
|
||||
"displayBusyInputMode": normalize_hermes_display_busy_input_mode(
|
||||
display.and_then(|map| yaml_string_field(map, "busy_input_mode")),
|
||||
false,
|
||||
).unwrap_or_else(|_| "interrupt".to_string()),
|
||||
"displayBackgroundProcessNotifications": normalize_hermes_display_background_process_notifications(
|
||||
display.and_then(|map| yaml_string_field(map, "background_process_notifications")),
|
||||
false,
|
||||
).unwrap_or_else(|_| "all".to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4461,6 +4510,28 @@ fn merge_hermes_display_config(config: &mut serde_yaml::Value, form: &Value) ->
|
||||
true,
|
||||
)?),
|
||||
);
|
||||
display.insert(
|
||||
yaml_key("busy_input_mode"),
|
||||
serde_yaml::Value::String(normalize_hermes_display_busy_input_mode(
|
||||
form_string(form, "displayBusyInputMode").or_else(|| {
|
||||
current["displayBusyInputMode"]
|
||||
.as_str()
|
||||
.map(ToString::to_string)
|
||||
}),
|
||||
true,
|
||||
)?),
|
||||
);
|
||||
display.insert(
|
||||
yaml_key("background_process_notifications"),
|
||||
serde_yaml::Value::String(normalize_hermes_display_background_process_notifications(
|
||||
form_string(form, "displayBackgroundProcessNotifications").or_else(|| {
|
||||
current["displayBackgroundProcessNotifications"]
|
||||
.as_str()
|
||||
.map(ToString::to_string)
|
||||
}),
|
||||
true,
|
||||
)?),
|
||||
);
|
||||
let runtime_footer = yaml_child_object(display, "runtime_footer")?;
|
||||
runtime_footer.insert(
|
||||
yaml_key("enabled"),
|
||||
@@ -14213,6 +14284,8 @@ mod hermes_display_config_tests {
|
||||
assert_eq!(values["displayFileMutationVerifier"], true);
|
||||
assert_eq!(values["displayLanguage"], "en");
|
||||
assert_eq!(values["displayResumeDisplay"], "full");
|
||||
assert_eq!(values["displayBusyInputMode"], "interrupt");
|
||||
assert_eq!(values["displayBackgroundProcessNotifications"], "all");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -14232,6 +14305,8 @@ display:
|
||||
file_mutation_verifier: false
|
||||
language: ZH
|
||||
resume_display: minimal
|
||||
busy_input_mode: QUEUE
|
||||
background_process_notifications: ERROR
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -14247,6 +14322,8 @@ display:
|
||||
assert_eq!(values["displayFileMutationVerifier"], false);
|
||||
assert_eq!(values["displayLanguage"], "zh");
|
||||
assert_eq!(values["displayResumeDisplay"], "minimal");
|
||||
assert_eq!(values["displayBusyInputMode"], "queue");
|
||||
assert_eq!(values["displayBackgroundProcessNotifications"], "error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -14280,6 +14357,8 @@ memory:
|
||||
"displayFileMutationVerifier": true,
|
||||
"displayLanguage": "zh-hant",
|
||||
"displayResumeDisplay": "minimal",
|
||||
"displayBusyInputMode": "steer",
|
||||
"displayBackgroundProcessNotifications": "result",
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -14326,6 +14405,11 @@ memory:
|
||||
config["display"]["resume_display"].as_str(),
|
||||
Some("minimal")
|
||||
);
|
||||
assert_eq!(config["display"]["busy_input_mode"].as_str(), Some("steer"));
|
||||
assert_eq!(
|
||||
config["display"]["background_process_notifications"].as_str(),
|
||||
Some("result")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -14353,6 +14437,18 @@ memory:
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err.contains("display.runtime_footer.fields"));
|
||||
|
||||
let err =
|
||||
merge_hermes_display_config(&mut config, &json!({ "displayBusyInputMode": "replace" }))
|
||||
.unwrap_err();
|
||||
assert!(err.contains("display.busy_input_mode"));
|
||||
|
||||
let err = merge_hermes_display_config(
|
||||
&mut config,
|
||||
&json!({ "displayBackgroundProcessNotifications": "silent" }),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err.contains("display.background_process_notifications"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@ const DISPLAY_DEFAULTS = {
|
||||
displayFileMutationVerifier: true,
|
||||
displayLanguage: 'en',
|
||||
displayResumeDisplay: 'full',
|
||||
displayBusyInputMode: 'interrupt',
|
||||
displayBackgroundProcessNotifications: 'all',
|
||||
}
|
||||
|
||||
const HUMAN_DELAY_DEFAULTS = {
|
||||
@@ -191,6 +193,8 @@ const IMAGE_INPUT_MODES = ['auto', 'native', 'text']
|
||||
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 DISPLAY_BUSY_INPUT_MODES = ['interrupt', 'queue', 'steer']
|
||||
const DISPLAY_BACKGROUND_PROCESS_NOTIFICATIONS = ['off', 'result', 'error', 'all']
|
||||
const HUMAN_DELAY_MODES = ['off', 'natural', 'custom']
|
||||
const APPROVAL_MODES = ['manual', 'smart', 'off']
|
||||
const APPROVAL_CRON_MODES = ['deny', 'approve']
|
||||
@@ -781,6 +785,18 @@ export function render() {
|
||||
${DISPLAY_RESUME_VALUES.map(mode => option(`engine.hermesDisplayConfigResumeDisplay_${mode}`, mode, displayValues.displayResumeDisplay)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesDisplayConfigBusyInputMode')}</span>
|
||||
<select id="hm-display-busy-input-mode" class="hm-input" ${disabled ? 'disabled' : ''}>
|
||||
${DISPLAY_BUSY_INPUT_MODES.map(mode => option(`engine.hermesDisplayConfigBusyInputMode_${mode}`, mode, displayValues.displayBusyInputMode)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesDisplayConfigBackgroundProcessNotifications')}</span>
|
||||
<select id="hm-display-background-process-notifications" class="hm-input" ${disabled ? 'disabled' : ''}>
|
||||
${DISPLAY_BACKGROUND_PROCESS_NOTIFICATIONS.map(mode => option(`engine.hermesDisplayConfigBackgroundProcessNotifications_${mode}`, mode, displayValues.displayBackgroundProcessNotifications)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesDisplayConfigRuntimeFooterFields')}</span>
|
||||
<textarea id="hm-display-runtime-footer-fields" class="hm-input" ${disabled ? 'disabled' : ''} style="min-height:96px;resize:vertical">${esc(displayValues.displayRuntimeFooterFields)}</textarea>
|
||||
@@ -2159,6 +2175,8 @@ export function render() {
|
||||
displayFileMutationVerifier: !!el.querySelector('#hm-display-file-mutation-verifier')?.checked,
|
||||
displayLanguage: el.querySelector('#hm-display-language')?.value || 'en',
|
||||
displayResumeDisplay: el.querySelector('#hm-display-resume-display')?.value || 'full',
|
||||
displayBusyInputMode: el.querySelector('#hm-display-busy-input-mode')?.value || 'interrupt',
|
||||
displayBackgroundProcessNotifications: el.querySelector('#hm-display-background-process-notifications')?.value || 'all',
|
||||
}
|
||||
displaySaving = true
|
||||
displayError = null
|
||||
|
||||
@@ -766,7 +766,7 @@ export default {
|
||||
hermesUnauthorizedDmConfigBehavior_ignore: _('静默忽略', 'Silently ignore', '靜默忽略'),
|
||||
hermesUnauthorizedDmConfigFootnote: _('pair 是默认值,会拒绝访问但在私信中回复一次性配对码;ignore 会静默丢弃陌生私信。平台级覆盖仍可在渠道配置或 raw YAML 中单独设置。', 'pair is the default: Hermes denies access but replies with a one-time pairing code in DMs. ignore silently drops unknown DMs. Platform-level overrides can still be set in channel settings or raw YAML.', 'pair 是預設值,會拒絕存取但在私訊中回覆一次性配對碼;ignore 會靜默丟棄陌生私訊。平台級覆蓋仍可在頻道設定或 raw YAML 中單獨設定。'),
|
||||
hermesDisplayConfigTitle: _('全局显示与可靠性', 'Global display and reliability', '全域顯示與可靠性'),
|
||||
hermesDisplayConfigDesc: _('控制消息平台和 CLI 的默认进度展示、静态提示语言、运行信息页脚,以及文件写入失败校验。', 'Control default progress display, static prompt language, runtime footer, and failed file-mutation verification for messaging platforms and CLI.', '控制訊息平台和 CLI 的預設進度顯示、靜態提示語言、執行資訊頁腳,以及檔案寫入失敗校驗。'),
|
||||
hermesDisplayConfigDesc: _('控制消息平台和 CLI 的默认进度展示、忙时输入、后台进程通知、静态提示语言、运行信息页脚,以及文件写入失败校验。', 'Control default progress display, busy input handling, background process notifications, static prompt language, runtime footer, and failed file-mutation verification for messaging platforms and CLI.', '控制訊息平台和 CLI 的預設進度顯示、忙時輸入、背景程序通知、靜態提示語言、執行資訊頁腳,以及檔案寫入失敗校驗。'),
|
||||
hermesDisplayConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
hermesDisplayConfigSave: _('保存显示设置', 'Save display settings', '儲存顯示設定'),
|
||||
hermesDisplayConfigSaveSuccess: _('显示与可靠性配置已保存,建议重启 Hermes Gateway 生效', 'Display and reliability settings saved. Restart Hermes Gateway to take effect.', '顯示與可靠性設定已儲存,建議重啟 Hermes Gateway 生效'),
|
||||
@@ -802,7 +802,16 @@ export default {
|
||||
hermesDisplayConfigResumeDisplay: _('恢复会话展示', 'Resume display', '恢復會話顯示'),
|
||||
hermesDisplayConfigResumeDisplay_full: _('显示完整上下文', 'Show full context', '顯示完整上下文'),
|
||||
hermesDisplayConfigResumeDisplay_minimal: _('仅显示一行摘要', 'Show one-line summary', '僅顯示一行摘要'),
|
||||
hermesDisplayConfigFootnote: _('这里写入全局 display 配置;平台级覆盖仍在渠道页管理。display.streaming 是 CLI-only,本面板不会把它误写成 Gateway 全局流式设置。运行信息字段支持 model、context_pct、cwd、duration、tokens、cost。', 'This writes global display settings; per-platform overrides remain in channel settings. display.streaming is CLI-only, so this panel does not write it as a global Gateway streaming setting. Runtime footer fields support model, context_pct, cwd, duration, tokens, and cost.', '這裡寫入全域 display 設定;平台級覆蓋仍在頻道頁管理。display.streaming 是 CLI-only,本面板不會把它誤寫成 Gateway 全域串流設定。執行資訊欄位支援 model、context_pct、cwd、duration、tokens、cost。'),
|
||||
hermesDisplayConfigBusyInputMode: _('忙时输入处理', 'Busy input handling', '忙時輸入處理'),
|
||||
hermesDisplayConfigBusyInputMode_interrupt: _('打断当前执行', 'Interrupt current run', '打斷目前執行'),
|
||||
hermesDisplayConfigBusyInputMode_queue: _('排队到下一轮', 'Queue for next turn', '排隊到下一輪'),
|
||||
hermesDisplayConfigBusyInputMode_steer: _('尝试中途引导', 'Steer current run', '嘗試中途引導'),
|
||||
hermesDisplayConfigBackgroundProcessNotifications: _('后台进程通知', 'Background process notifications', '背景程序通知'),
|
||||
hermesDisplayConfigBackgroundProcessNotifications_off: _('关闭通知', 'Off', '關閉通知'),
|
||||
hermesDisplayConfigBackgroundProcessNotifications_result: _('仅完成结果', 'Final result only', '僅完成結果'),
|
||||
hermesDisplayConfigBackgroundProcessNotifications_error: _('仅失败结果', 'Errors only', '僅失敗結果'),
|
||||
hermesDisplayConfigBackgroundProcessNotifications_all: _('运行输出与结果', 'Running output and result', '執行輸出與結果'),
|
||||
hermesDisplayConfigFootnote: _('这里写入全局 display 配置;平台级覆盖仍在渠道页管理。忙时输入控制长跑期间新消息如何处理,后台进程通知控制 messaging watcher 噪音。display.streaming 是 CLI-only,本面板不会把它误写成 Gateway 全局流式设置。运行信息字段支持 model、context_pct、cwd、duration、tokens、cost。', 'This writes global display settings; per-platform overrides remain in channel settings. Busy input controls how new messages are handled during long runs, and background process notifications tune messaging watcher noise. display.streaming is CLI-only, so this panel does not write it as a global Gateway streaming setting. Runtime footer fields support model, context_pct, cwd, duration, tokens, and cost.', '這裡寫入全域 display 設定;平台級覆蓋仍在頻道頁管理。忙時輸入控制長跑期間新訊息如何處理,背景程序通知控制 messaging watcher 噪音。display.streaming 是 CLI-only,本面板不會把它誤寫成 Gateway 全域串流設定。執行資訊欄位支援 model、context_pct、cwd、duration、tokens、cost。'),
|
||||
hermesHumanDelayConfigTitle: _('响应节奏', 'Response pacing', '回應節奏'),
|
||||
hermesHumanDelayConfigDesc: _('控制消息平台回复分块之间的等待时间,降低刷屏或模拟更自然发送节奏。', 'Control the wait time between reply chunks on messaging platforms to reduce flooding or mimic a more natural sending rhythm.', '控制訊息平台回覆分塊之間的等待時間,降低刷屏或模擬更自然的傳送節奏。'),
|
||||
hermesHumanDelayConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
|
||||
@@ -127,6 +127,8 @@ test('Hermes 配置页会暴露全局显示与可靠性结构化配置字段', (
|
||||
'hm-display-file-mutation-verifier',
|
||||
'hm-display-language',
|
||||
'hm-display-resume-display',
|
||||
'hm-display-busy-input-mode',
|
||||
'hm-display-background-process-notifications',
|
||||
]) {
|
||||
assert.match(source, new RegExp(`id="${id}"`), `缺少 ${id}`)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ test('Hermes 显示配置读取会提供上游默认值', () => {
|
||||
displayFileMutationVerifier: true,
|
||||
displayLanguage: 'en',
|
||||
displayResumeDisplay: 'full',
|
||||
displayBusyInputMode: 'interrupt',
|
||||
displayBackgroundProcessNotifications: 'all',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -34,6 +36,8 @@ test('Hermes 显示配置读取会规范化已有字段', () => {
|
||||
file_mutation_verifier: false,
|
||||
language: 'ZH',
|
||||
resume_display: 'minimal',
|
||||
busy_input_mode: 'QUEUE',
|
||||
background_process_notifications: 'ERROR',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -45,6 +49,8 @@ test('Hermes 显示配置读取会规范化已有字段', () => {
|
||||
assert.equal(values.displayFileMutationVerifier, false)
|
||||
assert.equal(values.displayLanguage, 'zh')
|
||||
assert.equal(values.displayResumeDisplay, 'minimal')
|
||||
assert.equal(values.displayBusyInputMode, 'queue')
|
||||
assert.equal(values.displayBackgroundProcessNotifications, 'error')
|
||||
})
|
||||
|
||||
test('Hermes 显示配置保存会保留未知 YAML 并写入 display', () => {
|
||||
@@ -70,6 +76,8 @@ test('Hermes 显示配置保存会保留未知 YAML 并写入 display', () => {
|
||||
displayFileMutationVerifier: true,
|
||||
displayLanguage: 'zh-hant',
|
||||
displayResumeDisplay: 'minimal',
|
||||
displayBusyInputMode: 'steer',
|
||||
displayBackgroundProcessNotifications: 'result',
|
||||
})
|
||||
|
||||
assert.deepEqual(next.model, { provider: 'anthropic' })
|
||||
@@ -85,6 +93,8 @@ test('Hermes 显示配置保存会保留未知 YAML 并写入 display', () => {
|
||||
assert.equal(next.display.file_mutation_verifier, true)
|
||||
assert.equal(next.display.language, 'zh-hant')
|
||||
assert.equal(next.display.resume_display, 'minimal')
|
||||
assert.equal(next.display.busy_input_mode, 'steer')
|
||||
assert.equal(next.display.background_process_notifications, 'result')
|
||||
})
|
||||
|
||||
test('Hermes 显示配置保存会拒绝非法枚举和页脚字段', () => {
|
||||
@@ -104,4 +114,12 @@ test('Hermes 显示配置保存会拒绝非法枚举和页脚字段', () => {
|
||||
() => mergeHermesDisplayConfig({}, { displayRuntimeFooterFields: 'model\npassword' }),
|
||||
/display\.runtime_footer\.fields/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesDisplayConfig({}, { displayBusyInputMode: 'replace' }),
|
||||
/display\.busy_input_mode/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesDisplayConfig({}, { displayBackgroundProcessNotifications: 'silent' }),
|
||||
/display\.background_process_notifications/,
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user