@songtonyli/dsh-cli 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +1 -1
  2. package/bin/dsh.mjs +1 -1
  3. package/node_modules/@deepseek-ai/dsh-api-authorization-controller/README.i18n.yaml +2 -2
  4. package/node_modules/@deepseek-ai/dsh-api-authorization-controller/README.md +1 -1
  5. package/node_modules/@deepseek-ai/dsh-api-authorization-controller/README.zh.md +1 -1
  6. package/node_modules/@deepseek-ai/dsh-api-authorization-controller/lib/index.js +1 -0
  7. package/node_modules/@deepseek-ai/dsh-api-authorization-controller/lib/typert.host.js +2 -1
  8. package/node_modules/@deepseek-ai/dsh-api-authorization-controller/lib/typert.remote-client.js +1 -0
  9. package/node_modules/@deepseek-ai/dsh-api-authorization-controller/lib/types/index.js +1 -0
  10. package/node_modules/@deepseek-ai/dsh-api-remotes/lib/client.js +1 -0
  11. package/node_modules/@deepseek-ai/dsh-authorization/README.i18n.yaml +2 -2
  12. package/node_modules/@deepseek-ai/dsh-authorization/README.md +1 -1
  13. package/node_modules/@deepseek-ai/dsh-authorization/README.zh.md +1 -1
  14. package/node_modules/@deepseek-ai/dsh-client-ui-settings-signin/lib/client.js +1 -0
  15. package/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js +1 -1
  16. package/node_modules/@deepseek-ai/dsh-llm-pi-ai/README.i18n.yaml +2 -2
  17. package/node_modules/@deepseek-ai/dsh-llm-pi-ai/README.md +1 -1
  18. package/node_modules/@deepseek-ai/dsh-llm-pi-ai/README.zh.md +1 -1
  19. package/node_modules/@deepseek-ai/dsh-llm-pi-ai/lib/index.js +3 -1
  20. package/node_modules/@deepseek-ai/dsh-native-command/README.i18n.yaml +2 -2
  21. package/node_modules/@deepseek-ai/dsh-native-command/README.md +7 -2
  22. package/node_modules/@deepseek-ai/dsh-native-command/README.zh.md +7 -2
  23. package/node_modules/@deepseek-ai/dsh-native-command/lib/index.js +93 -2
  24. package/node_modules/@deepseek-ai/dsh-native-command/package.json +4 -0
  25. package/node_modules/@deepseek-ai/dsh-tool-cordis/lib/index.js +2 -2
  26. package/node_modules/@deepseek-ai/dsh-tui-app/README.i18n.yaml +2 -2
  27. package/node_modules/@deepseek-ai/dsh-tui-app/README.md +47 -12
  28. package/node_modules/@deepseek-ai/dsh-tui-app/README.zh.md +47 -12
  29. package/node_modules/@deepseek-ai/dsh-tui-app/cordis.patch.yml +3 -2
  30. package/node_modules/@deepseek-ai/dsh-tui-app/lib/index.js +3087 -1012
  31. package/node_modules/@deepseek-ai/dsh-tui-app/lib/startup.js +3 -2
  32. package/node_modules/@deepseek-ai/dsh-tui-app/package.json +2 -0
  33. package/node_modules/@deepseek-ai/dsh-web-app/lib/index.js +2 -77
  34. package/node_modules/@deepseek-ai/dsh-web-app/package.json +1 -2
  35. package/node_modules/@types/node/README.md +1 -1
  36. package/node_modules/@types/node/package.json +2 -2
  37. package/package.json +2 -2
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-tui-app` is the terminal surface of dsh: `dsh tui` starts a multi-turn session in the terminal you are already in, with no browser and no server. Replies stream as you watch, tool calls become foldable cards, approvals and `ask_user_question` questions appear above the input, `@` completes paths and sessions, `/attach` adds images and files, and `/`-commands share the Web registry. Sessions persist: `/sessions`, `/new`, and `/fork` switch between them, `/export` writes the browser's ZIP, and `--resume` continues one later. It runs the same model, tools, and safety defaults as `dsh web`, one session at a time.
12
+ `dsh-tui-app` is the terminal surface of dsh: `dsh tui` starts a multi-turn session in the terminal you are already in, with no browser-hosted application and no server. Replies stream as you watch, tool calls become foldable cards, approvals and `ask_user_question` questions appear above the input, `@` completes paths and sessions, `/attach` adds images and files, and `/`-commands share the Web registry. Sessions persist: `/sessions`, `/new`, and `/fork` switch between them, `/export` writes the browser's ZIP, and `--resume` continues one later. It runs the same model, tools, and safety defaults as `dsh web`, one session at a time.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -33,13 +33,16 @@ Start a session, type, and read the answer in place. `dsh tui` is an alias of `d
33
33
  dsh tui # new session, wait for input
34
34
  dsh tui "explain this repository" # new session with a first prompt
35
35
  dsh tui --resume <session-id> # continue an earlier session
36
+ dsh tui --no-open # print sign-in URLs without opening a browser
36
37
  ```
37
38
 
38
39
  On quit the app prints `dsh: session <id> saved; resume with: dsh --profile tui --resume <id>` on stderr for the session bound at that moment. A resumed session redraws its persisted history before accepting input; inside the terminal, `/sessions` opens a picker over every persisted root session, `/new` starts a fresh one, and `/fork` copies the current session up to its last completed turn into a new one, the same cut the browser's fork takes. Switching releases the previous Agent and redraws the transcript of the next.
39
40
 
40
41
  ### The screen
41
42
 
42
- The header names the session by its title once one is generated or set, with the id beside it. The transcript grows in the terminal's own scrollback: your prompts start with `›` (attachments listed under them), assistant reasoning is dim above the Markdown reply, and each tool call is a card with a status glyph, the tool name, the presenter headline, and a body folded to `toolPreviewLines` rows. Below the transcript sit a spinner while the agent works, any open prompt, the editor, and a two-line footer with the model and reasoning effort, the permission preset, cumulative token usage, the context window percentage, todo and goal and plan-mode markers from the projection seam, the workspace, the count of pending attachments, and the key hints. Compaction and model-request retries appear as notices, the same facts the browser's markers carry.
43
+ The header names the session by its title once one is generated or set, with the id beside it. The transcript grows in the terminal's own scrollback: your prompts start with `›` (attachments listed under them), assistant reasoning is dim above the Markdown reply, and each tool call is a card with a status glyph, the tool name, the presenter headline, and a body folded to `toolPreviewLines` rows. Streamed reply text fades in: the newest words arrive near the terminal's background color and brighten to the normal foreground over `streamFadeSteps` levels of `streamFadeStepMs` each, so a soft edge trails the stream head, and text that has settled is never dimmed again. Below the transcript sit a spinner while the agent works, any open prompt, the editor, the subagent panel, and the footer: a status bar of segments over a line of key hints. The editor's caret is the terminal's own blinking bar: the app asks for that shape at start, gives your default back on quit, and draws no caret at all while the status bar or the panel holds the keyboard. Each segment appears only when its fact exists — the model and reasoning effort, the permission preset, the running turn's elapsed time, cumulative token usage, the context window percentage, todo and goal and plan-mode markers from the projection seam, the workspace path (shortened with `~` and `…/` when long), and the count of pending attachments and `Shift+Up` moves focus into the bar to read a segment's details. Compaction and model-request retries appear as notices, the same facts the browser's markers carry.
44
+
45
+ The subagent panel is drawn while a subagent session under the bound one is resident, or the listing carries a candidate it could not read. Its heading counts what it lists, and each row gives the child's depth indent, its label or id, its mode (`one-shot` or `continuable`), `resident`, whether its agent is `running` or `idle`, its elapsed time — the open turn's, else the total its settled turns took — and its token usage, as far as this process's own view of that child and the composed projections carry them. Six rows are drawn at most, with `+<n> more · /subagents lists them all` under them; a candidate the listing could not interpret draws as `unreadable: <reason>` and opens nothing, and a failed listing keeps the rows the last good one produced with `listing failed: <reason>` beneath. The panel disappears with its last row.
43
46
 
44
47
  ### Keys and commands
45
48
 
@@ -48,19 +51,27 @@ The header names the session by its title once one is generated or set, with the
48
51
  | `Enter` | Send the editor text; while a turn runs it is queued for the next turn |
49
52
  | `Ctrl+S` | While a turn runs, steer the editor text into the running turn's next step |
50
53
  | `Shift+Enter` | Insert a newline |
51
- | `Shift+Tab` | Cycle the current model's reasoning effort for the next request |
54
+ | `Shift+Tab` | While the editor has focus, cycle the current model's reasoning effort for the next request |
52
55
  | `Up` / `Down` | Recall earlier prompts |
56
+ | `Shift+Up` | Move focus on through the docked regions: editor → status bar → subagent panel → editor |
57
+ | `Shift+Down` | Move focus back through that cycle; in the editor with no panel drawn it stays the editor's own key |
53
58
  | `Esc` | Stop the running turn; queued messages stay queued |
54
59
  | `Ctrl+O` | Expand or collapse every tool card |
55
60
  | `Ctrl+C` | Clear the editor; a second press within 600 ms quits |
56
61
  | `Ctrl+D` | Quit when the editor is empty |
57
62
 
63
+ While the status bar has focus, `Left` / `Right` and `Tab` / `Shift+Tab` move between segments and wrap at both ends, `Enter` opens the selected segment while the bar keeps focus, `Shift+Up` moves on to the subagent panel while it is drawn, and `Esc` or `Shift+Down` returns focus to the editor. No other key reaches the editor while the bar has focus; `Ctrl+C` and `Ctrl+D` keep their usual meaning and return focus to the editor.
64
+
65
+ While the subagent panel has focus, `Up` / `Down` move the selection and wrap at both ends, `Enter` opens that child's session details as a read-only page and comes back to the panel on the same row, `Shift+Down` moves on to the status bar, and `Esc` or `Shift+Up` returns focus to the editor. Every other key is consumed there as well, apart from `Ctrl+C` and `Ctrl+D`; the panel also hands the keyboard back to the editor when its last row leaves.
66
+
67
+ Every segment except `todo` prints its details into the transcript, stating its current facts and naming what changes them: the model segment names `/model`, the effort segment `Shift+Tab`, and the permission segment its preset, the `turn` segment — drawn as `turn <elapsed>` between the permission and usage segments, and only while a turn runs — gives the turn number, its start time, its elapsed time, and the queued-message counts, while the usage, context, goal, and plan segments print the matching sections of the `/status` report, the workspace segment the full path, and the attachments segment the pending attachments. The `todo` segment opens the agent's todo list instead, the same list `/todos` opens.
68
+
58
69
  Typing `/` at the start of the editor completes the terminal's own commands and the shared registry's; `@` anywhere completes references.
59
70
 
60
71
  | Command | Effect |
61
72
  |---|---|
62
73
  | `/help` | List commands and keys |
63
- | `/model` | Pick the model, then its reasoning effort when the model declares more than one, for the next request; `/model <provider>/<model>` selects directly and `/model save` stores the current selection as the default |
74
+ | `/model` | Pick the model (type to filter the rows), then its reasoning effort when the model declares more than one, for the next request; `/model <provider>/<model>` selects directly and `/model save` stores the current selection as the default |
64
75
  | `/sessions` | Pick another persisted session and switch to it |
65
76
  | `/new` | Start a new session |
66
77
  | `/fork [turn]` | Fork this session at its last completed turn, or after turn `turn` |
@@ -72,9 +83,10 @@ Typing `/` at the start of the editor completes the terminal's own commands and
72
83
  | `/login` | Sign in with a provider subscription (hides key-collecting logins); `/login <key>` skips the picker |
73
84
  | `/export [dir]` | Write this session's log ZIP (sub-sessions and attachments included) into `dir`, default the workspace |
74
85
  | `/status` | Context window usage and breakdown, token totals with cache hit, session stats, todos, goal, plan mode, and permission |
86
+ | `/todos` | Browse the agent's todo list; `Enter` opens one item in full with its status, position, and turns |
75
87
  | `/outline` | The turns of this session with their prompt and reply previews |
76
88
  | `/deliverables` | The files the agent presented, grouped by turn |
77
- | `/subagents` | The subagent sessions under this session, with activity and ids |
89
+ | `/subagents` | Browse the subagent sessions under this session; `Enter` opens one session's details |
78
90
  | `/settings [ns [path value]]` | List namespaces, show one, or set one field; `/settings reset <ns>` restores defaults |
79
91
  | `/plugins` | The composed plugins with enablement and lifecycle phase |
80
92
  | `/tools` | Expand or collapse every tool card, like `Ctrl+O` |
@@ -82,6 +94,14 @@ Typing `/` at the start of the editor completes the terminal's own commands and
82
94
 
83
95
  Every other `/name` line goes to the shared command registry, so `/compact`, `/permission`, `/goal`, and plugin commands work as they do in the browser.
84
96
 
97
+ Every picker the terminal opens — the model and reasoning-effort lists of `/model`, `/sessions`, the `/subagents` and `/todos` lists, and the rows `/signin` and `/login` raise — filters its rows as you type: the query matches each row's label and description together, its whitespace- and slash-separated tokens must all match, and the rows are ordered best match first, so `dsk chat` and `deepseek/chat` both find `deepseek/deepseek-chat` and `gpt5` finds `gpt-5`. `Backspace` drops the last character, `Ctrl+U` clears the query, `Esc` clears a non-empty query and cancels the picker once the query is empty, `Up` / `Down` move within the matches, and `Enter` picks the highlighted row. The dim line above the rows reads `type to filter · Enter selects · Esc cancels` while the query is empty and `filter: <query> · <kept>/<total>` afterwards; a query nothing matches draws `no row matches "<query>"` in place of the rows, and the `✓` on the row in force — the row a picker opens on — shows only while the query is empty.
98
+
99
+ `/subagents`, `/todos`, and the `todo` status-bar segment share one list-then-details interaction: the picker lists the entries, `Enter` opens the highlighted entry as a read-only page, `Up` / `Down` and `PageUp` / `PageDown` scroll that page, and `Enter`, `Esc`, or `Left` returns to the list on the entry just read, so walking several entries costs no retyped command; `Esc` at the list returns to the editor. An entry whose details cannot be read says so on its page instead of closing the list.
100
+
101
+ ### Subscription sign-in
102
+
103
+ `/login` stores a subscription credential but does not activate a dormant model route. Configure the catalog route first, then use its full credential key; for example, run `/settings llm-pi-ai providers.openai-codex {}` and then `/login llm-pi-ai/openai-codex`. A marked authorization page opens in the local default browser while its URL remains in the transcript as a fallback. SSH launches, hosts without a desktop, `--no-open`, and opener failures leave the manual URL and device-code paths available instead.
104
+
85
105
  ### Prompts from the agent
86
106
 
87
107
  An approval request draws `Allow <tool>?` with the asker's reason, the logged call the request names (the same rows as its tool card, so a shell command reads before it runs), and two rows, allow once or reject; `Esc` rejects and `Ctrl+C` cancels the request. An `ask_user_question` question renders its `detail` as Markdown above its options plus a free-text row; multi-select toggles rows with `Space` and confirms through `Done`. A plan review (the `plan-review` intent `exit_plan_mode` sets) draws the plan as Markdown with Approve, Decline, and Discuss rows, where Discuss returns the request to the composer as the browser's card does. Prompts queue and show one at a time, and an aborted request withdraws its prompt.
@@ -97,8 +117,13 @@ An approval request draws `Allow <tool>?` with the asker's reason, the logged ca
97
117
  | `prompt` | none | A first prompt submitted when the terminal is up |
98
118
  | `resume` | none | A persisted session id to continue instead of starting a new one |
99
119
  | `toolPreviewLines` | `8` | Collapsed tool-card body rows before `Ctrl+O` expands them |
120
+ | `liveRefreshMs` | `1000` | Period of the redraw that advances the `turn` segment and the panel's elapsed values and re-reads a stale subagent listing |
121
+ | `streamFadeSteps` | `5` | Brightness levels streamed assistant text climbs before it draws in the normal foreground |
122
+ | `streamFadeStepMs` | `40` | How long one brightness level lasts, which is also the repaint period of the fading text |
123
+ | `reducedMotion` | `false` | Draw streamed text at the normal foreground, with no fade and no repeating repaint |
124
+ | `openBrowser` | `true` | Hand marked authorization pages to the local default browser |
100
125
 
101
- `prompt` and `resume` come from the command line through the startup provider; the generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tui-app) is the exhaustive source for every accepted field.
126
+ `prompt`, `resume`, and `openBrowser` come from the command line through the startup provider; the generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tui-app) is the exhaustive source for every accepted field.
102
127
 
103
128
  -----
104
129
 
@@ -112,11 +137,11 @@ The runner is a direct driver over the core API carrier, like `dsh-headless`, th
112
137
 
113
138
  ### Run flow
114
139
 
115
- The runner awaits the complete application (`ctx.get('loader')?.await()`) and builds a session host over the core registry with three operations: `create` makes one fresh persisted Agent with the shared [`agentDefaultModel`](../../core/agent-default-model/README.md) selection, `resume` reads the persisted log in pages through a read handle of `ctx.sessionPersistence` and resumes the Agent through the registry, and `fork` observes the source through `ctx.sessionQuery`, cuts after the chosen (by default the last) `turn/end` up to the next `turn/start`, and creates a seeded Agent with `parentSession` and `isSeeded` metadata. Every operation installs a `ModelSelectionRef` in the Agent's scoped setup so `/model` changes the next request. The terminal application starts on the session `--resume` or a fresh `create` yields, subscribes to `session/event`, `agent/assistant-stream`, and `agent/status`, answers the `approval/request` and `user-questions/request` waterfalls for the bound Agent only, and switches sessions by binding the next one and disposing the previous handle; while the host opens the next session the editor refuses input, and a quit during that wait releases the session that arrives afterwards. Quitting cancels any running turn, waits for quiescence, flushes the bound Session, disposes its handle, and requests exit 0; a driver failure writes `dsh: <message>` to stderr and requests exit 1. Shift+Tab cycles the bound model's adapter-owned reasoning efforts, wrapping through the provider default, and `/login` starts `authorization.begin` with only subscription methods (every method except a key-collecting `api-key` login).
140
+ The runner awaits the complete application (`ctx.get('loader')?.await()`) and builds a session host over the core registry with three operations: `create` makes one fresh persisted Agent with the shared [`agentDefaultModel`](../../core/agent-default-model/README.md) selection, `resume` reads the persisted log in pages through a read handle of `ctx.sessionPersistence` and resumes the Agent through the registry, and `fork` observes the source through `ctx.sessionQuery`, cuts after the chosen (by default the last) `turn/end` up to the next `turn/start`, and creates a seeded Agent with `parentSession` and `isSeeded` metadata. Every operation installs a `ModelSelectionRef` in the Agent's scoped setup so `/model` changes the next request. The terminal application starts on the session `--resume` or a fresh `create` yields, subscribes to `session/event`, `agent/assistant-stream`, and `agent/status`, answers the `approval/request` and `user-questions/request` waterfalls for the bound Agent only, and switches sessions by binding the next one and disposing the previous handle; while the host opens the next session the editor refuses input, and a quit during that wait releases the session that arrives afterwards. Quitting cancels any running turn, waits for quiescence, flushes the bound Session, disposes its handle, and requests exit 0; a driver failure writes `dsh: <message>` to stderr and requests exit 1. Shift+Tab cycles the bound model's adapter-owned reasoning efforts, wrapping through the provider default, and `/login` starts `authorization.begin` with only subscription methods (every method except a key-collecting `api-key` login). A notice the flow marks with `openInBrowser` is handed to the default browser through `dsh-native-command`'s credential-scrubbed helper while the URL stays printed; the handoff is suppressed when `openBrowser` is false, the launch came through SSH, or the host has no desktop, and an opener failure becomes a notice beside the URL rather than a sign-in failure.
116
141
 
117
142
  ### Rendering model
118
143
 
119
- Durable facts come from the session log: `user/message` (own submissions are drawn once and their echo skipped by message id; a plugin notice is one dim row and other injected context is not drawn), `assistant/message` (which replaces the streamed block with the committed text and folds usage into the footer), `tool/call` and `tool/result` (drawn through the tool's `presentCall` and `presentResult` views when it declares them, with a raw-argument and raw-result fallback), `turn/end` notices, `session/title` (header), and `permission/preset` (footer). Live incrementality comes from `agent/assistant-stream` text and reasoning deltas. Session facts outside the log come from the same services the browser reads: `sessionTitle`, `permissionPresets`, `sessionQuery` for the picker and `/deliverables`, `sessionProjections` for the footer, `/status`, and `/outline`, `fileReferences` and `sessionReferenceResolver` for `@` completion, `attachments`, `skills`, `authorization`, `settings`, `subagents`, and the Loader's entries for `/plugins`. Modal prompts are process-local presentation and are never logged.
144
+ Durable facts come from the session log: `user/message` (own submissions are drawn once and their echo skipped by message id; a plugin notice is one dim row and other injected context is not drawn), `assistant/message` (which replaces the streamed block with the committed text and folds usage into the footer), `tool/call` and `tool/result` (drawn through the tool's `presentCall` and `presentResult` views when it declares them, with a raw-argument and raw-result fallback), `turn/end` notices, `session/title` (header), `permission/preset` (footer), and `todo/write` under the enclosing `turn/start` (the turns one todo item's page reports; the list carries no per-item identity, so a reworded item counts as a new one). Live incrementality comes from `agent/assistant-stream` text and reasoning deltas. Session facts outside the log come from the same services the browser reads: `sessionTitle`, `permissionPresets`, `sessionQuery` for the picker, `/deliverables`, and subagent details, `sessionProjections` for the footer, `/status`, `/todos`, and `/outline`, `fileReferences` and `sessionReferenceResolver` for `@` completion, `attachments`, `skills`, `authorization`, `settings`, `subagents`, and the Loader's entries for `/plugins`. Modal prompts are process-local presentation and are never logged.
120
145
 
121
146
  ### Patch surface over base
122
147
 
@@ -127,24 +152,29 @@ The patch rides over `dsh-base`: it sets the coding persona prefix and cwd suffi
127
152
  | File | Role |
128
153
  |---|---|
129
154
  | [`src/index.ts`](src/index.ts) | The `tui-app` plugin: the session host (create, resume, fork), history read, quit flow, exit mapping |
130
- | [`src/startup.ts`](src/startup.ts) | The `tui-app-startup` provider: prompt positional, `--resume`, and `--help` |
155
+ | [`src/startup.ts`](src/startup.ts) | The `tui-app-startup` provider: prompt positional, `--resume`, `--no-open`, and `--help` |
131
156
  | [`src/app.ts`](src/app.ts) | The terminal application: layout, keys, commands, session binding, seams, log and stream folding |
132
157
  | [`src/sessions.ts`](src/sessions.ts) | The `/sessions` list over the query engine and its picker rows |
133
158
  | [`src/attach.ts`](src/attach.ts) | `/attach`: local files into image or file blocks through the attachment store |
134
159
  | [`src/export.ts`](src/export.ts) | `/export`: the session-log ZIP written through the export package's archive helpers |
135
160
  | [`src/blocks.ts`](src/blocks.ts) | Transcript components: user prompt, assistant reply, tool card, notice |
136
- | [`src/prompts.ts`](src/prompts.ts) | Approval, question, and picker prompts plus the modal queue |
161
+ | [`src/fade.ts`](src/fade.ts) | The streamed-text fade: the tail tracker, the background-to-foreground ramp, and the recolor of rendered lines |
162
+ | [`src/prompts.ts`](src/prompts.ts) | Approval, question, picker, and read-only detail prompts plus the modal queue |
137
163
  | [`src/transcript.ts`](src/transcript.ts) | Pure text folding of presentation views, usage, and turn-end reasons |
138
164
  | [`src/diff.ts`](src/diff.ts) | Line diff and hunk selection for diff cards |
139
165
  | [`src/style.ts`](src/style.ts) | The palette and the derived pi-tui themes |
140
166
  | [`src/completion.ts`](src/completion.ts) | Slash-command and `@`-reference completion for the editor |
141
- | [`src/status.ts`](src/status.ts) | Footer parts and the `/status` report over the projection seam; compaction and retry notices |
167
+ | [`src/editor.ts`](src/editor.ts) | The prompt editor without pi-tui's drawn block cursor, and the DECSCUSR sequences for the terminal's own caret |
168
+ | [`src/status.ts`](src/status.ts) | Projection-seam facts and the sections the `/status` report and the segment details share; compaction and retry notices |
169
+ | [`src/footer.ts`](src/footer.ts) | The status bar: the ordered segments, each segment's detail rows, and the footer's two rendered lines |
170
+ | [`src/subagent-panel.ts`](src/subagent-panel.ts) | The live subagent panel: one descendant listing plus sampled live facts become its rows, and the rows its text |
142
171
  | [`src/catalog.ts`](src/catalog.ts) | Rows for `/settings`, `/plugins`, `/subagents`, `/deliverables`, and `/outline` |
172
+ | [`src/todos.ts`](src/todos.ts) | The todo list: the status glyphs, the picker rows, and one item's detail rows |
143
173
  | [`cordis.patch.yml`](cordis.patch.yml) | The terminal patch over `dsh-base` |
144
174
  | — | No runtime invariant companion is published; the app registers listeners on one Agent and holds no mutable relation another observer could contradict. |
145
175
  | [`tests/app.spec.ts`](tests/app.spec.ts) | Rendering, keys, commands, and both seams over a fake terminal |
146
176
  | [`tests/commands.spec.ts`](tests/commands.spec.ts) | Session, attachment, queue, skill, sign-in, `/login`, Shift+Tab effort cycling, export, reference, and effort commands over scripted services |
147
- | [`tests/panels.spec.ts`](tests/panels.spec.ts) | Status footer and report, catalog commands, command hints, and the approval detail |
177
+ | [`tests/panels.spec.ts`](tests/panels.spec.ts) | Status footer and report, the navigable subagent and todo lists, catalog commands, command hints, and the approval detail |
148
178
  | [`tests/index.spec.ts`](tests/index.spec.ts) | Creation, resume paging, fork cut, session switching, quit flow, and failure reporting |
149
179
  | [`tests/startup.spec.ts`](tests/startup.spec.ts) | Command-line parsing over a real Loader tree |
150
180
  | [`../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts`](../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts) | The shipped profile through the real launcher with a keyless mock model |
@@ -195,6 +225,11 @@ These limits describe the terminal surface as shipped; they are not a general CL
195
225
  - **Browser-only pages stay in the browser** — workspace and directory pickers, open-in-app links, the trajectory ledger, and per-message like/dislike have no terminal counterpart; `/settings`, `/plugins`, `/subagents`, `/outline`, and the shared `/feedback` cover their facts as text, and subagent transcripts are read by switching to the child session.
196
226
  - **Deliverables are named, not opened** — `/deliverables` lists the presented paths; the browser previews the files.
197
227
  - **Terminal scrollback owns history** — the transcript is not searchable or foldable beyond tool cards; the browser surface owns richer navigation.
228
+ - **The panel lists residency, not the tree** — a child joins it while its session record is resident in this process, so a subagent run by an out-of-process provider, which owns no session here, never appears; `/subagents` remains the way to every durable descendant.
229
+ - **Residency is not work** — the listing's `activity: 'running'` says the child's record is resident, which is what the row's `resident` reports; whether the child is working is the separate `running` / `idle` word beside it, read from that child's Agent in this process.
230
+ - **Rows behind the overflow row are not selectable** — the panel draws at most six rows and `Up` / `Down` wrap inside them; the children folded into `+<n> more` are reached through `/subagents`, which walks the complete descendant tree.
231
+ - **The fade needs an answer from the terminal** — its ramp is built from the background color the terminal reports to the query sent at startup, so a terminal that stays silent, or that encodes neither truecolor nor 256 colors, gets the two-level faint mode instead; `NO_COLOR`, a disabled palette, `TERM=dumb`, and `reducedMotion` turn the effect off entirely.
232
+ - **The terminal's own caret can flicker** — the editor draws no caret of its own and the app turns the terminal cursor on, which pi-tui then moves across the lines it repaints; a terminal that does not honor the synchronized-output sequences pi-tui wraps a frame in can show that movement.
198
233
  - **Runs through the `dsh` launcher** — starting the profile another way fails at startup, because only the launcher can request the process exit.
199
234
 
200
235
  <a id="dev-note"></a>
@@ -9,7 +9,7 @@ kind: "package-bundle"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-tui-app` 是 dsh 的终端表层:`dsh tui` 在你当前所在的终端里启动一个多轮会话,没有浏览器、没有服务器。回复实时流式显示,工具调用变成可折叠的卡片,审批与 `ask_user_question` 的问题出现在输入框上方,`@` 补全路径与会话,`/attach` 加入图片与文件,`/` 命令与 Web 共用注册表。会话持久化:`/sessions`、`/new` 与 `/fork` 在会话间切换,`/export` 写出浏览器的 ZIP,`--resume` 稍后继续。它运行与 `dsh web` 相同的模型、工具与安全默认值,同一时间一个会话。
12
+ `dsh-tui-app` 是 dsh 的终端表层:`dsh tui` 在你当前所在的终端里启动一个多轮会话,没有浏览器托管的应用、也没有服务器。回复实时流式显示,工具调用变成可折叠的卡片,审批与 `ask_user_question` 的问题出现在输入框上方,`@` 补全路径与会话,`/attach` 加入图片与文件,`/` 命令与 Web 共用注册表。会话持久化:`/sessions`、`/new` 与 `/fork` 在会话间切换,`/export` 写出浏览器的 ZIP,`--resume` 稍后继续。它运行与 `dsh web` 相同的模型、工具与安全默认值,同一时间一个会话。
13
13
 
14
14
  ## 目录
15
15
 
@@ -33,13 +33,16 @@ kind: "package-bundle"
33
33
  dsh tui # new session, wait for input
34
34
  dsh tui "explain this repository" # new session with a first prompt
35
35
  dsh tui --resume <session-id> # continue an earlier session
36
+ dsh tui --no-open # print sign-in URLs without opening a browser
36
37
  ```
37
38
 
38
39
  退出时应用在 stderr 为当时绑定的会话打印 `dsh: session <id> saved; resume with: dsh --profile tui --resume <id>`。恢复的会话会在接受输入前先重绘其持久化历史;在终端内,`/sessions` 打开覆盖所有持久化根会话的选择器,`/new` 开始一个新会话,`/fork` 把当前会话复制到其最后一个完成轮次并作为新会话,切割点与浏览器的 fork 相同。切换会释放先前的 Agent 并重绘下一个会话的对话记录。
39
40
 
40
41
  ### 屏幕布局
41
42
 
42
- 页眉在标题生成或设置后以标题命名会话,并在旁边显示 id。对话记录在终端自身的回滚区中增长:你的提示以 `›` 开头(附件列在其下),assistant 的推理以暗色显示在 Markdown 回复上方,每次工具调用是一张卡片,含状态符号、工具名、呈现器标题,以及折叠到 `toolPreviewLines` 行的正文。对话记录下方依次是 agent 工作时的旋转指示、任何打开的提示、编辑器,以及两行页脚:模型与推理强度、权限预设、累计 token 用量、上下文窗口百分比、来自投影接缝的 todo、目标与计划模式标记、workspace、待发送附件数量与按键提示。压缩与模型请求重试以通知形式出现,与浏览器标记承载的事实相同。
43
+ 页眉在标题生成或设置后以标题命名会话,并在旁边显示 id。对话记录在终端自身的回滚区中增长:你的提示以 `›` 开头(附件列在其下),assistant 的推理以暗色显示在 Markdown 回复上方,每次工具调用是一张卡片,含状态符号、工具名、呈现器标题,以及折叠到 `toolPreviewLines` 行的正文。流式回复的文字会淡入:最新的词以接近终端背景色的亮度出现,并经 `streamFadeSteps` 级亮度、每级 `streamFadeStepMs` 提亮到正常前景色,因此流头之后拖着一条柔和的边缘,而已经稳定下来的文字不会再被调暗。对话记录下方依次是 agent 工作时的旋转指示、任何打开的提示、编辑器、子 agent 面板,以及页脚:一行分段状态栏,其下是一行按键提示。编辑器的光标是终端自身的闪烁竖条:应用在启动时请求这一形状,退出时把你的默认形状还回去,而状态栏或面板持有键盘期间完全不绘制光标。每个分段只在其事实存在时出现——模型与推理强度、权限预设、正在进行轮次的已用时间、累计 token 用量、上下文窗口百分比、来自投影接缝的 todo、目标与计划模式标记、workspace 路径(过长时以 `~` 与 `…/` 缩短)、待发送附件数量——`Shift+Up` 把焦点移入状态栏以查看某个分段的详情。压缩与模型请求重试以通知形式出现,与浏览器标记承载的事实相同。
44
+
45
+ 子 agent 面板在绑定会话之下有常驻的子 agent 会话、或列表中带有无法读取的候选者时绘制。其标题统计所列出的条目,每一行给出该子会话的层级缩进、其标签或 id、其模式(`one-shot` 或 `continuable`)、`resident`、其 agent 处于 `running` 还是 `idle`、其已用时间——进行中轮次的用时,否则是已结束轮次的合计——以及其 token 用量,以本进程对该子会话的可见程度与已组合的投影所能提供的为限。最多绘制六行,其下是 `+<n> more · /subagents lists them all`;列表无法解读的候选者绘制为 `unreadable: <reason>` 且打不开任何页面,读取失败的列表则保留上一次成功读取产生的各行,并在其下写出 `listing failed: <reason>`。面板随其最后一行一同消失。
43
46
 
44
47
  ### 按键与命令
45
48
 
@@ -48,19 +51,27 @@ dsh tui --resume <session-id> # continue an earlier session
48
51
  | `Enter` | 发送编辑器文本;轮次进行中时它排队到下一轮次 |
49
52
  | `Ctrl+S` | 轮次进行中时,把编辑器文本引导(steer)进当前轮次的下一步 |
50
53
  | `Shift+Enter` | 插入换行 |
51
- | `Shift+Tab` | 循环切换当前模型的推理强度,从下一次请求生效 |
54
+ | `Shift+Tab` | 编辑器持有焦点时,循环切换当前模型的推理强度,从下一次请求生效 |
52
55
  | `Up` / `Down` | 调出先前的提示 |
56
+ | `Shift+Up` | 在停靠区域之间向前移动焦点:编辑器 → 状态栏 → 子 agent 面板 → 编辑器 |
57
+ | `Shift+Down` | 沿该循环反向移动焦点;在编辑器中且未绘制面板时,它仍是编辑器自己的按键 |
53
58
  | `Esc` | 停止正在进行的轮次;已排队的消息保持排队 |
54
59
  | `Ctrl+O` | 展开或折叠所有工具卡片 |
55
60
  | `Ctrl+C` | 清空编辑器;600 ms 内再按一次则退出 |
56
61
  | `Ctrl+D` | 编辑器为空时退出 |
57
62
 
63
+ 状态栏持有焦点时,`Left` / `Right` 与 `Tab` / `Shift+Tab` 在分段之间移动并在两端环绕,`Enter` 打开所选分段且状态栏保持焦点,`Shift+Up` 在面板已绘制时继续移动到子 agent 面板,`Esc` 或 `Shift+Down` 把焦点交还编辑器。状态栏持有焦点期间其他按键不会到达编辑器;`Ctrl+C` 与 `Ctrl+D` 保持其一贯含义,并把焦点交还编辑器。
64
+
65
+ 子 agent 面板持有焦点时,`Up` / `Down` 移动选择并在两端环绕,`Enter` 把该子会话的详情作为只读页面打开、离开后回到面板的同一行,`Shift+Down` 继续移动到状态栏,`Esc` 或 `Shift+Up` 把焦点交还编辑器。除 `Ctrl+C` 与 `Ctrl+D` 外,其他按键同样在此被消费;面板的最后一行离开时,它也会把键盘交还编辑器。
66
+
67
+ 除 `todo` 之外的每个分段都把详情打印到对话记录,陈述其当前事实并指出改变它们的方式:模型分段指出 `/model`,推理强度分段指出 `Shift+Tab`,权限分段指出其预设;`turn` 分段——绘制在权限与用量分段之间、形如 `turn <elapsed>`,且只在轮次进行时出现——给出轮次编号、其开始时间、已用时间与排队消息数量;用量、上下文、目标与计划分段打印 `/status` 报告中对应的小节,workspace 分段显示完整路径,附件分段列出待发送的附件。`todo` 分段则改为打开 agent 的 todo 列表,与 `/todos` 打开的是同一个列表。
68
+
58
69
  在编辑器开头输入 `/` 会补全终端自身的命令与共享注册表的命令;任意位置的 `@` 补全引用。
59
70
 
60
71
  | 命令 | 效果 |
61
72
  |---|---|
62
73
  | `/help` | 列出命令与按键 |
63
- | `/model` | 为下一次请求选择模型,若模型声明多于一种推理强度则接着选择强度;`/model <provider>/<model>` 直接选择,`/model save` 把当前选择存为默认 |
74
+ | `/model` | 为下一次请求选择模型(输入即可过滤行),若模型声明多于一种推理强度则接着选择强度;`/model <provider>/<model>` 直接选择,`/model save` 把当前选择存为默认 |
64
75
  | `/sessions` | 选择另一个持久化会话并切换过去 |
65
76
  | `/new` | 开始新会话 |
66
77
  | `/fork [turn]` | 在本会话最后一个完成轮次处 fork,或在第 `turn` 轮之后 fork |
@@ -72,9 +83,10 @@ dsh tui --resume <session-id> # continue an earlier session
72
83
  | `/login` | 用提供方订阅登录(隐藏仅收集密钥的登录);`/login <key>` 跳过选择器 |
73
84
  | `/export [dir]` | 把本会话的日志 ZIP(含子会话与附件)写入 `dir`,默认 workspace |
74
85
  | `/status` | 上下文窗口用量与构成、含缓存命中的 token 总计、会话统计、todo、目标、计划模式与权限 |
86
+ | `/todos` | 浏览 agent 的 todo 列表;`Enter` 完整打开其中一条,含其状态、位置与轮次 |
75
87
  | `/outline` | 本会话各轮次及其提示与回复预览 |
76
88
  | `/deliverables` | agent 交付的文件,按轮次分组 |
77
- | `/subagents` | 本会话之下的子 agent 会话,含活动状态与 id |
89
+ | `/subagents` | 浏览本会话之下的子 agent 会话;`Enter` 打开某个会话的详情 |
78
90
  | `/settings [ns [path value]]` | 列出命名空间、显示某一个或设置某个字段;`/settings reset <ns>` 恢复默认 |
79
91
  | `/plugins` | 已组合的插件及其启用状态与生命周期阶段 |
80
92
  | `/tools` | 像 `Ctrl+O` 一样展开或折叠所有工具卡片 |
@@ -82,6 +94,14 @@ dsh tui --resume <session-id> # continue an earlier session
82
94
 
83
95
  其他每条 `/name` 行都交给共享命令注册表,因此 `/compact`、`/permission`、`/goal` 与插件命令的行为和浏览器中一致。
84
96
 
97
+ 终端打开的每个选择器——`/model` 的模型列表与推理强度列表、`/sessions`、`/subagents` 与 `/todos` 的列表,以及 `/signin` 与 `/login` 引出的各行——都随输入过滤其行:查询同时匹配每行的标签与描述,其以空白与斜杠分隔的各段必须全部匹配,各行按最佳匹配在前排序,因此 `dsk chat` 与 `deepseek/chat` 都能找到 `deepseek/deepseek-chat`,`gpt5` 能找到 `gpt-5`。`Backspace` 删除最后一个字符,`Ctrl+U` 清空查询,`Esc` 在查询非空时清空查询、在查询为空时取消选择器,`Up` / `Down` 在匹配行之间移动,`Enter` 选中高亮行。行上方的暗色行在查询为空时显示 `type to filter · Enter selects · Esc cancels`,此后显示 `filter: <query> · <kept>/<total>`;无任何行匹配的查询会以 `no row matches "<query>"` 取代这些行,而生效行上的 `✓`——也就是选择器打开时定位的那一行——只在查询为空时显示。
98
+
99
+ `/subagents`、`/todos` 与状态栏的 `todo` 分段共用同一套先列表、后详情的交互:选择器列出各条目,`Enter` 把高亮条目作为只读页面打开,`Up` / `Down` 与 `PageUp` / `PageDown` 滚动该页面,`Enter`、`Esc` 或 `Left` 返回列表并停在刚读过的条目上,因此连续查看多个条目无需重新输入命令;在列表上按 `Esc` 返回编辑器。详情无法读取的条目会在其页面上说明原因,而不会关闭列表。
100
+
101
+ ### 订阅登录
102
+
103
+ `/login` 只存储订阅凭据,不会激活休眠的模型路由。先配置 catalog 路由,再使用完整的凭据键;例如先运行 `/settings llm-pi-ai providers.openai-codex {}`,再运行 `/login llm-pi-ai/openai-codex`。被标记的授权页面会在本地默认浏览器中打开,其 URL 同时保留在对话记录中作为后备。SSH 启动、无桌面的宿主、`--no-open` 以及打开器失败时,手动 URL 与设备码路径仍然可用。
104
+
85
105
  ### 来自 agent 的提示
86
106
 
87
107
  审批请求绘制 `Allow <tool>?`、请求方的理由、请求所指的已记录调用(与其工具卡片相同的行,因此 shell 命令在运行前可读)以及两行选项:允许一次或拒绝;`Esc` 拒绝,`Ctrl+C` 取消该请求。`ask_user_question` 的问题把其 `detail` 渲染为 Markdown,置于选项与一行自由文本之上;多选用 `Space` 切换各行并通过 `Done` 确认。计划评审(`exit_plan_mode` 设置的 `plan-review` 意图)把计划绘制为 Markdown,并提供 Approve、Decline 与 Discuss 行,其中 Discuss 像浏览器卡片一样把请求交回编辑器。提示排队、一次只显示一个,被中止的请求会撤回其提示。
@@ -97,8 +117,13 @@ dsh tui --resume <session-id> # continue an earlier session
97
117
  | `prompt` | 无 | 终端就绪后提交的首个提示 |
98
118
  | `resume` | 无 | 要继续的持久化会话 id,而不是新建会话 |
99
119
  | `toolPreviewLines` | `8` | `Ctrl+O` 展开前折叠的工具卡片正文行数 |
120
+ | `liveRefreshMs` | `1000` | 重绘周期:推进 `turn` 分段与面板中的已用时间,并重新读取已过期的子 agent 列表 |
121
+ | `streamFadeSteps` | `5` | 流式 assistant 文本在以正常前景色绘制之前经过的亮度级数 |
122
+ | `streamFadeStepMs` | `40` | 每级亮度持续多久,这也是淡入文字的重绘周期 |
123
+ | `reducedMotion` | `false` | 以正常前景色绘制流式文本,不做淡入,也不重复重绘 |
124
+ | `openBrowser` | `true` | 把被标记的授权页面交给本地默认浏览器 |
100
125
 
101
- `prompt` 与 `resume` 经启动提供方来自命令行;生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tui-app)是所有可接受字段的完整来源。
126
+ `prompt`、`resume` 与 `openBrowser` 经启动提供方来自命令行;生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tui-app)是所有可接受字段的完整来源。
102
127
 
103
128
  -----
104
129
 
@@ -112,11 +137,11 @@ runner 与 `dsh-headless` 一样是核心 API 载体之上的直接驱动器,
112
137
 
113
138
  ### 运行流程
114
139
 
115
- runner 等待完整应用就绪(`ctx.get('loader')?.await()`),并在核心注册表之上构建含三个操作的会话宿主:`create` 用共享的 [`agentDefaultModel`](../../core/agent-default-model/README.zh.md) 选择创建一个全新的持久化 Agent,`resume` 通过 `ctx.sessionPersistence` 的只读句柄分页读取持久化日志并经注册表恢复 Agent,`fork` 通过 `ctx.sessionQuery` 观察源会话、在所选(默认最后一个)`turn/end` 之后直到下一个 `turn/start` 处切割,并创建带 `parentSession` 与 `isSeeded` 元数据的种子 Agent。每个操作都在 Agent 的作用域 setup 中安装 `ModelSelectionRef`,因此 `/model` 会改变下一次请求。终端应用从 `--resume` 或一次新的 `create` 产生的会话开始,订阅 `session/event`、`agent/assistant-stream` 与 `agent/status`,只为绑定的 Agent 应答 `approval/request` 与 `user-questions/request` waterfall,并通过绑定下一个会话、dispose 先前句柄来切换会话;宿主打开下一个会话期间编辑器拒绝输入,等待期间退出会释放随后到达的会话。退出时取消任何进行中的轮次、等待完全停稳、flush 绑定的会话、dispose 其句柄并请求以 0 退出;驱动器失败会向 stderr 写入 `dsh: <message>` 并请求以 1 退出。Shift+Tab 循环切换绑定模型的适配器自有推理强度,并在提供方默认值处回绕;`/login` 只带着订阅方法(除收集密钥的 `api-key` 登录外的每一种方法)启动 `authorization.begin`。
140
+ runner 等待完整应用就绪(`ctx.get('loader')?.await()`),并在核心注册表之上构建含三个操作的会话宿主:`create` 用共享的 [`agentDefaultModel`](../../core/agent-default-model/README.zh.md) 选择创建一个全新的持久化 Agent,`resume` 通过 `ctx.sessionPersistence` 的只读句柄分页读取持久化日志并经注册表恢复 Agent,`fork` 通过 `ctx.sessionQuery` 观察源会话、在所选(默认最后一个)`turn/end` 之后直到下一个 `turn/start` 处切割,并创建带 `parentSession` 与 `isSeeded` 元数据的种子 Agent。每个操作都在 Agent 的作用域 setup 中安装 `ModelSelectionRef`,因此 `/model` 会改变下一次请求。终端应用从 `--resume` 或一次新的 `create` 产生的会话开始,订阅 `session/event`、`agent/assistant-stream` 与 `agent/status`,只为绑定的 Agent 应答 `approval/request` 与 `user-questions/request` waterfall,并通过绑定下一个会话、dispose 先前句柄来切换会话;宿主打开下一个会话期间编辑器拒绝输入,等待期间退出会释放随后到达的会话。退出时取消任何进行中的轮次、等待完全停稳、flush 绑定的会话、dispose 其句柄并请求以 0 退出;驱动器失败会向 stderr 写入 `dsh: <message>` 并请求以 1 退出。Shift+Tab 循环切换绑定模型的适配器自有推理强度,并在提供方默认值处回绕;`/login` 只带着订阅方法(除收集密钥的 `api-key` 登录外的每一种方法)启动 `authorization.begin`。flow 用 `openInBrowser` 标记的 notice 会经 `dsh-native-command` 的凭据擦除辅助进程交给默认浏览器,URL 同时保持打印;当 `openBrowser` 为 false、启动经过 SSH 或宿主没有桌面时抑制该交接,打开器失败则成为 URL 旁的一条通知,而非登录失败。
116
141
 
117
142
  ### 渲染模型
118
143
 
119
- 持久事实来自会话日志:`user/message`(自己提交的消息只绘制一次,其回显按消息 id 跳过;插件通知是一行暗色文字,其他注入的上下文不绘制)、`assistant/message`(用已提交文本替换流式块,并把用量折入页脚)、`tool/call` 与 `tool/result`(工具声明 `presentCall` 与 `presentResult` 视图时据此绘制,否则回退到原始参数与原始结果)、`turn/end` 通知、`session/title`(页眉)与 `permission/preset`(页脚)。实时增量来自 `agent/assistant-stream` 的文本与推理增量。日志之外的会话事实来自浏览器读取的同一批服务:`sessionTitle`、`permissionPresets`、供选择器与 `/deliverables` 使用的 `sessionQuery`、供页脚、`/status` 与 `/outline` 使用的 `sessionProjections`、供 `@` 补全使用的 `fileReferences` 与 `sessionReferenceResolver`、`attachments`、`skills`、`authorization`、`settings`、`subagents`,以及供 `/plugins` 使用的 Loader 条目。模态提示是进程本地的呈现,从不写入日志。
144
+ 持久事实来自会话日志:`user/message`(自己提交的消息只绘制一次,其回显按消息 id 跳过;插件通知是一行暗色文字,其他注入的上下文不绘制)、`assistant/message`(用已提交文本替换流式块,并把用量折入页脚)、`tool/call` 与 `tool/result`(工具声明 `presentCall` 与 `presentResult` 视图时据此绘制,否则回退到原始参数与原始结果)、`turn/end` 通知、`session/title`(页眉)、`permission/preset`(页脚),以及所属 `turn/start` 之下的 `todo/write`(某条 todo 的详情页所报告的轮次;该列表不带逐条标识,因此改写措辞的条目算作新条目)。实时增量来自 `agent/assistant-stream` 的文本与推理增量。日志之外的会话事实来自浏览器读取的同一批服务:`sessionTitle`、`permissionPresets`、供选择器、`/deliverables` 与子 agent 详情使用的 `sessionQuery`、供页脚、`/status`、`/todos` 与 `/outline` 使用的 `sessionProjections`、供 `@` 补全使用的 `fileReferences` 与 `sessionReferenceResolver`、`attachments`、`skills`、`authorization`、`settings`、`subagents`,以及供 `/plugins` 使用的 Loader 条目。模态提示是进程本地的呈现,从不写入日志。
120
145
 
121
146
  ### 基于 base 的 patch 面
122
147
 
@@ -127,24 +152,29 @@ runner 等待完整应用就绪(`ctx.get('loader')?.await()`),并在核心
127
152
  | 文件 | 职责 |
128
153
  |---|---|
129
154
  | [`src/index.ts`](src/index.ts) | `tui-app` 插件:会话宿主(创建、恢复、fork)、历史读取、退出流程、退出码映射 |
130
- | [`src/startup.ts`](src/startup.ts) | `tui-app-startup` 提供方:提示位置参数、`--resume` 与 `--help` |
155
+ | [`src/startup.ts`](src/startup.ts) | `tui-app-startup` 提供方:提示位置参数、`--resume`、`--no-open` 与 `--help` |
131
156
  | [`src/app.ts`](src/app.ts) | 终端应用:布局、按键、命令、会话绑定、接缝、日志与流的折叠 |
132
157
  | [`src/sessions.ts`](src/sessions.ts) | 基于查询引擎的 `/sessions` 列表及其选择器行 |
133
158
  | [`src/attach.ts`](src/attach.ts) | `/attach`:本地文件经附件存储成为图片或文件块 |
134
159
  | [`src/export.ts`](src/export.ts) | `/export`:通过导出包的归档辅助函数写出会话日志 ZIP |
135
160
  | [`src/blocks.ts`](src/blocks.ts) | 对话记录组件:用户提示、assistant 回复、工具卡片、通知 |
136
- | [`src/prompts.ts`](src/prompts.ts) | 审批、提问与选择器提示以及模态队列 |
161
+ | [`src/fade.ts`](src/fade.ts) | 流式文本淡入:尾部追踪器、从背景色到前景色的亮度级别,以及对已渲染行的重新着色 |
162
+ | [`src/prompts.ts`](src/prompts.ts) | 审批、提问、选择器与只读详情提示以及模态队列 |
137
163
  | [`src/transcript.ts`](src/transcript.ts) | 呈现视图、用量与轮次结束原因的纯文本折叠 |
138
164
  | [`src/diff.ts`](src/diff.ts) | diff 卡片的行 diff 与 hunk 选择 |
139
165
  | [`src/style.ts`](src/style.ts) | 调色板与派生的 pi-tui 主题 |
140
166
  | [`src/completion.ts`](src/completion.ts) | 编辑器的斜杠命令与 `@` 引用补全 |
141
- | [`src/status.ts`](src/status.ts) | 基于投影接缝的页脚部件与 `/status` 报告;压缩与重试通知 |
167
+ | [`src/editor.ts`](src/editor.ts) | 去掉 pi-tui 自绘块状光标的提示编辑器,以及终端自身光标所用的 DECSCUSR 序列 |
168
+ | [`src/status.ts`](src/status.ts) | 投影接缝的事实,以及 `/status` 报告与分段详情共享的小节;压缩与重试通知 |
169
+ | [`src/footer.ts`](src/footer.ts) | 状态栏:有序的各分段、每个分段的详情行,以及页脚渲染出的两行 |
170
+ | [`src/subagent-panel.ts`](src/subagent-panel.ts) | 实时子 agent 面板:一次后代列表加上采样到的实时事实构成其各行,各行再构成其文本 |
142
171
  | [`src/catalog.ts`](src/catalog.ts) | `/settings`、`/plugins`、`/subagents`、`/deliverables` 与 `/outline` 的行 |
172
+ | [`src/todos.ts`](src/todos.ts) | todo 列表:状态符号、选择器行与单个条目的详情行 |
143
173
  | [`cordis.patch.yml`](cordis.patch.yml) | 基于 `dsh-base` 的终端 patch |
144
174
  | — | 不发布运行时不变量伴随模块;应用只在一个 Agent 上注册监听器,不持有其他观察者可能与之矛盾的可变关系。 |
145
175
  | [`tests/app.spec.ts`](tests/app.spec.ts) | 基于伪终端的渲染、按键、命令与两个接缝 |
146
176
  | [`tests/commands.spec.ts`](tests/commands.spec.ts) | 基于脚本化服务的会话、附件、队列、技能、登录、`/login`、Shift+Tab 推理强度循环、导出、引用与推理强度命令 |
147
- | [`tests/panels.spec.ts`](tests/panels.spec.ts) | 状态页脚与报告、目录命令、命令提示与审批详情 |
177
+ | [`tests/panels.spec.ts`](tests/panels.spec.ts) | 状态页脚与报告、可导航的子 agent 与 todo 列表、目录命令、命令提示与审批详情 |
148
178
  | [`tests/index.spec.ts`](tests/index.spec.ts) | 创建、恢复分页、fork 切割、会话切换、退出流程与失败报告 |
149
179
  | [`tests/startup.spec.ts`](tests/startup.spec.ts) | 基于真实 Loader 配置树的命令行解析 |
150
180
  | [`../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts`](../../../apps/cli/tests/profiles/tui/tests/keyless-smoke.e2e.ts) | 通过真实启动器与免密钥模拟模型运行随附 profile |
@@ -195,6 +225,11 @@ runner 不向请求前缀添加任何内容;`/model` 切换像在浏览器中
195
225
  - **仅浏览器的页面留在浏览器**——workspace 与目录选择器、在应用中打开的链接、轨迹账本与逐条消息的点赞/点踩没有终端对应物;`/settings`、`/plugins`、`/subagents`、`/outline` 与共享的 `/feedback` 以文本覆盖其事实,子 agent 的对话记录通过切换到子会话来阅读。
196
226
  - **交付物只列名、不打开**——`/deliverables` 列出交付路径;浏览器会预览这些文件。
197
227
  - **历史由终端回滚区持有**——除工具卡片外,对话记录不可搜索或折叠;更丰富的导航由浏览器表层持有。
228
+ - **面板列出的是常驻,而不是整棵树**——子会话在其会话记录常驻于本进程期间加入面板,因此由进程外提供方运行、在此没有自己会话的子 agent 永远不会出现;`/subagents` 仍是到达每一个持久后代的途径。
229
+ - **常驻不等于正在工作**——列表的 `activity: 'running'` 表示该子会话的记录常驻,这正是行内 `resident` 所报告的内容;子会话是否在工作则是它旁边单独的 `running` / `idle`,读自本进程中该子会话的 Agent。
230
+ - **溢出行之后的各行不可选中**——面板最多绘制六行,`Up` / `Down` 只在这些行内环绕;被折叠进 `+<n> more` 的子会话通过 `/subagents` 到达,后者遍历完整的后代树。
231
+ - **淡入需要终端的应答**——其亮度级别由终端对启动时发出的查询所报告的背景色构建,因此保持沉默、或既不编码真彩色也不编码 256 色的终端只会得到两级的暗淡模式;`NO_COLOR`、被禁用的调色板、`TERM=dumb` 与 `reducedMotion` 则完全关闭该效果。
232
+ - **终端自身的光标可能闪烁**——编辑器不绘制自己的光标,应用打开终端光标,而 pi-tui 会在其重绘的各行之间移动它;不支持 pi-tui 为一帧包裹的同步输出序列的终端可能显示出这种移动。
198
233
  - **通过 `dsh` 启动器运行**——以其他方式启动该 profile 会在启动时失败,因为只有启动器能请求进程退出。
199
234
 
200
235
  <a id="dev-note"></a>
@@ -1,8 +1,8 @@
1
1
  # The dsh-tui-app bundle patch: the interactive terminal surface directly over
2
2
  # dsh-base. It mounts no Host, HTTP server, Web runtime, or browser plugin. An
3
3
  # ordinary provider plugin injects `cmdlineArgs`, parses the optional prompt
4
- # positional and `--resume` (`dsh --profile tui [prompt...]`) plus this app's
5
- # --help, then the terminal runner creates or resumes one Agent through the
4
+ # positional, `--resume`, and `--no-open` (`dsh --profile tui [prompt...]`)
5
+ # plus this app's --help, then the terminal runner creates or resumes one Agent through the
6
6
  # core registry and drives it from the terminal until the user quits.
7
7
 
8
8
  - id: system-prompt
@@ -56,4 +56,5 @@
56
56
  config:
57
57
  prompt: !!js ctx.tuiStartup.prompt
58
58
  resume: !!js ctx.tuiStartup.resume
59
+ openBrowser: !!js ctx.tuiStartup.openBrowser
59
60
  toolPreviewLines: 8