From 3863d13440d61aba6c8abc36d2d763e8364d950e Mon Sep 17 00:00:00 2001
From: Teknium <127238744+teknium1@users.noreply.github.com>
Date: Tue, 8 Sep 2026 03:36:32 -0700
Subject: [PATCH] docs: describe one-line docks and consistent transcript
controls
---
ui-tui/src/__tests__/agentsCompact.test.tsx | 40 +++++++++++------
ui-tui/src/components/agentsOverlay.tsx | 21 ++++++---
ui-tui/src/components/agentsPanel.tsx | 45 +++++++++++++------
ui-tui/src/components/appLayout.tsx | 15 +++++--
ui-tui/src/components/textInput.tsx | 15 +++++--
website/docs/user-guide/cli.md | 1 +
.../docs/user-guide/features/delegation.md | 4 +-
website/docs/user-guide/tui.md | 3 +-
.../current/user-guide/cli.md | 1 +
.../current/user-guide/features/delegation.md | 4 +-
.../current/user-guide/tui.md | 3 +-
11 files changed, 109 insertions(+), 43 deletions(-)
diff --git a/ui-tui/src/__tests__/agentsCompact.test.tsx b/ui-tui/src/__tests__/agentsCompact.test.tsx
index 0507f37805..59e2cf2c05 100644
--- a/ui-tui/src/__tests__/agentsCompact.test.tsx
+++ b/ui-tui/src/__tests__/agentsCompact.test.tsx
@@ -27,34 +27,48 @@ it('keeps collapsed live chrome to one row without losing count or restore contr
expect(text).toContain(`${rows.running} live agents`)
expect(text).toContain('Ctrl+T expand')
expect(text).toContain('F7 restore')
- expect(renderToScreen(, cols).height).toBeGreaterThan(view.height)
+ expect(renderToScreen(, cols).height).toBeGreaterThan(
+ view.height
+ )
}
})
it('opens the selected live transcript on Enter while details remain independently accessible', async () => {
patchUiState({ sid: 'owner' })
- applyAgentSnapshot('owner', { subagents: [{ subagent_id: 'child', goal: 'Inspect ownership', status: 'running' }], delegations: [] })
+ applyAgentSnapshot('owner', {
+ subagents: [{ subagent_id: 'child', goal: 'Inspect ownership', status: 'running' }],
+ delegations: []
+ })
- const request = vi.fn(async (method: string) => method === 'subagent.tail'
- ? { available: true, text: 'CHILD_TOOL_OUTPUT', truncated: false }
- : {})
+ const request = vi.fn(async (method: string) =>
+ method === 'subagent.tail' ? { available: true, text: 'CHILD_TOOL_OUTPUT', truncated: false } : {}
+ )
const stdout = Object.assign(new PassThrough(), { columns: 80, rows: 20, isTTY: false })
const stdin = Object.assign(new PassThrough(), { isTTY: true, setRawMode: () => {}, ref: () => {}, unref: () => {} })
let output = ''
- stdout.on('data', chunk => { output += stripAnsi(chunk.toString()) })
-
- const view = renderSync( {}} t={DEFAULT_THEME} />, {
- stdout: stdout as unknown as NodeJS.WriteStream,
- stdin: stdin as unknown as NodeJS.ReadStream,
- stderr: new PassThrough() as unknown as NodeJS.WriteStream,
- patchConsole: false
+ stdout.on('data', chunk => {
+ output += stripAnsi(chunk.toString())
})
+ const view = renderSync(
+
+ {}} t={DEFAULT_THEME} />
+ ,
+ {
+ stdout: stdout as unknown as NodeJS.WriteStream,
+ stdin: stdin as unknown as NodeJS.ReadStream,
+ stderr: new PassThrough() as unknown as NodeJS.WriteStream,
+ patchConsole: false
+ }
+ )
+
try {
await vi.waitFor(() => expect(output).toContain('Inspect ownership'))
stdin.write('\r')
- await vi.waitFor(() => expect(request).toHaveBeenCalledWith('subagent.tail', { session_id: 'owner', subagent_id: 'child' }))
+ await vi.waitFor(() =>
+ expect(request).toHaveBeenCalledWith('subagent.tail', { session_id: 'owner', subagent_id: 'child' })
+ )
await vi.waitFor(() => expect(output).toContain('CHILD_TOOL_OUTPUT'))
output = ''
stdin.write('d')
diff --git a/ui-tui/src/components/agentsOverlay.tsx b/ui-tui/src/components/agentsOverlay.tsx
index e0467dc699..f7221bf450 100644
--- a/ui-tui/src/components/agentsOverlay.tsx
+++ b/ui-tui/src/components/agentsOverlay.tsx
@@ -772,9 +772,13 @@ export function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t }: Agent
return setMode('steer')
}
- if (ch === 't' && !key.ctrl && selected) {return setMode('tail')}
+ if (ch === 't' && !key.ctrl && selected) {
+ return setMode('tail')
+ }
- if (ch === 'd' && !key.ctrl && selected) {return setMode('detail')}
+ if (ch === 'd' && !key.ctrl && selected) {
+ return setMode('detail')
+ }
if (ch === 'q') {
return closeWithCleanup()
@@ -980,12 +984,19 @@ export function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t }: Agent
)}
- {replayMode ? 'Enter/d detail' : 'Enter/t tail · d detail'} · e steer · x stop · Esc back
- {flash ? {flash} : null}
+
+ {replayMode ? 'Enter/d detail' : 'Enter/t tail · d detail'} · e steer · x stop · Esc back
+
+ {flash ? (
+
+ {flash}
+
+ ) : null}
{mode === 'list' ? (
- ↑↓/jk move · g/G top/bottom · {replayMode ? 'Enter/→ detail' : 'Enter tail · d/→ detail'}{controlsHint} · s sort:{SORT_LABEL[sort]} · f filter:
+ ↑↓/jk move · g/G top/bottom · {replayMode ? 'Enter/→ detail' : 'Enter tail · d/→ detail'}
+ {controlsHint} · s sort:{SORT_LABEL[sort]} · f filter:
{FILTER_LABEL[filter]}
{history.length > 0 ? ` · [ / ] history ${historyIndex}/${history.length}` : ''}
{' · q close'}
diff --git a/ui-tui/src/components/agentsPanel.tsx b/ui-tui/src/components/agentsPanel.tsx
index 7bf5fa6b3c..00f4e07aaf 100644
--- a/ui-tui/src/components/agentsPanel.tsx
+++ b/ui-tui/src/components/agentsPanel.tsx
@@ -11,8 +11,17 @@ import { fmtDuration } from '../lib/subagentTree.js'
import { compactPreview } from '../lib/text.js'
import type { Theme } from '../theme.js'
-export function AgentsPanelView({ collapsed = false, cols, hidden, rows, running, t }: AgentRows & { collapsed?: boolean; cols: number; t: Theme }) {
- if (!running) {return null}
+export function AgentsPanelView({
+ collapsed = false,
+ cols,
+ hidden,
+ rows,
+ running,
+ t
+}: AgentRows & { collapsed?: boolean; cols: number; t: Theme }) {
+ if (!running) {
+ return null
+ }
const summary = `▸ ${running} live agents`
const hints = ' · Ctrl+T expand · F7 restore'
@@ -27,18 +36,21 @@ export function AgentsPanelView({ collapsed = false, cols, hidden, rows, running
width={cols}
>
- {collapsed ? summary + activity + hints : `▾ ${running} live agents${hidden ? ` · +${hidden} more` : ''} · Ctrl+T expand · F7 collapse`}
+ {collapsed
+ ? summary + activity + hints
+ : `▾ ${running} live agents${hidden ? ` · +${hidden} more` : ''} · Ctrl+T expand · F7 collapse`}
- {!collapsed && rows.map(row => (
-
-
- {statusGlyph(row.status, t).glyph}
- {compactPreview(row.goal, Math.max(8, cols - 18))}
- {row.elapsedSeconds == null ? '' : fmtDuration(row.elapsedSeconds)}
-
- {` ↳ ${compactPreview(row.detail, cols - 4)}`}
-
- ))}
+ {!collapsed &&
+ rows.map(row => (
+
+
+ {statusGlyph(row.status, t).glyph}
+ {compactPreview(row.goal, Math.max(8, cols - 18))}
+ {row.elapsedSeconds == null ? '' : fmtDuration(row.elapsedSeconds)}
+
+ {` ↳ ${compactPreview(row.detail, cols - 4)}`}
+
+ ))}
)
}
@@ -61,6 +73,11 @@ export function LiveAgentsPanel({ cols }: { cols: number }) {
}, [live])
return (
-
+
)
}
diff --git a/ui-tui/src/components/appLayout.tsx b/ui-tui/src/components/appLayout.tsx
index 79e3eb3faf..09fbb075a2 100644
--- a/ui-tui/src/components/appLayout.tsx
+++ b/ui-tui/src/components/appLayout.tsx
@@ -277,7 +277,9 @@ const ComposerPane = memo(function ComposerPane({
composer,
cursorSnapshotRef,
status
-}: Pick & { cursorSnapshotRef: MutableRefObject }) {
+}: Pick & {
+ cursorSnapshotRef: MutableRefObject
+}) {
const ui = useStore($uiState)
const isBlocked = useStore($isBlocked)
const sh = (composer.inputBuf[0] ?? composer.input).startsWith('!')
@@ -534,7 +536,9 @@ export const AppLayout = memo(function AppLayout({
const ui = useStore($uiState)
const cursorSnapshotRef = useRef(null)
- useEffect(() => { cursorSnapshotRef.current = null }, [ui.sid])
+ useEffect(() => {
+ cursorSnapshotRef.current = null
+ }, [ui.sid])
// Inline mode skips AlternateScreen so the host terminal's native
// scrollback captures rows scrolled off the top; composer + progress
@@ -577,7 +581,12 @@ export const AppLayout = memo(function AppLayout({
-
+
{SHOW_FPS && (
diff --git a/ui-tui/src/components/textInput.tsx b/ui-tui/src/components/textInput.tsx
index 29264205a1..a24100169c 100644
--- a/ui-tui/src/components/textInput.tsx
+++ b/ui-tui/src/components/textInput.tsx
@@ -790,7 +790,9 @@ export function TextInput({
color,
focus = true
}: TextInputProps) {
- const [cur, setCur] = useState(() => cursorSnapshotRef?.current?.value === value ? cursorSnapshotRef.current.cursor : value.length)
+ const [cur, setCur] = useState(() =>
+ cursorSnapshotRef?.current?.value === value ? cursorSnapshotRef.current.cursor : value.length
+ )
const [sel, setSel] = useState(null)
const fwdDel = useFwdDelete(focus)
const termFocus = useTerminalFocus()
@@ -939,9 +941,14 @@ export function TextInput({
// The composer unmounts while full-screen monitors own input. Keep its
// insertion point with the shell, not with transient steer/secret inputs.
- useEffect(() => () => {
- if (cursorSnapshotRef) {cursorSnapshotRef.current = { cursor: curRef.current, value: vRef.current }}
- }, [cursorSnapshotRef])
+ useEffect(
+ () => () => {
+ if (cursorSnapshotRef) {
+ cursorSnapshotRef.current = { cursor: curRef.current, value: vRef.current }
+ }
+ },
+ [cursorSnapshotRef]
+ )
useEffect(() => {
if (!focus) {
diff --git a/website/docs/user-guide/cli.md b/website/docs/user-guide/cli.md
index df128820e0..61ad61687e 100644
--- a/website/docs/user-guide/cli.md
+++ b/website/docs/user-guide/cli.md
@@ -187,6 +187,7 @@ When resuming a previous session (`hermes -c` or `hermes --resume `), a "Pre
| `Ctrl+S` | **Stash the prompt.** Parks the current draft and clears the composer so you can send something else first. Press `Ctrl+S` again on an empty composer to bring the draft back (cursor at the end, attached images restored). Repeated presses build a stack rather than overwriting, so an earlier draft is never silently lost — with two or more stashed, `Ctrl+S` opens a browse panel (`↑`/`↓` to navigate, `Enter` to restore, `D` to discard, `Esc` or `Ctrl+S` to close). A `📌 N` badge in the status bar shows how many drafts are parked. Multi-line drafts round-trip exactly, including blank lines. The stash lives in memory for the session only — nothing is written to disk, since drafts often contain secrets. |
| `Ctrl+C` | Interrupt agent (double-press within 2s to force exit) |
| `F6` | Open the full-screen live subagent monitor without losing the composer draft. The live dock appears automatically above the status bar; arrows select a worker, `Enter` shows its recent log, `s` steers, and `x` requests stop with confirmation. See [Monitoring subagents](/user-guide/features/delegation#monitoring-running-subagents-agents). |
+| `F7` | Toggle the live subagent dock between its multi-row preview and a single summary line without moving composer focus. |
| `Ctrl+D` | Exit |
| `Ctrl+Z` | Suspend Hermes to background (Unix only). Run `fg` in the shell to resume. |
| `Tab` | Accept auto-suggestion (ghost text) or autocomplete slash commands |
diff --git a/website/docs/user-guide/features/delegation.md b/website/docs/user-guide/features/delegation.md
index ef97852ffd..3e0e19eb53 100644
--- a/website/docs/user-guide/features/delegation.md
+++ b/website/docs/user-guide/features/delegation.md
@@ -392,11 +392,13 @@ The classic CLI, TUI, and Desktop automatically show live subagents above the co
| Surface | Expand and inspect | Control a selected worker |
|---|---|---|
| Classic CLI | **F6** opens the full-screen live roster; arrows select, **Enter** opens the transcript tail, **PgUp/PgDn** scroll | **s** opens a separate steering input; **x**, then **y** requests stop |
-| TUI | **Ctrl+T** or `/agents` opens the full-height tree; **Enter** opens detail; **t** opens the live transcript tail | **e** opens steering; **x** stops the selected worker; **X** stops its subtree |
+| TUI | **Ctrl+T** or `/agents` opens the full-height tree; **Enter/t** opens the live transcript tail; **d** opens rich detail (archived/replay Enter still opens detail) | **e** opens steering; **x** stops the selected worker; **X** stops its subtree |
| Desktop | Expand **Subagents** above the composer, then select a worker to inspect its activity and details | **Steer** queues guidance; **Stop** requests interruption for that worker |
Closing the terminal monitor returns to your existing composer draft. Steering uses its own input and acknowledges **queued**, not delivery: the child consumes guidance at a checkpoint. Stop does not interrupt unrelated siblings.
+Press **F7** in the Classic CLI or TUI composer to toggle the dock between its multi-row preview and a single shaded summary line. The summary retains the live count and expand/restore hints, adding activity when space permits. Typing and sending remain available; opening and closing the monitor preserves your draft and insertion point. This is a local presentation choice, not a saved config change.
+
The live transcript tail is a bounded recent excerpt, not an unlimited conversation browser. A child leaving the live registry leaves the dock; completion messages and the TUI/Desktop history views remain the place to review finished work. Latest activity is an observation, not a percentage-complete estimate.
The classic CLI's `/agents` and `/tasks` commands still print a text summary; **F6** is the immediate interactive monitor, including while the parent is busy. See [TUI — Slash commands](/user-guide/tui#slash-commands).
diff --git a/website/docs/user-guide/tui.md b/website/docs/user-guide/tui.md
index e601b3a085..87bed9a239 100644
--- a/website/docs/user-guide/tui.md
+++ b/website/docs/user-guide/tui.md
@@ -102,7 +102,8 @@ The directory must contain `dist/entry.js`.
Keybindings match the [Classic CLI](cli.md#keybindings) exactly. The only behavioral differences:
-- **`Ctrl+T`** expands the automatic live-subagent dock into the full-height `/agents` roster. Select a worker to inspect details, press **`t`** for its recent transcript, **`e`** to steer, or **`x`** to stop it. The dock fits its row count to terminal height and preserves your composer draft. See [Monitoring subagents](/user-guide/features/delegation#monitoring-running-subagents-agents).
+- **`Ctrl+T`** expands the automatic live-subagent dock into the full-height `/agents` roster. Select a worker and press **Enter** (or **`t`**) for its live transcript, **`d`** for rich details, **`e`** to steer, or **`x`** to stop it. The dock fits its row count to terminal height and preserves your composer draft. See [Monitoring subagents](/user-guide/features/delegation#monitoring-running-subagents-agents).
+- **`F7`** toggles the live dock between its default preview and one summary line. This does not open the monitor or move composer focus; the choice lasts for this TUI process without changing config.
- **Mouse drag** highlights text with a uniform selection background.
- **`Cmd+V` / `Ctrl+V`** first tries normal text paste, then falls back to OSC52/native clipboard reads, and finally image attach when the clipboard or pasted payload resolves to an image.
- **`/terminal-setup`** installs local VS Code / Cursor / Windsurf terminal bindings for better `Cmd+Enter` and undo/redo parity on macOS.
diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/cli.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/cli.md
index 6ff86a5b24..6ff9cb00a0 100644
--- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/cli.md
+++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/cli.md
@@ -103,6 +103,7 @@ hermes -w -z "Fix issue #123" # 在 worktree 中以单次查询模式运行
| `Ctrl+X Ctrl+E` | 外部编辑器的 Emacs 风格备用绑定(与 `Ctrl+G` 行为相同)。 |
| `Ctrl+C` | 中断 agent(2 秒内双击强制退出) |
| `F6` | 打开全屏实时子智能体监视器,保留输入草稿。方向键选择,`Enter` 查看近期日志,`s` 引导,`x` 请求停止并确认。 |
+| `F7` | 将实时子智能体栏切换为单行摘要或恢复多行预览,不改变输入焦点。 |
| `Ctrl+D` | 退出 |
| `Ctrl+Z` | 将 Hermes 挂起到后台(仅 Unix)。在 shell 中运行 `fg` 恢复。 |
| `Tab` | 接受自动建议(ghost text)或自动补全斜杠命令 |
diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/delegation.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/delegation.md
index 0971ef374a..ee35ae5e81 100644
--- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/delegation.md
+++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/delegation.md
@@ -257,11 +257,13 @@ TUI 提供 `/agents` 浮层(别名 `/tasks`),将递归 `delegate_task` 扇
经典 CLI、TUI 和 Desktop 会在输入框上方自动显示正在运行的子智能体,包括总数、任务名称、已运行时间和最近活动。终端根据屏幕高度限制可见行数,并显示隐藏数量;Desktop 最多预览三个工作者。
- **经典 CLI:F6** 打开全屏实时列表;方向键选择,**Enter** 查看近期日志,**PgUp/PgDn** 滚动,**s** 输入引导,**x** 后按 **y** 确认停止。关闭后保留原有输入草稿。
-- **TUI:Ctrl+T** 或 `/agents` 打开完整树状列表;**t** 查看实时日志,**e** 输入引导,**x** 停止选中的工作者,**X** 停止其子树。
+- **TUI:Ctrl+T** 或 `/agents` 打开完整树状列表;**Enter/t** 查看实时日志,**d** 查看详情(历史回放中 Enter 仍打开详情),**e** 输入引导,**x** 停止选中的工作者,**X** 停止其子树。
- **Desktop:** 展开输入框上方的 **Subagents**,选择工作者查看详情并使用 **Steer** / **Stop**。
引导的“已排队”确认不代表子智能体已经读取;它会在检查点接收。日志预览只包含有大小限制的近期内容。工作者结束后离开实时列表,完成消息和已有历史视图仍可用于回顾。
+在经典 CLI 和 TUI 中按 **F7**,可将实时栏折叠为单行摘要,再按一次恢复多行预览。单行保留运行数量和展开/恢复提示,空间允许时显示活动。输入和发送不受影响;关闭监视器后保留草稿及光标位置。此选项不写入配置。
+
经典 CLI 的 `/agents` 和 `/tasks` 仍打印文本摘要;父智能体忙碌时可直接按 **F6** 打开交互式监视器。参见 [TUI — 斜杠命令](/user-guide/tui#slash-commands)。
## 深度限制与嵌套编排 {#depth-limit-and-nested-orchestration}
diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/tui.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/tui.md
index c79b3d95d0..3c0becd661 100644
--- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/tui.md
+++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/tui.md
@@ -85,7 +85,8 @@ hermes --tui
快捷键与 [Classic CLI](cli.md#keybindings) 完全一致。仅有以下行为差异:
-- **`Ctrl+T`** — 将输入框上方的实时子智能体栏展开为完整 `/agents` 列表;**`t`** 查看近期日志,**`e`** 引导,**`x`** 停止选中的工作者。可见行数随终端高度调整,关闭后保留输入草稿。
+- **`Ctrl+T`** — 将输入框上方的实时子智能体栏展开为完整 `/agents` 列表;**Enter/t** 查看实时日志,**`d`** 查看详细信息,**`e`** 引导,**`x`** 停止选中的工作者。可见行数随终端高度调整,关闭后保留输入草稿。
+- **`F7`** — 在多行预览和单行摘要之间切换,保留输入焦点,不写入配置。
- **鼠标拖拽** — 以统一选区背景色高亮文本。
- **`Cmd+V` / `Ctrl+V`** — 优先尝试普通文本粘贴,然后回退到 OSC52/原生剪贴板读取,最后在剪贴板或粘贴内容解析为图片时进行图片附件操作。
- **`/terminal-setup`** — 安装本地 VS Code / Cursor / Windsurf 终端绑定,以在 macOS 上获得更好的 `Cmd+Enter` 和撤销/重做一致性。