From 520034c071211518e41733d2d75bc5ca4185b9f5 Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Sat, 25 Apr 2026 18:51:06 -0700 Subject: [PATCH] v0.50.214: busy input modes + queue/interrupt/steer slash commands (#1067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: busy input modes with queue/interrupt/steer slash commands - Add busy_input_mode setting (queue/interrupt/steer) to config defaults - Add /queue, /interrupt, /steer slash commands with handlers - Modify send() to respect busy_input_mode (interrupt cancels and resends, steer falls back to interrupt with toast, queue preserves existing behavior) - Add settings dropdown in settings panel with load/save/apply wiring - Initialize window._busyInputMode at boot and on settings save - Add 17 i18n keys across all 6 locale blocks (en/ru/es/de/zh/zh-Hant) Addresses #720 * test: 17 regression tests for busy_input_mode + slash commands PR description noted manual testing only. Added structural tests matching the pattern used by recent contributor PRs (#1010, #1011, #1018, #1022, #1058) so future refactors don't silently regress the wiring: Backend (api/config.py): - default 'queue' is set in _DEFAULT_SETTINGS - enum validator restricts to {queue, interrupt, steer} Slash commands (static/commands.js): - /queue, /interrupt, /steer all registered with correct fns - /interrupt and /steer set noEcho:true (the queued payload becomes the visible turn, not the slash invocation) - cmdQueue requires S.busy - cmdInterrupt + cmdSteer call queueSessionMessage before cancelStream (otherwise the drain has nothing to pick up) send() busy branch (static/messages.js): - reads window._busyInputMode - calls cancelStream on interrupt/steer - queues before cancelling (ordering invariant) Boot init + panels.js wiring (static/boot.js, static/panels.js): - both success and fallback paths set window._busyInputMode - load/save/apply path threads busy_input_mode through i18n (static/i18n.js): - all 17 new keys present in each of the 6 locale blocks Co-Authored-By: Claude Opus 4.7 (1M context) * fix: add noEcho:true to /queue; clear pendingFiles in all three slash handlers 1. /queue was missing noEcho:true — the dispatcher would echo the raw slash text as a user bubble, then the drain would send the queued message, causing a double-bubble in the conversation (#840 pattern). 2. cmdQueue, cmdInterrupt, and cmdSteer all captured S.pendingFiles into the queue payload but never cleared S.pendingFiles or called renderTray(). Staged files would remain in the tray and be re-attached on the next send(), duplicating attachments. Fix: add S.pendingFiles=[];renderTray() after updateQueueBadge(). 3. test_all_three_busy_commands_are_no_echo: expanded to cover /queue (was only interrupt + steer), now documents that all three must set noEcho:true. 4. test_slash_commands_clear_pending_files: new test that all three handlers clear S.pendingFiles and call renderTray() after enqueuing. Co-authored-by: bergeouss * docs: v0.50.214 release notes and version bump --------- Co-authored-by: bergeouss Co-authored-by: Nathan Esquenazi Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: nesquena-hermes --- CHANGELOG.md | 10 ++ ROADMAP.md | 2 +- api/config.py | 2 + static/boot.js | 2 + static/commands.js | 61 ++++++++ static/i18n.js | 102 +++++++++++++ static/index.html | 9 ++ static/messages.js | 32 +++- static/panels.js | 10 ++ tests/test_1062_busy_input_modes.py | 220 ++++++++++++++++++++++++++++ 10 files changed, 443 insertions(+), 7 deletions(-) create mode 100644 tests/test_1062_busy_input_modes.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 1da334f6..2236b708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## [Unreleased] +## v0.50.214 — 2026-04-26 + +### Added +- **Busy input mode setting** — new `Settings → Preferences → Busy input mode` dropdown with three options: `Queue` (default, preserves existing behavior), `Interrupt` (cancel the current stream and re-send immediately), `Steer` (placeholder for future mid-stream injection, currently falls back to Interrupt with a toast). (`api/config.py`, `static/messages.js`, `static/boot.js`, `static/panels.js`, `static/index.html`, `static/i18n.js`) Closes #720 [#1062 @bergeouss] +- **`/queue`, `/interrupt`, `/steer` slash commands** — per-message overrides for the busy mode regardless of the current setting. `/queue ` enqueues explicitly; `/interrupt ` cancels the current turn and re-sends; `/steer ` same today with a future-upgrade toast. (`static/commands.js`) [#1062 @bergeouss] + +### Fixed +- **`/queue` command double-bubble** — missing `noEcho:true` caused the raw slash text to be echoed as a user bubble, then the drained message appeared again as a second bubble. (`static/commands.js`) +- **Staged-file duplication via slash commands** — `cmdQueue`, `cmdInterrupt`, and `cmdSteer` captured `S.pendingFiles` but never cleared the tray, so staged files were re-attached on the next send. Added `S.pendingFiles=[];renderTray()` after enqueue in all three handlers. (`static/commands.js`) + ## v0.50.213 — 2026-04-26 ### Fixed diff --git a/ROADMAP.md b/ROADMAP.md index 25012459..38ff5387 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,7 +3,7 @@ > Goal: Full 1:1 parity with the Hermes CLI experience via a clean dark web UI. > Everything you can do from the CLI terminal, you can do from this UI. > -> Last updated: v0.50.213 (April 26, 2026) — 2281 tests collected +> Last updated: v0.50.214 (April 26, 2026) — 2299 tests collected > Tests: 2107 collected (`pytest tests/ --collect-only -q`) > Source: / diff --git a/api/config.py b/api/config.py index 341c0027..75909f31 100644 --- a/api/config.py +++ b/api/config.py @@ -1859,6 +1859,7 @@ _SETTINGS_DEFAULTS = { "show_thinking": True, # show/hide thinking/reasoning blocks in chat view "sidebar_density": "compact", # compact | detailed "auto_title_refresh_every": "0", # adaptive title refresh: 0=off, 5/10/20=every N exchanges + "busy_input_mode": "queue", # behavior when sending while agent is running: queue | interrupt | steer "password_hash": None, # PBKDF2-HMAC-SHA256 hash; None = auth disabled } _SETTINGS_LEGACY_DROP_KEYS = {"assistant_language", "bubble_layout", "default_model"} @@ -1960,6 +1961,7 @@ _SETTINGS_ENUM_VALUES = { "send_key": {"enter", "ctrl+enter"}, "sidebar_density": {"compact", "detailed"}, "auto_title_refresh_every": {"0", "5", "10", "20"}, + "busy_input_mode": {"queue", "interrupt", "steer"}, } _SETTINGS_BOOL_KEYS = { "onboarding_completed", diff --git a/static/boot.js b/static/boot.js index 94a85aa8..234b9213 100644 --- a/static/boot.js +++ b/static/boot.js @@ -794,6 +794,7 @@ function applyBotName(){ window._notificationsEnabled=!!s.notifications_enabled; window._showThinking=s.show_thinking!==false; window._sidebarDensity=(s.sidebar_density==='detailed'?'detailed':'compact'); + window._busyInputMode=(s.busy_input_mode||'queue'); window._botName=s.bot_name||'Hermes'; if(s.default_model) window._defaultModel=s.default_model; // Persist default workspace so the blank new-chat page can show it @@ -820,6 +821,7 @@ function applyBotName(){ window._notificationsEnabled=false; window._showThinking=true; window._sidebarDensity='compact'; + window._busyInputMode='queue'; window._botName='Hermes'; _bootSettings={check_for_updates:false}; if(typeof setLocale==='function'){ diff --git a/static/commands.js b/static/commands.js index 8a934114..bbb9f973 100644 --- a/static/commands.js +++ b/static/commands.js @@ -17,6 +17,9 @@ const COMMANDS=[ {name:'personality', desc:t('cmd_personality'), fn:cmdPersonality, arg:'name', subArgs:'personalities'}, {name:'skills', desc:t('cmd_skills'), fn:cmdSkills, arg:'query'}, {name:'stop', desc:t('cmd_stop'), fn:cmdStop, noEcho:true}, + {name:'queue', desc:t('cmd_queue'), fn:cmdQueue, arg:'message', noEcho:true}, + {name:'interrupt', desc:t('cmd_interrupt'), fn:cmdInterrupt, arg:'message', noEcho:true}, + {name:'steer', desc:t('cmd_steer'), fn:cmdSteer, arg:'message', noEcho:true}, {name:'title', desc:t('cmd_title'), fn:cmdTitle, arg:'[title]'}, {name:'retry', desc:t('cmd_retry'), fn:cmdRetry, noEcho:true}, {name:'undo', desc:t('cmd_undo'), fn:cmdUndo, noEcho:true}, @@ -531,6 +534,64 @@ async function cmdStop(){ if(typeof cancelStream==='function'){await cancelStream();showToast(t('stream_stopped'));} else showToast(t('cancel_unavailable')); } + +// ── Busy-input mode commands ────────────────────────────────────────────── +// These commands let users override the default busy_input_mode setting for a +// specific message. They are only meaningful while the agent is running. + +/** + * /queue — Explicitly queue a message for the next turn. + * Works regardless of the busy_input_mode setting. + */ +async function cmdQueue(args){ + const msg=(args||'').trim(); + if(!msg){showToast(t('cmd_queue_no_msg'));return;} + if(!S.busy){showToast(t('cmd_queue_not_busy'));return;} + if(!S.session){showToast(t('no_active_session'));return;} + queueSessionMessage(S.session.session_id,{text:msg,files:[...S.pendingFiles],model:S.session&&S.session.model||($('modelSelect')&&$('modelSelect').value)||'',profile:S.activeProfile||'default'}); + updateQueueBadge(S.session.session_id); + S.pendingFiles=[];renderTray(); + showToast(t('cmd_queue_confirm'),2000); +} + +/** + * /interrupt — Cancel the current turn and send a new message. + * Calls cancelStream() then queues the message so the drain picks it up. + */ +async function cmdInterrupt(args){ + const msg=(args||'').trim(); + if(!msg){showToast(t('cmd_interrupt_no_msg'));return;} + if(!S.busy||!S.activeStreamId){showToast(t('no_active_task'));return;} + if(!S.session){showToast(t('no_active_session'));return;} + // Queue the message first (before cancel sets busy=false and drains) + queueSessionMessage(S.session.session_id,{text:msg,files:[...S.pendingFiles],model:S.session&&S.session.model||($('modelSelect')&&$('modelSelect').value)||'',profile:S.activeProfile||'default'}); + updateQueueBadge(S.session.session_id); + S.pendingFiles=[];renderTray(); + // Cancel the active stream; setBusy(false) will drain the queue + if(typeof cancelStream==='function'){await cancelStream();} + showToast(t('cmd_interrupt_confirm'),2000); +} + +/** + * /steer — Inject a steering hint mid-task. + * Currently falls back to interrupt behaviour because the WebUI cannot + * inject messages into an in-flight agent thread. Shows a toast to + * inform the user that true steering is not yet available. + */ +async function cmdSteer(args){ + const msg=(args||'').trim(); + if(!msg){showToast(t('cmd_steer_no_msg'));return;} + if(!S.busy||!S.activeStreamId){showToast(t('no_active_task'));return;} + if(!S.session){showToast(t('no_active_session'));return;} + // True steer (inject without cancelling) requires agent-side support + // that is not yet available in the WebUI. Fall back to interrupt. + queueSessionMessage(S.session.session_id,{text:msg,files:[...S.pendingFiles],model:S.session&&S.session.model||($('modelSelect')&&$('modelSelect').value)||'',profile:S.activeProfile||'default'}); + updateQueueBadge(S.session.session_id); + S.pendingFiles=[];renderTray(); + if(typeof cancelStream==='function'){await cancelStream();} + showToast(t('cmd_steer_fallback'),2500); +} + async function cmdTitle(args){ if(!S.session){showToast(t('no_active_session'));return;} const name=(args||'').trim(); diff --git a/static/i18n.js b/static/i18n.js index fd8cc5df..467d00c3 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -99,6 +99,23 @@ const LOCALES = { theme_usage: 'Usage: /theme ', theme_set: 'Theme: ', no_active_session: 'No active session', + cmd_queue: 'Queue a message for the next turn', + cmd_interrupt: 'Cancel current turn and send a new message', + cmd_steer: 'Steer the agent with a correction (falls back to interrupt)', + cmd_queue_no_msg: 'Usage: /queue ', + cmd_queue_not_busy: 'No active task — just send normally', + cmd_queue_confirm: 'Message queued', + cmd_interrupt_no_msg: 'Usage: /interrupt ', + cmd_interrupt_confirm: 'Interrupted — sending new message', + cmd_steer_no_msg: 'Usage: /steer ', + cmd_steer_fallback: 'Steer is not yet available — interrupted and queued instead', + busy_steer_fallback: 'Steer not available — interrupted instead', + busy_interrupt_confirm: 'Interrupted — sending new message', + settings_label_busy_input_mode: 'Busy input mode', + settings_desc_busy_input_mode: 'Controls what happens when you send a message while the agent is running. Queue waits; Interrupt cancels and starts fresh; Steer sends a correction (currently falls back to interrupt).', + settings_busy_input_mode_queue: 'Queue follow-up', + settings_busy_input_mode_interrupt: 'Interrupt current turn', + settings_busy_input_mode_steer: 'Steer (interrupt + send)', slash_skill_badge:'Skill', slash_skill_desc:'Invoke this skill', @@ -708,6 +725,23 @@ const LOCALES = { theme_usage: 'Использование: /theme ', theme_set: 'Тема: ', no_active_session: 'Нет активной сессии', + cmd_queue: 'Поставить сообщение в очередь на следующий оборот', + cmd_interrupt: 'Прервать текущий оборот и отправить новое сообщение', + cmd_steer: 'Направить агента исправлением (переходит к прерыванию)', + cmd_queue_no_msg: 'Использование: /queue <сообщение>', + cmd_queue_not_busy: 'Нет активной задачи — просто отправьте', + cmd_queue_confirm: 'Сообщение в очереди', + cmd_interrupt_no_msg: 'Использование: /interrupt <сообщение>', + cmd_interrupt_confirm: 'Прервано — отправка нового сообщения', + cmd_steer_no_msg: 'Использование: /steer <сообщение>', + cmd_steer_fallback: 'Steer пока недоступен — прервано и поставлено в очередь', + busy_steer_fallback: 'Steer недоступен — прервано', + busy_interrupt_confirm: 'Прервано — отправка нового сообщения', + settings_label_busy_input_mode: 'Режим ввода при занятости', + settings_desc_busy_input_mode: 'Определяет поведение при отправке сообщения во время работы агента. Очередь ждёт; Прерывание отменяет и начинает заново; Steer отправляет исправление (сейчас как прерывание).', + settings_busy_input_mode_queue: 'Поставить в очередь', + settings_busy_input_mode_interrupt: 'Прервать текущий оборот', + settings_busy_input_mode_steer: 'Steer (прерывание + отправка)', no_personalities: 'Личности не найдены (добавьте их в ~/.hermes/personalities/)', clarify_heading: 'Требуется уточнение', @@ -1300,6 +1334,23 @@ const LOCALES = { theme_usage: 'Uso: /theme ', theme_set: 'Tema: ', no_active_session: 'No hay ninguna sesión activa', + cmd_queue: 'Poner mensaje en cola para el siguiente turno', + cmd_interrupt: 'Cancelar turno actual y enviar nuevo mensaje', + cmd_steer: 'Redirigir al agente con una correcci\u00f3n (usa interrupci\u00f3n)', + cmd_queue_no_msg: 'Uso: /queue ', + cmd_queue_not_busy: 'Sin tarea activa \u2014 env\u00eda normalmente', + cmd_queue_confirm: 'Mensaje en cola', + cmd_interrupt_no_msg: 'Uso: /interrupt ', + cmd_interrupt_confirm: 'Interrumpido \u2014 enviando nuevo mensaje', + cmd_steer_no_msg: 'Uso: /steer ', + cmd_steer_fallback: 'Steer no disponible a\u00fan \u2014 interrumpido y encolado', + busy_steer_fallback: 'Steer no disponible \u2014 interrumpido', + busy_interrupt_confirm: 'Interrumpido \u2014 enviando nuevo mensaje', + settings_label_busy_input_mode: 'Modo de entrada ocupada', + settings_desc_busy_input_mode: 'Controla qu\u00e9 sucede al enviar un mensaje mientras el agente est\u00e1 activo. Cola espera; Interrumpir cancela y empieza de nuevo; Steer env\u00eda una correcci\u00f3n (actualmente usa interrupci\u00f3n).', + settings_busy_input_mode_queue: 'Poner en cola', + settings_busy_input_mode_interrupt: 'Interrumpir turno actual', + settings_busy_input_mode_steer: 'Steer (interrupci\u00f3n + env\u00edo)', no_personalities: 'No se encontraron personalidades (añádelas a ~/.hermes/personalities/)', available_personalities: 'Personalidades disponibles:', personality_switch_hint: '\n\nUsa `/personality ` para cambiar, o `/personality none` para limpiar.', @@ -1861,6 +1912,23 @@ const LOCALES = { theme_usage: 'Nutzung: /theme ', theme_set: 'Theme: ', no_active_session: 'Keine aktive Sitzung', + cmd_queue: 'Nachricht f\u00fcr den n\u00e4chsten Durchgang einreihen', + cmd_interrupt: 'Aktuellen Durchgang abbrechen und neue Nachricht senden', + cmd_steer: 'Agent mit Korrektur lenken (f\u00e4llt zur\u00fcck auf Unterbrechung)', + cmd_queue_no_msg: 'Verwendung: /queue ', + cmd_queue_not_busy: 'Keine aktive Aufgabe \u2014 normal senden', + cmd_queue_confirm: 'Nachricht eingereiht', + cmd_interrupt_no_msg: 'Verwendung: /interrupt ', + cmd_interrupt_confirm: 'Unterbrochen \u2014 neue Nachricht wird gesendet', + cmd_steer_no_msg: 'Verwendung: /steer ', + cmd_steer_fallback: 'Steer noch nicht verf\u00fcgbar \u2014 unterbrochen und eingereiht', + busy_steer_fallback: 'Steer nicht verf\u00fcgbar \u2014 unterbrochen', + busy_interrupt_confirm: 'Unterbrochen \u2014 neue Nachricht wird gesendet', + settings_label_busy_input_mode: 'Eingabemodus bei Besch\u00e4ftigung', + settings_desc_busy_input_mode: 'Steuert, was passiert, wenn Sie w\u00e4hrend der Agentenaktivit\u00e4t eine Nachricht senden. Warteschlange wartet; Unterbrechen bricht ab und startet neu; Steer sendet eine Korrektur (aktuell wie Unterbrechen).', + settings_busy_input_mode_queue: 'In Warteschlange einreihen', + settings_busy_input_mode_interrupt: 'Aktuellen Durchgang unterbrechen', + settings_busy_input_mode_steer: 'Steer (Unterbrechen + Senden)', no_personalities: 'Keine Persönlichkeiten gefunden (füge sie in ~/.hermes/personalities/ hinzu)', available_personalities: 'Verfügbare Persönlichkeiten:', personality_switch_hint: '\n\nNutze `/personality ` zum Wechseln, oder `/personality none` zum Löschen.', @@ -2212,6 +2280,23 @@ const LOCALES = { theme_usage: '\u7528\u6cd5\uff1a/theme ', theme_set: '\u4e3b\u9898\uff1a', no_active_session: '\u5f53\u524d\u6ca1\u6709\u6d3b\u52a8\u4f1a\u8bdd', + cmd_queue: '\u5c06\u6d88\u606f\u52a0\u5165\u4e0b\u4e00\u8f6e\u7684\u961f\u5217', + cmd_interrupt: '\u53d6\u6d88\u5f53\u524d\u56de\u5408\u5e76\u53d1\u9001\u65b0\u6d88\u606f', + cmd_steer: '\u7528\u7ea0\u6b63\u4fe1\u606f\u5f15\u5bfc\u4ee3\u7406\uff08\u56de\u9000\u4e3a\u4e2d\u65ad\uff09', + cmd_queue_no_msg: '\u7528\u6cd5\uff1a/queue <\u6d88\u606f>', + cmd_queue_not_busy: '\u6ca1\u6709\u6d3b\u52a8\u4efb\u52a1 \u2014 \u76f4\u63a5\u53d1\u9001\u5373\u53ef', + cmd_queue_confirm: '\u6d88\u606f\u5df2\u52a0\u5165\u961f\u5217', + cmd_interrupt_no_msg: '\u7528\u6cd5\uff1a/interrupt <\u6d88\u606f>', + cmd_interrupt_confirm: '\u5df2\u4e2d\u65ad \u2014 \u6b63\u5728\u53d1\u9001\u65b0\u6d88\u606f', + cmd_steer_no_msg: '\u7528\u6cd5\uff1a/steer <\u6d88\u606f>', + cmd_steer_fallback: 'Steer \u5c1a\u4e0d\u53ef\u7528 \u2014 \u5df2\u4e2d\u65ad\u5e76\u52a0\u5165\u961f\u5217', + busy_steer_fallback: 'Steer \u4e0d\u53ef\u7528 \u2014 \u5df2\u4e2d\u65ad', + busy_interrupt_confirm: '\u5df2\u4e2d\u65ad \u2014 \u6b63\u5728\u53d1\u9001\u65b0\u6d88\u606f', + settings_label_busy_input_mode: '\u5fd9\u788c\u8f93\u5165\u6a21\u5f0f', + settings_desc_busy_input_mode: '\u63a7\u5236\u5728\u4ee3\u7406\u8fd0\u884c\u65f6\u53d1\u9001\u6d88\u606f\u7684\u884c\u4e3a\u3002\u961f\u5217\u7b49\u5f85\uff1b\u4e2d\u65ad\u53d6\u6d88\u5e76\u91cd\u65b0\u5f00\u59cb\uff1bSteer \u53d1\u9001\u7ea0\u6b63\uff08\u76ee\u524d\u56de\u9000\u4e3a\u4e2d\u65ad\uff09\u3002', + settings_busy_input_mode_queue: '\u52a0\u5165\u961f\u5217', + settings_busy_input_mode_interrupt: '\u4e2d\u65ad\u5f53\u524d\u56de\u5408', + settings_busy_input_mode_steer: 'Steer\uff08\u4e2d\u65ad + \u53d1\u9001\uff09', workspace_empty_no_path: '未选择工作区。请在 设置 → 工作区 中设置工作区以浏览文件。', workspace_empty_dir: '此工作区为空。', @@ -3118,6 +3203,23 @@ const LOCALES = { name_required: '\u9700\u8981\u540d\u7a31', never: '\u5f9e\u4e0d', no_active_session: '\u7121\u6d3b\u8e8d\u6703\u8a71', + cmd_queue: '\u5c07\u8a0a\u606f\u52a0\u5165\u4e0b\u4e00\u8f2a\u7684\u4f47\u5217', + cmd_interrupt: '\u53d6\u6d88\u7576\u524d\u56de\u5408\u4e26\u767c\u9001\u65b0\u8a0a\u606f', + cmd_steer: '\u7528\u7d0a\u6b63\u8a0a\u606f\u5f15\u5c0e\u4ee3\u7406\uff08\u56de\u9000\u70ba\u4e2d\u65ad\uff09', + cmd_queue_no_msg: '\u7528\u6cd5\uff1a/queue <\u8a0a\u606f>', + cmd_queue_not_busy: '\u6c92\u6709\u6d3b\u52d5\u4efb\u52d9 \u2014 \u76f4\u63a5\u767c\u9001\u5373\u53ef', + cmd_queue_confirm: '\u8a0a\u606f\u5df2\u52a0\u5165\u4f47\u5217', + cmd_interrupt_no_msg: '\u7528\u6cd5\uff1a/interrupt <\u8a0a\u606f>', + cmd_interrupt_confirm: '\u5df2\u4e2d\u65ad \u2014 \u6b63\u5728\u767c\u9001\u65b0\u8a0a\u606f', + cmd_steer_no_msg: '\u7528\u6cd5\uff1a/steer <\u8a0a\u606f>', + cmd_steer_fallback: 'Steer \u5c1a\u4e0d\u53ef\u7528 \u2014 \u5df2\u4e2d\u65ad\u4e26\u52a0\u5165\u4f47\u5217', + busy_steer_fallback: 'Steer \u4e0d\u53ef\u7528 \u2014 \u5df2\u4e2d\u65ad', + busy_interrupt_confirm: '\u5df2\u4e2d\u65ad \u2014 \u6b63\u5728\u767c\u9001\u65b0\u8a0a\u606f', + settings_label_busy_input_mode: '\u5fd9\u788c\u8f38\u5165\u6a21\u5f0f', + settings_desc_busy_input_mode: '\u63a7\u5236\u5728\u4ee3\u7406\u904b\u884c\u6642\u767c\u9001\u8a0a\u606f\u7684\u884c\u70ba\u3002\u4f47\u5217\u7b49\u5f85\uff1b\u4e2d\u65ad\u53d6\u6d88\u4e26\u91cd\u65b0\u958b\u59cb\uff1bSteer \u767c\u9001\u7d0a\u6b63\uff08\u76ee\u524d\u56de\u9000\u70ba\u4e2d\u65ad\uff09\u3002', + settings_busy_input_mode_queue: '\u52a0\u5165\u4f47\u5217', + settings_busy_input_mode_interrupt: '\u4e2d\u65ad\u7576\u524d\u56de\u5408', + settings_busy_input_mode_steer: 'Steer\uff08\u4e2d\u65ad + \u767c\u9001\uff09', no_active_task: '\u7121\u57f7\u884c\u4e2d\u7684\u4efb\u52d9\u53ef\u505c\u6b62\u3002', no_notes_yet: '\u5c1a\u7121\u5099\u8a3b\u3002', no_profile_yet: '\u5c1a\u7121\u8a2d\u5b9a\u6a94\u3002', diff --git a/static/index.html b/static/index.html index 6c11da75..1f05fe7e 100644 --- a/static/index.html +++ b/static/index.html @@ -662,6 +662,15 @@
Automatically re-generates the session title based on the latest exchange, keeping it relevant as the conversation evolves. Requires an LLM title generation model to be configured.
+
+ + +
Controls what happens when you send a message while the agent is running. Queue waits for the current task; Interrupt cancels and starts fresh; Steer sends a correction (currently falls back to interrupt).
+