@sagmans/dsh-tui 0.4.0 → 0.5.0

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 (200) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +270 -68
  3. package/lib/agent/present.js +1 -1
  4. package/lib/agent/present.js.map +1 -1
  5. package/lib/agent/prompt-history.d.ts.map +1 -1
  6. package/lib/agent/prompt-history.js +5 -3
  7. package/lib/agent/prompt-history.js.map +1 -1
  8. package/lib/agent/status.d.ts +1 -1
  9. package/lib/cards.d.ts +83 -13
  10. package/lib/cards.d.ts.map +1 -1
  11. package/lib/cards.js +96 -28
  12. package/lib/cards.js.map +1 -1
  13. package/lib/export.d.ts +3 -3
  14. package/lib/export.d.ts.map +1 -1
  15. package/lib/export.js +23 -13
  16. package/lib/export.js.map +1 -1
  17. package/lib/fold-cursor.d.ts +12 -5
  18. package/lib/fold-cursor.d.ts.map +1 -1
  19. package/lib/fold-cursor.js +16 -5
  20. package/lib/fold-cursor.js.map +1 -1
  21. package/lib/gates.d.ts.map +1 -1
  22. package/lib/gates.js +42 -9
  23. package/lib/gates.js.map +1 -1
  24. package/lib/index.d.ts.map +1 -1
  25. package/lib/index.js +459 -72
  26. package/lib/index.js.map +1 -1
  27. package/lib/injection.d.ts +9 -0
  28. package/lib/injection.d.ts.map +1 -0
  29. package/lib/injection.js +128 -0
  30. package/lib/injection.js.map +1 -0
  31. package/lib/input/actions.d.ts +19 -2
  32. package/lib/input/actions.d.ts.map +1 -1
  33. package/lib/input/actions.js +57 -11
  34. package/lib/input/actions.js.map +1 -1
  35. package/lib/input/cancel.d.ts +55 -0
  36. package/lib/input/cancel.d.ts.map +1 -0
  37. package/lib/input/cancel.js +47 -0
  38. package/lib/input/cancel.js.map +1 -0
  39. package/lib/input/completion.d.ts +5 -2
  40. package/lib/input/completion.d.ts.map +1 -1
  41. package/lib/input/completion.js +79 -4
  42. package/lib/input/completion.js.map +1 -1
  43. package/lib/input/file-search.d.ts +105 -0
  44. package/lib/input/file-search.d.ts.map +1 -0
  45. package/lib/input/file-search.js +665 -0
  46. package/lib/input/file-search.js.map +1 -0
  47. package/lib/input/fuzzy.d.ts +42 -0
  48. package/lib/input/fuzzy.d.ts.map +1 -0
  49. package/lib/input/fuzzy.js +289 -0
  50. package/lib/input/fuzzy.js.map +1 -0
  51. package/lib/input/keymap.d.ts +3 -1
  52. package/lib/input/keymap.d.ts.map +1 -1
  53. package/lib/input/keymap.js +14 -3
  54. package/lib/input/keymap.js.map +1 -1
  55. package/lib/input/match.d.ts +12 -4
  56. package/lib/input/match.d.ts.map +1 -1
  57. package/lib/input/match.js +50 -51
  58. package/lib/input/match.js.map +1 -1
  59. package/lib/input/submission.d.ts +10 -0
  60. package/lib/input/submission.d.ts.map +1 -1
  61. package/lib/input/submission.js +7 -6
  62. package/lib/input/submission.js.map +1 -1
  63. package/lib/keys-command.d.ts +19 -3
  64. package/lib/keys-command.d.ts.map +1 -1
  65. package/lib/keys-command.js +34 -31
  66. package/lib/keys-command.js.map +1 -1
  67. package/lib/settings-notice.d.ts +5 -0
  68. package/lib/settings-notice.d.ts.map +1 -1
  69. package/lib/settings-notice.js +9 -7
  70. package/lib/settings-notice.js.map +1 -1
  71. package/lib/stash/paths.d.ts +8 -8
  72. package/lib/stash/paths.d.ts.map +1 -1
  73. package/lib/stash/paths.js +19 -18
  74. package/lib/stash/paths.js.map +1 -1
  75. package/lib/stash/schema.d.ts +11 -12
  76. package/lib/stash/schema.d.ts.map +1 -1
  77. package/lib/stash/schema.js +10 -29
  78. package/lib/stash/schema.js.map +1 -1
  79. package/lib/stash/store.js +10 -10
  80. package/lib/stash/store.js.map +1 -1
  81. package/lib/stash.d.ts +37 -6
  82. package/lib/stash.d.ts.map +1 -1
  83. package/lib/stash.js +93 -36
  84. package/lib/stash.js.map +1 -1
  85. package/lib/terminal/external-editor.d.ts +93 -0
  86. package/lib/terminal/external-editor.d.ts.map +1 -0
  87. package/lib/terminal/external-editor.js +263 -0
  88. package/lib/terminal/external-editor.js.map +1 -0
  89. package/lib/terminal/host-writes.d.ts +39 -0
  90. package/lib/terminal/host-writes.d.ts.map +1 -0
  91. package/lib/terminal/host-writes.js +136 -0
  92. package/lib/terminal/host-writes.js.map +1 -0
  93. package/lib/terminal/signals.d.ts +25 -0
  94. package/lib/terminal/signals.d.ts.map +1 -0
  95. package/lib/terminal/signals.js +39 -0
  96. package/lib/terminal/signals.js.map +1 -0
  97. package/lib/terminal/warning-screen.d.ts +19 -1
  98. package/lib/terminal/warning-screen.d.ts.map +1 -1
  99. package/lib/terminal/warning-screen.js +37 -1
  100. package/lib/terminal/warning-screen.js.map +1 -1
  101. package/lib/terminal-text.d.ts +72 -0
  102. package/lib/terminal-text.d.ts.map +1 -0
  103. package/lib/terminal-text.js +603 -0
  104. package/lib/terminal-text.js.map +1 -0
  105. package/lib/text.d.ts +45 -6
  106. package/lib/text.d.ts.map +1 -1
  107. package/lib/text.js +106 -46
  108. package/lib/text.js.map +1 -1
  109. package/lib/theme-command.d.ts +7 -4
  110. package/lib/theme-command.d.ts.map +1 -1
  111. package/lib/theme-command.js +32 -12
  112. package/lib/theme-command.js.map +1 -1
  113. package/lib/theme-files.d.ts +110 -0
  114. package/lib/theme-files.d.ts.map +1 -0
  115. package/lib/theme-files.js +352 -0
  116. package/lib/theme-files.js.map +1 -0
  117. package/lib/theme-schema.d.ts +121 -0
  118. package/lib/theme-schema.d.ts.map +1 -0
  119. package/lib/theme-schema.js +87 -0
  120. package/lib/theme-schema.js.map +1 -0
  121. package/lib/theme-settings.d.ts +43 -10
  122. package/lib/theme-settings.d.ts.map +1 -1
  123. package/lib/theme-settings.js +103 -43
  124. package/lib/theme-settings.js.map +1 -1
  125. package/lib/theme-tokens.d.ts +39 -7
  126. package/lib/theme-tokens.d.ts.map +1 -1
  127. package/lib/theme-tokens.js +96 -13
  128. package/lib/theme-tokens.js.map +1 -1
  129. package/lib/theme.d.ts +24 -2
  130. package/lib/theme.d.ts.map +1 -1
  131. package/lib/theme.js +49 -6
  132. package/lib/theme.js.map +1 -1
  133. package/lib/tool-display.d.ts +57 -0
  134. package/lib/tool-display.d.ts.map +1 -0
  135. package/lib/tool-display.js +51 -0
  136. package/lib/tool-display.js.map +1 -0
  137. package/lib/transcript.d.ts +28 -0
  138. package/lib/transcript.d.ts.map +1 -1
  139. package/lib/transcript.js +126 -37
  140. package/lib/transcript.js.map +1 -1
  141. package/lib/ui/diff.d.ts +49 -0
  142. package/lib/ui/diff.d.ts.map +1 -0
  143. package/lib/ui/diff.js +208 -0
  144. package/lib/ui/diff.js.map +1 -0
  145. package/lib/ui/dock.d.ts.map +1 -1
  146. package/lib/ui/dock.js +9 -6
  147. package/lib/ui/dock.js.map +1 -1
  148. package/lib/ui/editor.d.ts.map +1 -1
  149. package/lib/ui/editor.js +4 -2
  150. package/lib/ui/editor.js.map +1 -1
  151. package/lib/ui/frame.d.ts +16 -5
  152. package/lib/ui/frame.d.ts.map +1 -1
  153. package/lib/ui/frame.js +32 -13
  154. package/lib/ui/frame.js.map +1 -1
  155. package/lib/ui/keymap-picker.d.ts +16 -0
  156. package/lib/ui/keymap-picker.d.ts.map +1 -0
  157. package/lib/ui/keymap-picker.js +41 -0
  158. package/lib/ui/keymap-picker.js.map +1 -0
  159. package/lib/ui/layout.d.ts +33 -0
  160. package/lib/ui/layout.d.ts.map +1 -0
  161. package/lib/ui/layout.js +33 -0
  162. package/lib/ui/layout.js.map +1 -0
  163. package/lib/ui/markdown.d.ts +25 -3
  164. package/lib/ui/markdown.d.ts.map +1 -1
  165. package/lib/ui/markdown.js +12 -8
  166. package/lib/ui/markdown.js.map +1 -1
  167. package/lib/ui/picker-card.d.ts +42 -0
  168. package/lib/ui/picker-card.d.ts.map +1 -0
  169. package/lib/ui/picker-card.js +148 -0
  170. package/lib/ui/picker-card.js.map +1 -0
  171. package/lib/ui/picker.d.ts +40 -2
  172. package/lib/ui/picker.d.ts.map +1 -1
  173. package/lib/ui/picker.js +50 -6
  174. package/lib/ui/picker.js.map +1 -1
  175. package/lib/ui/prompt.d.ts +10 -2
  176. package/lib/ui/prompt.d.ts.map +1 -1
  177. package/lib/ui/prompt.js +21 -2
  178. package/lib/ui/prompt.js.map +1 -1
  179. package/lib/ui/rows.d.ts +8 -6
  180. package/lib/ui/rows.d.ts.map +1 -1
  181. package/lib/ui/rows.js +10 -18
  182. package/lib/ui/rows.js.map +1 -1
  183. package/lib/ui/stash-picker.d.ts +2 -2
  184. package/lib/ui/stash-picker.d.ts.map +1 -1
  185. package/lib/ui/stash-picker.js +4 -4
  186. package/lib/ui/stash-picker.js.map +1 -1
  187. package/lib/ui/status.d.ts.map +1 -1
  188. package/lib/ui/status.js +18 -16
  189. package/lib/ui/status.js.map +1 -1
  190. package/lib/ui/theme-picker.d.ts +18 -0
  191. package/lib/ui/theme-picker.d.ts.map +1 -0
  192. package/lib/ui/theme-picker.js +68 -0
  193. package/lib/ui/theme-picker.js.map +1 -0
  194. package/lib/ui/view.d.ts +146 -22
  195. package/lib/ui/view.d.ts.map +1 -1
  196. package/lib/ui/view.js +461 -154
  197. package/lib/ui/view.js.map +1 -1
  198. package/package.json +3 -1
  199. package/themes/deepseek-blue.yaml +183 -0
  200. package/themes/violet-orbit.yaml +178 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 sagmans
3
+ Copyright (c) 2026 Sercan Sagman - ahmetsercansagman@gmail.com
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Interactive terminal (TUI) surface for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): use `dsh` in a terminal instead of a browser.
4
4
 
5
- Status: **v1 feature-complete; published on npm as `@sagmans/dsh-tui`.** The surface owns the alternate screen, streams assistant text as markdown, renders every tool's own card, answers approvals and questions, restores and names stored conversations, switches model mid-session, runs any of the four shipped agent modes and switches between them before a session's first turn, reads a child agent's conversation in place, keeps the goal, plan mode, todo list, delegations, and background jobs above the editor with a status line below it, nudges an agent whose plan has aged without an update, parks and restores prompt drafts per working directory, and hands the terminal back on every graceful exit. Publication is tag-driven with GitHub OIDC provenance and no stored npm token; see [RELEASE.md](RELEASE.md).
5
+ Status: **v1 feature-complete; published on npm as `@sagmans/dsh-tui`.** The surface owns the alternate screen, renders every message as markdown, renders every tool's own card, answers approvals and questions, restores and names stored conversations, switches model mid-session, runs any of the four shipped agent modes and switches between them before a session's first turn, reads a child agent's conversation in place, keeps the goal, plan mode, todo list, delegations, and background jobs above the editor with a status line below it, nudges an agent whose plan has aged without an update, parks and restores prompt drafts per session, and hands the terminal back on every graceful exit. Publication is tag-driven with GitHub OIDC provenance and no stored npm token; see [RELEASE.md](RELEASE.md).
6
6
 
7
7
  ## Install
8
8
 
@@ -117,30 +117,34 @@ dsh --profile tui --no-color
117
117
  dsh --profile tui --no-bell # do not ring when a long turn finishes
118
118
  ```
119
119
 
120
- Every key below is a shipped default. `/keys` lists every action the surface
121
- and its library can perform with the keys in force, and the `keys:` section
122
- moves any of them — see [Keys](#keys).
120
+ Every key below is a shipped default. `/keys` opens every action the surface
121
+ and its library can perform as a list you filter as you type, with the keys in
122
+ force, and the `keys:` section moves any of them — see [Keys](#keys).
123
123
 
124
124
  | Key | Action |
125
125
  |---|---|
126
126
  | Enter / Shift+Enter | break the line: a prompt is written before it is sent |
127
127
  | Ctrl+Enter / Alt+Enter / Ctrl+S | submit the prompt |
128
- | Ctrl+C | interrupt the running turn, or leave when idle |
128
+ | Ctrl+C | take back one thing at a time: the draft in the bar, the prompts waiting in the agent's inbox, the running turn, or a child's conversation; with a picker, an approval, a question, or the transcript search open it closes that instead. With nothing left to cancel it does nothing — it never leaves |
129
+ | Ctrl+D | leave and print the resume command, when the bar holds no text and nothing is open; a running turn is cancelled first |
129
130
  | Ctrl+O | open every tool card: its header plus every retained row. Folded, a card is one line, and a shell card keeps its command plus the last 20 rows of output with a hint naming what it dropped |
130
- | Ctrl+Y | show or hide the calls a PTC program dispatched: one two-space-indented entry per call under its `run_code` card, named and argued from the tool's own header and wrapped at the screen edge; shown by default |
131
- | Shift+Tab | expand or fold the reasoning behind an answer: folded, the row names itself, its token count, and the key; opened, it adds the thought |
131
+ | Ctrl+Y | show or hide the calls a PTC program dispatched: one two-space-indented row per call under its `run_code` card, named and argued from the tool's own header and cut at the screen edge; clicking one row opens that call in full — the change it declared, then what it produced — for every tool, a failed call included; shown by default |
132
+ | Shift+Tab | expand or fold every thought behind the answers: folded, the row names itself, its token count, and the key; opened, it adds the thought, laid out as markdown; a click decides for one thought instead |
132
133
  | Ctrl+T | pick the reasoning effort for the next step |
133
134
  | Ctrl+R | reverse-search recorded prompts: the list opens filtered by whatever is in the bar, `enter` puts one back, `esc` keeps the draft |
134
135
  | Ctrl+X then S | stash the current draft |
135
- | Ctrl+X then L | open this directory's stashed drafts |
136
+ | Ctrl+X then L | open this session's stashed drafts |
136
137
  | Ctrl+X then M | open the model picker |
137
138
  | Ctrl+X then Y | copy the last answer to the clipboard |
138
- | `y` / `n` / Esc | allow once, reject, or cancel a pending approval |
139
- | digits / space / ↑↓ / Enter / Esc | answer a question: pick or toggle, confirm, or skip one; `0` answers with your own text in the input bar |
139
+ | Ctrl+X then E | edit the draft in `$VISUAL` (or `$EDITOR`) and take back what it saves |
140
+ | Ctrl+X then ? | search the key map: every action and the keys in force, in a box over the transcript |
141
+ | `y` / `n` / Esc / Ctrl+C | allow once, reject, or cancel a pending approval |
142
+ | digits / space / ↑↓ / Enter / Esc / Ctrl+C | answer a question: pick or toggle, confirm, or skip one with Esc; Ctrl+C abandons the whole batch with no answers, like an aborted call; `0` answers with your own text in the input bar |
143
+ | ↑↓ / Ctrl+P / Ctrl+N | move through the open list: a picker's rows, a question's options, or the completion menu above the bar |
140
144
  | typing in any picker or question | narrow the rows by fragment (`glm53` finds `GLM-5.3`); backspace widens, `esc` or Ctrl+C leaves |
141
145
  | `/` then Tab | complete commands, including every command this session registered |
142
- | `@` or a path then Tab | complete workspace file references |
143
- | `ctrl+shift+f` | search the transcript (`enter` next, `shift+enter` previous, `esc` close) |
146
+ | `@` | open the workspace file menu, narrowed as you type; a path then Tab still completes a file reference |
147
+ | `ctrl+shift+f` | search the transcript (`enter` next, `shift+enter` previous, `esc` or Ctrl+C close) |
144
148
  | `home` / `end` | jump to the start or the end of the transcript |
145
149
  | `ctrl+down` | jump to the next prompt |
146
150
  | `ctrl+b` | leave a child's conversation and return to this session (the status line names the key you have now) |
@@ -153,6 +157,7 @@ moves any of them — see [Keys](#keys).
153
157
  | `/model <provider>/<model>/<effort>` | use that route and reasoning effort (the effort must be one the route advertises) |
154
158
  | `/preset` | pick the agent mode for this session from the roster |
155
159
  | `/preset <id>` | switch to that mode, while the session is still blank |
160
+ | `/new [title]` | start a fresh session without leaving the terminal (`ctrl+x` then `n` starts one untitled) |
156
161
  | `/jobs` | list background jobs with their state and duration |
157
162
  | `/jobs read <id>` / `/jobs kill <id>` | show the tail of a job's output, or stop it |
158
163
  | `/subagents` | list the delegations this session started, with their provider and age |
@@ -165,39 +170,47 @@ moves any of them — see [Keys](#keys).
165
170
  | `/clear` | clear the visible transcript |
166
171
  | `/history` | show how many prompts are recorded and where the file is |
167
172
  | `/history clear` | forget every recorded prompt, reporting how many went |
168
- | `/theme` | list every styled element and the value in force |
169
- | `/keys` | list every action and the keys in force; `/keys <layer>` narrows it (see [Keys](#keys)) |
173
+ | `/theme` | open the theme picker: type to filter, the screen paints the row under the cursor |
174
+ | `/theme <name>` | apply a theme by name and write the choice to the settings document |
175
+ | `/theme tokens` | list every styled element and the value in force |
176
+ | `/theme export <built-in>` | copy a built-in into your own themes directory to edit |
177
+ | `/keys` | open the key map as a list you filter as you type; `/keys <layer>` opens it already narrowed to one layer (see [Keys](#keys)) |
170
178
  | `/stash <draft>` | park the text given after the command (`ctrl+x` then `s` parks the editor) |
171
179
  | `/stash-pop [index\|id]` | put a stashed draft into the editor and remove it (newest by default) |
172
180
  | `/stash-apply [index\|id]` | put a stashed draft into the editor and keep it |
173
- | `/stash-list` | pick from this directory's stashed drafts; `enter` pops the marked one |
181
+ | `/stash-list` | pick from this session's stashed drafts; `enter` pops the marked one |
174
182
  | `/stash-drop [index\|id]` | delete a stashed draft without using it |
175
- | `/stash-clear` | delete every stashed draft for this directory, after a confirmation |
183
+ | `/stash-clear` | delete every draft stashed in this session, after a confirmation |
176
184
  | `/quit` | leave and print the resume command |
177
185
 
186
+ Typing `@` opens this workspace's files above the editor, ranked as the fragment is typed the way a fuzzy finder ranks a path list: `@edtr` reaches `src/ui/editor.ts` without spelling the separators, a directory offers itself with a trailing slash so typing continues into it, and a path holding a space is quoted. A directory whose own name holds a space offers no row, because the menu stops following the token once one is in it; the files under it are still listed, each quoted whole. The rows are what git tracks or would add, with ignored paths left out, so a suggestion never names build output or a secret the repository deliberately ignores; a tree git does not own is walked instead, skipping `node_modules`, `.git`, and the rest of the build litter. A path typed from the working directory still completes on Tab as before.
187
+
178
188
  `Ctrl+X` starts a chord. For the next two seconds the footer leads with the
179
189
  prefix alone — enough to say that a key is waiting, without reciting the map —
180
190
  and a key that finishes nothing is typed as usual rather than swallowed, so a
181
191
  prefix pressed by accident costs nothing; `/help` lists the chords, `m` for the
182
- model picker, `p` for plan mode, `y` for the last answer, `s` to stash the
183
- draft, and `l` for the stashes.
192
+ model picker, `p` for plan mode, `n` for a fresh session, `y` for the last
193
+ answer, `s` to stash the draft, `l` for the stashes, `e` for the draft in the
194
+ reader's own editor, and `?` for the key map.
184
195
  `keys.chord.prefix: alt+x` starts the chord with another key — or with a list of
185
196
  them, as so many ways in — and `prefixWindow: 0` waits for the next key instead
186
197
  of lapsing; every second key is a row of its own (`chord.model`, `chord.plan`,
187
- `chord.copy`, `chord.stash`, `chord.stashes`), so a chord can be respelled whole. A prefix that is not a modifier chord, that
188
- the surface or the prompt bar already answers (`ctrl+c`, `ctrl+s`), or that the
189
- terminal keeps (`ctrl+q`) is refused with the reason, and the shipped keymap
190
- stays in force. The chords themselves are the commands they stand for: `m`, `p`,
191
- `y`, `s`, and `l` ask the same dispatcher `/model`, `/plan`, `/copy`, `/stash`,
192
- and `/stash-list` do. Plan mode is
193
- the one pair that cannot share a name: `/plan` only enters, so the chord names
194
- `/plan off` instead when the agent is in plan mode — or is waiting for the turn
195
- boundary to become so — and reads that state from the plan package rather than
196
- from the dock.
198
+ `chord.new`, `chord.copy`, `chord.stash`, `chord.stashes`, `chord.editor`,
199
+ `chord.keys`), so a chord can be respelled whole. A prefix that is not a modifier
200
+ chord, that the surface or the prompt bar already answers (`ctrl+c`, `ctrl+s`),
201
+ or that the terminal keeps (`ctrl+q`) is refused with the reason, and the
202
+ shipped keymap stays in force. The chords themselves are the commands they stand
203
+ for: `m`, `p`, `n`, `y`, `s`, `l`, and `?` ask the same dispatcher `/model`,
204
+ `/plan`, `/new`, `/copy`, `/stash`, `/stash-list`, and `/keys` do; `e` is the
205
+ one chord with no command behind it, because it opens a program rather than
206
+ running a line. Plan mode is the one pair that cannot share a name: `/plan` only
207
+ enters, so the chord names `/plan off` instead when the agent is in plan mode —
208
+ or is waiting for the turn boundary to become so — and reads that state from the
209
+ plan package rather than from the dock.
197
210
 
198
211
  An approval or a question draws inline above the editor and takes the keyboard. A question that lists options always adds row `0. other — type your own answer`: type or paste an answer the model did not offer, and the seam receives it as that question's free text — replacing a single-select choice, or supplementing a multi-select one. `0`, or `↓` past the last option, reaches the row; `↑` walks back to the list with the text kept, and `esc` does the same from that row, because a question skipped by accident is a question answered twice — an escape from the list skips it. Free text is written in the prompt bar's own editor, drawn under that row: movement, word and line deletion, undo, completion, and multi-line paste are all the editor the reader already uses, and the prompt bar steps aside while a question is open, so a prompt written but not sent comes back untouched once the question is answered. No question hides its answer — the reader is the one who has to check what they are about to send. Every gate row wraps at the screen edge under its own label, so a long option or question is readable rather than cut.
199
212
 
200
- While a turn runs, a prompt submitted into the editor waits in the agent's own inbox instead of disappearing: it is drawn above the editor in the input bar's own frame, faint and italic, and moves into the transcript when the agent takes it — where it keeps that frame in the prompt's own mint shade, so what the reader typed is never mistaken for what the agent said. `editor.queued` and `editor.queued.more` restyle or hide the waiting rows; `transcript.user` restyles the submitted prompt.
213
+ While a turn runs, a prompt submitted into the editor waits in the agent's own inbox instead of disappearing: it is drawn above the editor in the input bar's own frame, faint and italic, and moves into the transcript when the agent takes it — where it keeps that frame in the prompt's own mint shade, so what the reader typed is never mistaken for what the agent said. Its markdown lays out inside that frame, so a list or a fence reads in the same box it was typed into. `editor.queued` and `editor.queued.more` restyle or hide the waiting rows; `transcript.user` restyles the submitted prompt. A reply is drawn in a frame of its own, so one exchange reads as two objects rather than as a box followed by a stream of rows: `transcript.assistant.border` restyles that frame, and hiding it draws the reply bare. `ctrl+c` takes them back: an interrupt drops whatever the agent has not started, so the waiting prompts are read first and put into the bar before the turn is stopped.
201
214
 
202
215
  Every submitted line is also kept in a global prompt history at
203
216
  `$DSH_HOME/prompt-history.json`. Typing the start of a prompt that was sent
@@ -222,10 +235,10 @@ Any other `/command` goes to the command registry, so `/plan`, `/compact`, `/goa
222
235
  says so, because submitting a command consumes the line it was typed on and there
223
236
  is nothing left of the draft to park. `/stash-pop` puts a parked draft back and
224
237
  removes it, so a prompt written for the wrong moment survives a restart instead
225
- of being retyped or sent; `l` opens the list of them. A stash belongs to the exact
226
- working directory, so the drafts parked in one checkout never appear in another;
227
- the footer shows `stash N` while any are waiting, ranked above the context and
228
- cache numbers it shares a row with.
238
+ of being retyped or sent; `l` opens the list of them. A stash belongs to the
239
+ session it was parked in, so a second terminal in the same checkout never sees
240
+ these drafts while resuming the session does; the footer shows `stash N` while
241
+ any are waiting, ranked above the context and cache numbers it shares a row with.
229
242
 
230
243
  A selector is the number the list shows in brackets — `0` is the newest — or the
231
244
  entry's own id; leaving it out takes the newest. `apply` and `pop` refuse to
@@ -243,7 +256,7 @@ the write is attempted. The bar is only cleared while it still holds that same
243
256
  draft and no question has borrowed it, so an answer typed during the write is
244
257
  never wiped by a stash finishing.
245
258
 
246
- The bank is one JSON file per directory under `$DSH_HOME/tui-stash`, written with
259
+ The bank is one JSON file per session under `$DSH_HOME/tui-stash`, written with
247
260
  owner-only permissions (`0700` directory, `0600` file) through a no-follow open,
248
261
  and every directory the path passes through must be owned by the reader (or by
249
262
  root) and not writable by anyone else — the sticky bit is the only exception,
@@ -254,14 +267,14 @@ directory is refused at the directory it jumped through. A directory
254
267
  that another user or a group member could redirect the storage through is refused
255
268
  rather than trusted, which is also why a group-writable home directory fails the
256
269
  stash with the offending path named. Every update is a locked read-modify-write
257
- and an atomic temp-and-rename, so two surfaces in the same directory cannot lose
258
- each other's entries; reclaiming a lock whose owner is gone is serialized on a
270
+ and an atomic temp-and-rename, so two surfaces using one bank cannot lose each
271
+ other's entries; reclaiming a lock whose owner is gone is serialized on a
259
272
  per-bank claim file, and the removal only applies to the lock it judged, so a
260
273
  holder that released in between cannot have its successor's live lock deleted. A
261
274
  contender never deletes a lock it did not publish, so losing the name to a
262
275
  successor costs a retry rather than the successor's turn.
263
276
 
264
- A bank whose working directory is not this one, or whose bytes do not parse, is
277
+ A bank whose session id is not this one, or whose bytes do not parse, is
265
278
  moved aside as `<name>.corrupt-<time>` and reported with its path — including when
266
279
  the directory holding the copy could not be synced. A bank written by a newer
267
280
  format, or one past the size cap, is refused in place rather than moved, because
@@ -274,6 +287,32 @@ Unicode bidi controls are stripped when a draft is stored and again when it is
274
287
  read, so a hand-edited bank cannot park a terminal escape or a reordering trick in
275
288
  the bar.
276
289
 
290
+ ## External editor
291
+
292
+ `ctrl+x` then `e` hands the draft to the editor the environment already names:
293
+ `$VISUAL` first, then `$EDITOR`, split on whitespace with quotes grouping and
294
+ nothing else special — no shell, no backslash escapes — so `code --wait` works
295
+ and a quoted path stays one argument. This surface cannot
296
+ draw an editor inside its own screen, so it gives the terminal up — the
297
+ alternate screen leaves, the child runs on the same tty — and takes it back
298
+ when the child exits; the frame is repainted whole, and the bar holds whatever
299
+ was saved. Nothing is submitted: a draft written for later survives a detour
300
+ through a full editor.
301
+
302
+ No shell is involved: the configured line is split here and the program is
303
+ spawned directly, because an environment value is data and a typo in it must not
304
+ become a command. The scratch file is a fresh directory per handoff, mode `0700`
305
+ with a `0600` file, removed when the editor leaves; the text read back is read
306
+ through one handle that follows no link and accepts only a plain file, so a draft
307
+ swapped for a link, a fifo, or a device is refused rather than followed, and it
308
+ is stripped of control and bidi characters, because it is going into a live
309
+ editor rather than being drawn as text. A child that exits non-zero is not a failure —
310
+ an editor that refused to save has already said so, and what it did save is what
311
+ the reader meant to keep. Nothing configured, a program that could not start, a
312
+ save that cannot be read, and a draft past 1 MiB are notices that leave the bar
313
+ as it was; the oversized draft is left on disk with its path, because a refusal
314
+ must not also be a way to lose the work.
315
+
277
316
  ## Settings
278
317
 
279
318
  Every styled element is a named token with a shipped default, and every key is
@@ -283,11 +322,17 @@ code. Preferences live in the same user-settings document as every other
283
322
 
284
323
  ```yaml
285
324
  dsh-tui:
325
+ theme: violet-orbit # restyle the whole surface by name (default: deepseek-blue)
286
326
  subcalls: collapsed # fold the calls a PTC program dispatched (default inline)
287
327
  mermaid: streaming # draw a reply's mermaid fences: off, final, or streaming (default streaming)
328
+ tools:
329
+ default: { collapsed: true, output: hidden } # how every tool's card starts
330
+ bash: { output: tail, tail: 5 } # keep the last five output rows behind bash's fold
331
+ read: { collapsed: false } # start reads open
288
332
  prefixWindow: 2 # seconds a chord waits for its second key; 0 waits for the next key instead
289
333
  keys:
290
334
  chord.prefix: ctrl+x # the key that starts a chord; "prefix:" is the older spelling of this row
335
+ chord.keys: '?' # quoted: a bare ? is a YAML indicator, not a key
291
336
  prompt.submit: [ctrl+enter, alt+enter, ctrl+s]
292
337
  surface.effort: ctrl+t # one key, or a list of them
293
338
  tui.editor.yank: ctrl+y # any action the library draws, by the id /keys prints
@@ -298,7 +343,7 @@ dsh-tui:
298
343
  palette:
299
344
  muted: '#5c5c5c' # one shade quiets every receding element
300
345
  tokens:
301
- transcript.reasoning.body:
346
+ transcript.notice:
302
347
  fg: '#7a7a7a'
303
348
  italic: true
304
349
  tool.title:
@@ -314,12 +359,27 @@ keymap on the next press, and `/theme` shows each element's effective value and
314
359
  whether it came from an override, the palette, or the default.
315
360
 
316
361
 
317
- The section is not only shades. By default every session draws the calls a PTC
318
- program dispatched under their card, and `subcalls: collapsed` starts with the
319
- card alone instead. `Ctrl+Y` toggles the same choice for the current session,
320
- and an edit to the document re-seeds it. An unknown key or value is
362
+ The section is not only shades. By default every session draws one row per call
363
+ a PTC program dispatched under its card, and `subcalls: collapsed` starts with
364
+ the card alone instead. `Ctrl+Y` toggles the same choice for the current
365
+ session, and an edit to the document re-seeds it. An unknown key or value is
321
366
  refused with a notice naming it, so a typo cannot quietly do nothing.
322
367
 
368
+ The `tools` block decides how each tool's cards draw. `collapsed` starts a
369
+ tool folded to one header row (default `true`), and `output` is `hidden`
370
+ (default) or `tail`, where `tail` is how many output rows a folded card keeps
371
+ (default `20`). A folded row ends a few columns short of the screen edge and
372
+ the argument is what gives up that room: a wide terminal shows more of the call,
373
+ a narrow one still shows the tool, how it ended, and how much waits behind the
374
+ fold. The reserved `default` row applies to every tool without its own, and a
375
+ tool name nothing declares is inert: the surface cannot know which tools a
376
+ profile mounts. Clicking a card opens or folds that one message, and a dispatched
377
+ call's row opens on the same click to what the tool itself drew for it — an
378
+ edit's diff in the diff colours, a read's numbered lines, a command and its
379
+ output — followed by the outcome it produced; a call that failed opens to the
380
+ reason it reported instead of rows for work that never happened. `Ctrl+O` still
381
+ decides for every message nobody clicked.
382
+
323
383
  The `history` block tunes the prompt history. `enabled: false` stops recording
324
384
  and offering it; `ghost: false` keeps reverse search but stops the dimmed
325
385
  completion; `maxEntries` bounds the file, and an exact repeat moves to the
@@ -340,14 +400,81 @@ restyleable like anything else through `markdown.diagram.border`,
340
400
  lists it with the rest. Nothing is lost by drawing: `/export` and the session
341
401
  file keep the reply exactly as the model wrote it.
342
402
 
343
- `fg` and `bg` accept `#rrggbb`, a palette name (`default`, `muted`, `accent`,
344
- `arg`, `warn`, `added`, `removed`, `user`, `assistant`), or an index. A colour is
403
+ A fenced block whose language is `diff` or `patch` is drawn as the change it
404
+ describes rather than as one plain code block: file headers and hunk headers
405
+ recede, added rows draw green, removed rows draw red, and a row that replaced
406
+ another puts the characters that actually changed on a darker band of its own
407
+ colour, so a one-word edit reads at a glance instead of as two unrelated lines.
408
+ A pair that shares too little to be an edit draws whole-row, unchanged rows keep
409
+ the shade a code block always had, and any other language draws exactly as
410
+ before. The change is drawn in replies, submitted prompts, and thoughts alike,
411
+ because red and green say what the fence means rather than how loudly it is
412
+ drawn. The seven elements — `markdown.diff.header`, `.hunk`, `.context`,
413
+ `.added`, `.removed`, and the `.addedEmphasis` and `.removedEmphasis` bands
414
+ — are named by every shipped theme and overridden like any other, `hidden`
415
+ included; hiding an emphasis element keeps the row's own colour instead of
416
+ leaving a gap, and `NO_COLOR` draws the fence as plain text. `/export` and the
417
+ session file still keep the fence exactly as the model wrote it.
418
+
419
+ A theme restyles the whole surface by name, and a theme is a file. The package
420
+ ships two: `deepseek-blue`, the table written out in full in the colours the
421
+ project answers to, and `violet-orbit`, a port of pi's theme of that name — its
422
+ palette, plus the elements it draws its own way. `deepseek-blue` is also what a
423
+ document naming no theme draws, so the default look is a file you can read, list,
424
+ and copy rather than a table compiled in. Your own themes live in
425
+ `$DSH_HOME/themes/`, which the surface creates at start-up and watches, so saving
426
+ a file there is how you change the surface you are looking at. A bare `/theme`
427
+ opens the list of them, narrowing as you type, and the screen paints the row under
428
+ the cursor as it moves: two themes are compared on your own transcript, and nothing
429
+ is written until one is taken, so leaving the list puts back the theme that was in
430
+ force. `theme: violet-orbit` applies one from the document, and a name nothing
431
+ answers to is reported with the names that do, drawing the default while you fix it.
432
+
433
+ Both files name every element and every palette entry, so a copy of one is a
434
+ complete theme rather than a diff against something you cannot see.
435
+ `deepseek-blue` is the one to copy to move a single shade, because every element
436
+ follows one of its ten palette entries, each taken from DeepSeek's own design
437
+ tokens with the token named beside it — and the accent is one line.
438
+ `/theme export <built-in>` writes that copy into your own directory as
439
+ `<built-in>_export_<n>.yaml`, adding one comment naming the release it came from:
440
+ the package's own file is replaced whenever the package updates, so the copy is the
441
+ only one worth editing. A file whose name is a built-in's is ignored, and reported
442
+ at start-up with the rename that fixes it.
443
+
444
+ A theme is a layer and not a replacement: everything it says nothing about keeps
445
+ its shipped appearance, and a `tokens:` entry of your own still wins over it one
446
+ field at a time, so naming a single attribute does not discard the shade the theme
447
+ gave that same element. `/theme tokens` names the theme in force in its heading and
448
+ reports each element as `override`, `theme`, `palette`, or `default`, marking the
449
+ themes in your own directory and printing the export hint, so a screen that looks
450
+ wrong can be traced to the layer that drew it.
451
+
452
+ A fenced block whose language is `diff` or `patch` is drawn as the change it
453
+ describes rather than as one plain code block: file headers and hunk headers
454
+ recede, added rows draw green, removed rows draw red, and a row that replaced
455
+ another puts the characters that actually changed on a darker band of its own
456
+ colour, so a one-word edit reads at a glance instead of as two unrelated lines.
457
+ A pair that shares too little to be an edit draws whole-row, unchanged rows keep
458
+ the shade a code block always had, and any other language draws exactly as
459
+ before. The change is drawn in replies, submitted prompts, and thoughts alike,
460
+ because red and green say what the fence means rather than how loudly it is
461
+ drawn. The seven elements — `markdown.diff.header`, `.hunk`, `.context`,
462
+ `.added`, `.removed`, and the `.addedEmphasis` and `.removedEmphasis` bands
463
+ — are overridden like any other, `hidden` included; hiding an emphasis element
464
+ keeps the row's own colour instead of leaving a gap, and `NO_COLOR` draws the
465
+ fence as plain text. `/export` and the session file still keep the fence exactly
466
+ as the model wrote it.
467
+
468
+ `fg` and `bg` accept `#rrggbb`, a palette name (`default`, `muted`, `faint`,
469
+ `accent`, `arg`, `warn`, `added`, `removed`, `user`, `assistant`), or an index. A colour is
345
470
  emitted as 24-bit when the terminal advertises it (`COLORTERM`) and degraded to
346
471
  the nearest 256-colour entry or 16-colour slot otherwise; a hue keeps its family
347
472
  there, so an addition stays green instead of collapsing to black. Muted elements
348
473
  name the palette rather than a terminal slot, so on anything but a 16-colour
349
474
  terminal their contrast does not depend on what the reader's colour scheme maps
350
- slot 8 to. `arg` is the pale blue a card gives the argument it was called with,
475
+ slot 8 to. `faint` is the shade below `muted`: a thought and the row naming it both take
476
+ it, and only the row is italic, so the signpost does not compete with the text
477
+ it introduces. `arg` is the pale blue a card gives the argument it was called with,
351
478
  so `tool.args` is restyled on its own and stays distinct from the tool's own
352
479
  label and from its output. `user` is the mint a submitted prompt takes, so a
353
480
  reader's own turns stand apart from the reply without reading either.
@@ -357,22 +484,57 @@ outrank everything in this section. A token or palette name the surface does not
357
484
  have is refused with the offending name, and the surface prints the refusal as a
358
485
  notice when the document loads, so a typo cannot quietly paint nothing.
359
486
 
487
+ ### Terminal text
488
+
489
+ A tool result, a file's contents, and a model's answer are text a terminal may
490
+ read as commands, so the surface reads them first. A whitelisted subset of the
491
+ SGR family (`1`, `2`, `3`, `4`, `7`, `9`, `21`/`22`, `23`, `24`, `27`, `29`, the
492
+ 30–37/90–97 and 40–47/100–107 slots, `38`/`48` indexed and RGB, `39`/`49`, and
493
+ `0`) is re-emitted at the session's own colour budget: 24-bit where the terminal
494
+ advertises it, 256 or 16 colours otherwise, and nothing at all with `--no-color`
495
+ or `NO_COLOR`. A tab advances to the next eight-column stop measured from the
496
+ column the text starts at, and a carriage return repaints its row in place, so
497
+ the last state of a progress bar is the only one drawn.
498
+
499
+ Everything else a terminal would act on — cursor movement, screen clearing,
500
+ private modes, window titles, clipboard writes, hyperlinks — is consumed rather
501
+ than shown, and a control byte that is not a sequence is spelled out (`\x07`)
502
+ rather than silently dropped. A full reset inside tool output restores the colour
503
+ of the element holding the text, not the terminal default, and the surface never
504
+ writes a reset of its own inside a row; a carriage return cannot repaint past the
505
+ column the text started at, so indented output cannot reach the frame around it.
506
+
507
+ Text the surface draws itself — a ghost suggestion, a completion row, a queued
508
+ prompt, an export — is drawn without colour, because the surface is already
509
+ painting it and a second style would fight the first.
510
+
360
511
  ### Keys
361
512
 
362
513
  Every press the surface answers is an action with an id and a shipped key.
363
- `/keys` prints all of them with the keys in force, marks the rows you wrote,
364
- names the keys your map took from the library, and ends with a sample `keys:`
365
- section rather than a whole one; the table above it is the complete account.
366
- `/keys prompt`, `surface`, `chord`, `gate`, `question`,
367
- `picker`, and `library` narrow that list to one part of the surface.
514
+ `/keys`, or `Ctrl+X` then `?`, opens the whole map in a box over the
515
+ transcript: one row per action with the keys in force, one row for every key your
516
+ map took from the library, and a filter over all of it — `gate` for a layer,
517
+ `ctrl+o` for a key, `stash` for what a row does. The heading counts the actions
518
+ shown and how many of them you wrote, the box gives up rows rather than grow past
519
+ four fifths of the screen, and `enter` or `esc` closes it with the transcript
520
+ exactly as it was. `/keys prompt`, `surface`, `chord`, `gate`, `question`,
521
+ `picker`, and `library` open it already narrowed to one part of the surface, and
522
+ a name that is none of them is refused with the names. The table above is the
523
+ complete account of the shipped keys.
368
524
 
369
525
  An entry is one key or a list of them. A key is a modifier chord
370
526
  (`ctrl`/`alt`/`shift` joined by `+`, written in that order), a named key
371
527
  (`enter`, `escape`, `tab`, `space`, `backspace`, `delete`, `home`, `end`,
372
528
  `pageUp`, `pageDown`, the arrows, `f1`–`f12`), or a bare character where the
373
- layer reads one: `y` and `n` for an approval, or a chord's second key. Ids
374
- beginning `tui.` are pi-tui's own actions, so the editor, the search, and the
375
- transcript move where you tell them to.
529
+ layer reads one: `y` and `n` for an approval, or a chord's second key (`?` for
530
+ the key map, `m` for the model). Ids beginning `tui.` are pi-tui's own actions,
531
+ so the editor, the search, and the transcript move where you tell them to.
532
+
533
+ Ctrl+P and Ctrl+N ship as alternatives to ↑ and ↓ wherever a list moves — a
534
+ picker, a question's options, and the editor's completion menu. They are
535
+ ordinary rows: `picker.up`, `picker.down`, `question.up`, `question.down`,
536
+ and the library's `tui.select.up`/`tui.select.down` take other keys, or more
537
+ of them, like any other row.
376
538
 
377
539
  Refused, with the reason in a notice and the shipped map left in force: an
378
540
  action the surface does not have, a key no terminal reports, `ctrl+q` (the
@@ -392,9 +554,12 @@ control byte carries both `ctrl+-` and `ctrl+_`, and an escape with a letter
392
554
  reaches `alt+up` as readily as `alt+p`.
393
555
 
394
556
  A key the surface or a chord answers is a key the library never sees: that is
395
- how `ctrl+y` shows nested calls instead of yanking a line in the editor.
396
- `/keys` names every shadow your map introduces, and moving the surface key
397
- hands the library its own key back.
557
+ how `ctrl+y` shows nested calls instead of yanking a line in the editor, how
558
+ `ctrl+c` closes the transcript search the library owns, and how `ctrl+d` leaves
559
+ rather than deleting forward while the bar holds nothing. The key map carries a
560
+ row for every shadow your map introduces, naming the action that wins and the
561
+ library row that loses, and moving the surface key hands the library its own key
562
+ back.
398
563
 
399
564
  Left alone, because they are typing rather than commands: the keys a question's
400
565
  filter narrows with and the ones that leave its free-text row, the digits and
@@ -442,9 +607,9 @@ A question whose id ends in `:secret` declares its typed answer a credential: th
442
607
 
443
608
  The fold is durable-only: the live stream decorates the row that is still being written, and everything else — cards, reasoning, work state, compaction markers — comes from the log, so a resumed session renders what the live one did. Subagent start and finish are the exception: they arrive as service events, and the transcript shows them as decoration because the durable record of a delegation is the tool call that asked for it.
444
609
 
445
- Tool cards are folded by default: a card draws its header and nothing else, so a long read, diff, or search cannot bury the conversation. A shell card is the exception, because its output is the answer the reader asked for: it names the tool in its header, always shows the command that ran, keeps the last 20 output rows, and adds a hint naming the rows it dropped. `Ctrl+O` opens every card to its header plus every retained row.
610
+ Tool cards are folded by default: a card draws one header row — the tool, its argument clipped to the configured budget, and the facts the result measured — so a long read, diff, or search cannot bury the conversation. A shell card's row also carries the exit status and the count of output rows waiting behind the fold, because its output is the answer the reader asked for and a fold that left no trace of it would read as a call that produced nothing. Clicking a card opens or folds that one message; `Ctrl+O` opens or folds every card at once, and `tools:` in the [settings](#settings) decides how each tool starts and whether a fold hides its rows or keeps a `tail` of them.
446
611
 
447
- A PTC card is the one card with children: every call the `run_code` program dispatched hangs off the card that made it, and each draws under the header as the tool's own name and argument, wrapping rather than being cut. `Ctrl+Y` folds them away again, and `subcalls: collapsed` starts every session folded; see [Settings](#settings).
612
+ A PTC card is the one card with children: every call the `run_code` program dispatched hangs off the card that made it, and each draws under the header as the tool's own name and argument, on one row cut at the screen edge whether the card itself is open or folded — a program's work must stay legible without opening its card. Clicking one of those rows opens that call's argument in full and leaves its neighbours and the card as they were; a shell call also brings back the rows it printed, because the program's return value is all the card itself keeps. `Ctrl+Y` hides or shows them all, and `subcalls: collapsed` starts every session with them hidden; see [Settings](#settings).
448
613
 
449
614
  A card's header names the tool, then the argument the call was made with — a path or a command — in the `tool.args` colour, then the facts the result measured: a read reports its line range, line count, and token size; a file change that carried no prior content to compare against reports its lines and tokens; one that did reports added, changed, and removed lines as `+n ~n -n` in green, yellow, and red. Each stat is its own token, so any of them can be recoloured or hidden independently.
450
615
 
@@ -534,13 +699,24 @@ The automated checks drive a real PTY, but they run on this machine's terminal.
534
699
  | `NO_COLOR=1 dsh --profile tui` | no styling anywhere, layout unchanged |
535
700
  | `dsh --profile tui --no-bell` | a turn that runs for minutes still ends silently |
536
701
  | `dsh --profile tui --preset ptc`, then a turn | the status line names `ptc`, and the agent reaches its tools through one TypeScript program rather than one shell call at a time |
537
- | a PTC turn | one entry per dispatched call draws two spaces indented under the `run_code` header, from the first frame and with no keypress |
538
- | that turn in a terminal narrower than a call's own argument | the argument wraps onto continuation rows indented to the same two spaces, with no ellipsis |
539
- | that same turn, then `ctrl+y` | the lines fold away; `ctrl+y` again draws them back |
540
- | `dsh-tui: { subcalls: collapsed }` in `$DSH_HOME/settings.yaml`, then a PTC turn | the card arrives alone, and editing the document to `inline` unfolds a running session |
702
+ | a PTC turn | one row per dispatched call draws two spaces indented under the `run_code` header without opening it |
703
+ | that turn, then a click on one of those rows | that call unfolds in full at the same indent: its argument, then what the tool itself drew — an edit's diff in the diff colours, a read's lines, a shell's output — and its neighbours and the card stay as they were |
704
+ | a PTC turn in which a dispatched call failed, then a click on that red row | the row opens to the reason the call reported, and a click on those rows folds it back |
705
+ | that same turn, then `ctrl+y` | the rows fold away; `ctrl+y` again draws them back |
706
+ | `dsh-tui: { subcalls: collapsed }` in `$DSH_HOME/settings.yaml`, then a PTC turn | the card arrives alone, and editing the document to `inline` draws the one-line calls in a running session |
707
+ | a thought, folded | a click on the row opens the thought under its summary; a click on the body folds it back, and the other thoughts keep their own state |
708
+ | a bash card, then a click on it | the row opens to its command, its retained output, and its exit status; a click folds it back to one row |
709
+ | a call that failed, then a click on its card | the card opens to the reason it failed — the same words the model was shown — and a click folds it back |
710
+ | `dsh-tui: { tools: { bash: { output: tail, tail: 5 } } }`, then a bash run | the folded row keeps the last five output rows and counts the rest |
711
+ | `dsh-tui: { tools: { read: { collapsed: false } } }`, then a read | the card starts open; folded on a narrow terminal, the path gives up room first and the row stops short of the edge |
712
+ | `dsh-tui: { tools: { nope: { collapsed: false } } }` | accepted and inert, because the surface cannot know which tools a profile mounts |
713
+ | `dsh-tui: { tools: { bash: { collapse: true } } }` | refused with a notice naming `bash.collapse` |
541
714
  | a reply carrying a mermaid fence | it draws as box art at the transcript width, with the prose around it untouched |
542
715
  | that reply in a terminal narrower than the drawing | the fence stays source, and widening the window draws it without a new turn |
543
716
  | `dsh-tui: { mermaid: off }` in `$DSH_HOME/settings.yaml`, then a mermaid reply | the fence stays source; editing the value to `streaming` draws a settled reply without a restart |
717
+ | a reply carrying a `` ```diff `` fence | the file and hunk headers recede, `+` rows draw green and `-` rows red, and the characters that changed in a paired row sit on a darker band |
718
+ | `dsh-tui: { tokens: { markdown.diff.addedEmphasis: { hidden: true } } }` in `$DSH_HOME/settings.yaml`, then that reply | the changed run keeps its row's colour instead of the band, and the row's text is unchanged |
719
+ | `NO_COLOR=1 dsh --profile tui`, then that reply | the fence draws as plain text with no escape sequences, and a re-run without it colours the fence again |
544
720
  | type the start of a prompt already recorded | the rest of the newest match follows the cursor in a faint shade; `ctrl+e` takes it whole, the word-right key takes one word, and both keys do their old job when nothing is offered |
545
721
  | `ctrl+r`, then a fragment | reverse search opens seeded with the bar's draft; `enter` puts a prompt back, `esc` keeps the draft |
546
722
  | `dsh-tui: { history: { ghost: false } }`, then type a known prefix | no suggestion is drawn, and `ctrl+r` still searches |
@@ -551,6 +727,7 @@ The automated checks drive a real PTY, but they run on this machine's terminal.
551
727
  | `--resume --preset <mode>` and then picking a session that runs another mode | the list stays open and says why that row cannot be taken; `esc` leaves the picker |
552
728
  | `dsh --profile tui --preset nope` | exits non-zero naming the modes that do exist, before the alternate screen appears |
553
729
  | arrow keys in a picker, or on a question's options, in a terminal that reports key events (Kitty, WezTerm, Ghostty, iTerm2) | one press moves one row, and holding a key still repeats; a terminal that sends only the legacy sequence behaves the same |
730
+ | Ctrl+N / Ctrl+P in a picker, on a question's options, on its `0. other` row, or in the completion menu | the cursor moves down and up exactly as the arrows do |
554
731
  | resize the window mid-turn | the transcript rewraps; the dock, editor, and status row stay put |
555
732
  | a 40-column terminal | transcript and card rows end in `…` instead of wrapping into the next line |
556
733
  | a question with a long option at 40 columns | the option wraps onto rows indented under its label, and `0. other — type your own answer` sits under the list |
@@ -558,10 +735,12 @@ The automated checks drive a real PTY, but they run on this machine's terminal.
558
735
  | type a prompt without sending it, then answer a question | the prompt bar steps aside while the question is open and holds the same prompt again afterwards |
559
736
  | `echo hi \| dsh --profile tui` | refuses with a non-zero exit and a message naming the TTY requirement |
560
737
  | `/stash`, `/stash-pop` in one terminal | the footer shows `stash 1` after the stash and the draft returns to the editor after the pop |
561
- | a second `dsh --profile tui` in the same directory | `/stash-list` shows the draft the first terminal parked |
562
- | `dsh --profile tui` in another directory | `/stash-list` says `no stashed drafts`, even though the first directory still has one |
738
+ | a second `dsh --profile tui` in the same directory | `/stash-list` says `no stashed drafts`, and the first terminal's bank is untouched |
739
+ | `/quit`, then `dsh --profile tui --resume=<id>` | `/stash-list` still shows the draft that session parked |
563
740
  | hand-edit `$DSH_HOME/tui-stash/<key>.json` into invalid JSON, then `/stash-list` | the surface reports the quarantine path, starts empty, and leaves the moved file readable |
564
- | `/quit`, Ctrl+C while idle, `kill -TERM <pid>` | the shell returns with cursor, echo, mouse, and title restored |
741
+ | `ctrl+x` then `e` with `$VISUAL` set to your editor | the alternate screen gives way to that editor with the draft in it; saving returns to the same frame with what was saved in the bar, and nothing is submitted |
742
+ | the same with `$VISUAL` and `$EDITOR` unset | the draft stays in the bar, and a notice names the variables to set |
743
+ | `/quit`, Ctrl+D with an empty bar, `kill -TERM <pid>` | the shell returns with cursor, echo, mouse, and title restored |
565
744
 
566
745
  ## Releasing
567
746
 
@@ -584,13 +763,36 @@ The workflow stores no npm token: the registry trusts `release.yml` on the `npm-
584
763
  - A card reads its tool's own render intent through the agent whose session is on screen, so a stored session with no live agent — one this process is not running, or a child that has already finished — folds to the generic card instead of the tool's own.
585
764
  - Reading a child's conversation does not move the terminal: commands, approvals, and the status line stay with the session you launched, and the transcript is the only thing that switches. The status line carries the way back, read from the map in force, so a remap shows up without reopening the view.
586
765
  - Delete is unimplemented: the session store exposes no delete, and the surface does not reach around that seam into its files. `/fork` covers the case that needs it — it branches into a new session and leaves the original alone.
587
- - The prompt stash holds text only. It does not read Pi's `pi-stash` data, does not migrate an older key format, and keeps no pasted images: a draft larger than 1 MiB, or a bank larger than 16 MiB, is refused rather than stored — the cap is measured on the bytes the file will hold, escapes included, before anything is written, so a refusal cannot leave a bank that saves and then refuses to load. A corrupt bank is quarantined and reported, never repaired in place; a bank from a newer format is refused where it lies, so an older build cannot swallow a newer build's drafts.
766
+ - The prompt stash holds text only. It does not read Pi's `pi-stash` data, does not migrate an older key format (the directory-scoped banks written by 0.4.0 are left where they are and are never read), and keeps no pasted images: a draft larger than 1 MiB, or a bank larger than 16 MiB, is refused rather than stored — the cap is measured on the bytes the file will hold, escapes included, before anything is written, so a refusal cannot leave a bank that saves and then refuses to load. A corrupt bank is quarantined and reported, never repaired in place; a bank from a newer format is refused where it lies, so an older build cannot swallow a newer build's drafts.
588
767
  - Approvals and questions render inline and take the keyboard; a question batch is answered in order, and a question that lists options can always be answered with free text on row `0`.
589
768
  - Inside Herdr the pane reports its own state, and that report is the only thing that makes it an agent there: Herdr cannot start, resume, or prompt this surface, so launching and resuming stay with `dsh` itself (or a Herdr plugin that runs it).
590
769
  - Styling is per element and overridable; see [Settings](#settings). Shipped defaults are emitted as 24-bit colour where the terminal advertises it and degraded to 256 or 16 colours otherwise, so a light or dark terminal still follows its own palette where it has one.
591
- - Tool text, model text, and file content are escaped before rendering, so a hostile result cannot inject terminal control sequences; the cost is that a literal tab shows as \x09. A stashed draft is stripped of control and bidi characters instead, because it is restored into a live editor rather than drawn as text.
592
- - Mermaid fences draw in assistant replies only, and only at the top level of one: a fence nested in a list, quoted inside another fence, or carried by a prompt, a thought, or a tool card stays source. Author `:::class` styling and diagram links are ignored — the renderer reports what each run is, and the theme decides how it looks.
770
+ - Tool text, model text, and file content are drawn the way the terminal that produced them would have drawn them — see [Terminal text](#terminal-text) — so a tab lands where its writer saw it and a colour is a colour. A hostile result still cannot reach the terminal: everything a terminal would act on is consumed before the row is measured. A stashed draft and a stored prompt are stripped of control and bidi characters instead, because they are restored into a live editor rather than drawn as text.
771
+ - Mermaid fences draw in assistant replies and submitted prompts, and only at the top level of one: a fence nested in a list, quoted inside another fence, or carried by a thought or a tool card stays source. A thought never draws one, because a diagram there would carry the answer's weight. A fenced diff is the exception: it is the change itself rather than a drawing of it, so replies, prompts, and thoughts all draw it in the diff elements. Author `:::class` styling and diagram links are ignored — the renderer reports what each run is, and the theme decides how it looks.
593
772
  - Prompt history is global to this machine, not per project: `$DSH_HOME/prompt-history.json` holds every submitted line, deduplicated exactly, and a file this build cannot parse is left untouched with writes refused so a newer format is never overwritten. Every write re-reads the file under a lock shared by sessions, so a second session's prompts are folded in rather than overwritten, and a lock whose holder stopped is reclaimed or reported instead of guessed at; control characters are spelled out before a prompt is stored. A multiline suggestion draws its first line with `↵` marking the fold. `history.ghost: false` keeps reverse search without the suggestion, `history.enabled: false` stops recording and offering it, and `NO_COLOR`/`--no-color` suppresses the ghost because text the reader cannot see but could still accept is worse than none.
773
+ - The editor handoff gives the whole terminal to `$VISUAL` (or `$EDITOR`) and waits for it: while the child owns the screen this surface draws nothing: a title from a turn in flight is written again when the screen comes back, a bell that falls in the gap is dropped rather than rung late, and a second `ctrl+x` then `e` is ignored until the first editor leaves. A host that unloads the surface during the handoff gives the terminal back while the child is still running, because only the child's own exit can end the wait. What the editor saved is read back only up to 1 MiB; a larger draft is left on disk with its path in the notice rather than loaded into the bar.
774
+
775
+ ## Related plugins
776
+
777
+ Two companion bundles stack onto the same profile and complement this surface.
778
+ Neither is a dependency of this package: a profile works without them, and each
779
+ publishes to npm under the same tag-driven, provenance-carrying release
780
+ discipline as this one.
781
+
782
+ | Plugin | What it adds |
783
+ |---|---|
784
+ | [`@sagmans/dsh-auto-compact`](https://github.com/sagmans/dsh-auto-compact) | An absolute token trigger for automatic compaction: the conversation condenses at `min(thresholdTokens, contextWindow × thresholdRatio)` instead of the window ratio alone, so a large-window model pays a fixed price, with per-route overrides. Its patch swaps the shipped `compaction-basic` backend — the same row this bundle already takes out of the global composition — so the profile still keeps exactly one compaction service, and this surface needs no setting for it. |
785
+ | [`@sagmans/dsh-provider-extra`](https://github.com/sagmans/dsh-provider-extra) | Extra provider routes: OpenCode Go, which sends the live conversation id in `x-opencode-session` for routing and prompt caching, and OpenAI Codex over a ChatGPT subscription's OAuth flow with the harness credential store. Its routes join the `/model` picker like every configured provider. |
786
+
787
+ ```sh
788
+ dsh plugin --profile tui add @sagmans/dsh-auto-compact
789
+ dsh plugin --profile tui add @sagmans/dsh-provider-extra
790
+ ```
791
+
792
+ The base stack this bundle itself mounts — `@deepseek-ai/dsh-base`, the agent
793
+ presets, and the host machinery PTC and creator modes need — is named in
794
+ [How it works](#how-it-works); the multiplexer this surface reports to is
795
+ [Herdr](#herdr).
594
796
 
595
797
  ## License
596
798
 
@@ -67,7 +67,7 @@ export function createToolPresenter(ctx, scope) {
67
67
  try {
68
68
  const view = definition(name)?.presentResult?.(parseArguments(input.argumentsJson), { content: toolResultContent(input.content), isError: input.isError, meta: input.meta });
69
69
  return cardOfResult(view, {
70
- fallbackTitle: name,
70
+ name,
71
71
  failed: input.isError,
72
72
  contentLines: contentLines(input.content),
73
73
  });
@@ -1 +1 @@
1
- {"version":3,"file":"present.js","sourceRoot":"","sources":["../../src/agent/present.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,GAGb,MAAM,aAAa,CAAA;AAEpB,SAAS,cAAc,CAAC,aAAqB;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAY,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,OAAgB;IACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAA;IAC3C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,SAAQ;QACzD,MAAM,MAAM,GAAG,KAAgC,CAAA;QAC/C,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,OAAO,MAAM,CAAC,OAAO,CAAA;IAC3F,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAY,EAAE,KAA+B;IAC/E,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC,CAAA;IACD,OAAO;QACL,IAAI,CAAC,IAAI,EAAE,aAAa;YACtB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,cAAc,CAAC,aAAa,CAAU,CAAC,CAAA;gBACpF,wEAAwE;gBACxE,wEAAwE;gBACxE,sEAAsE;gBACtE,yCAAyC;gBACzC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAChE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAA;YAClB,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,KAAsB;YACjC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,CAC5C,cAAc,CAAC,KAAK,CAAC,aAAa,CAAU,EAC5C,EAAE,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAW,CACjG,CAAA;gBACD,OAAO,YAAY,CAAC,IAAI,EAAE;oBACxB,aAAa,EAAE,IAAI;oBACnB,MAAM,EAAE,KAAK,CAAC,OAAO;oBACrB,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC;iBAC1C,CAAC,CAAA;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAA;YAClB,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"present.js","sourceRoot":"","sources":["../../src/agent/present.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,GAGb,MAAM,aAAa,CAAA;AAEpB,SAAS,cAAc,CAAC,aAAqB;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAY,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,OAAgB;IACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAA;IAC3C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,SAAQ;QACzD,MAAM,MAAM,GAAG,KAAgC,CAAA;QAC/C,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,OAAO,MAAM,CAAC,OAAO,CAAA;IAC3F,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAY,EAAE,KAA+B;IAC/E,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC,CAAA;IACD,OAAO;QACL,IAAI,CAAC,IAAI,EAAE,aAAa;YACtB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,cAAc,CAAC,aAAa,CAAU,CAAC,CAAA;gBACpF,wEAAwE;gBACxE,wEAAwE;gBACxE,sEAAsE;gBACtE,yCAAyC;gBACzC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAChE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAA;YAClB,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,KAAsB;YACjC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,CAC5C,cAAc,CAAC,KAAK,CAAC,aAAa,CAAU,EAC5C,EAAE,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAW,CACjG,CAAA;gBACD,OAAO,YAAY,CAAC,IAAI,EAAE;oBACxB,IAAI;oBACJ,MAAM,EAAE,KAAK,CAAC,OAAO;oBACrB,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC;iBAC1C,CAAC,CAAA;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAA;YAClB,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-history.d.ts","sourceRoot":"","sources":["../../src/agent/prompt-history.ts"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AAEH,mFAAmF;AACnF,eAAO,MAAM,YAAY,aAAa,CAAA;AACtC,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,SAAS,CAAA;AACvC,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,wBAAwB,CAAA;AACtD,0FAA0F;AAC1F,eAAO,MAAM,sBAAsB,IAAI,CAAA;AACvC,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,OAAO,CAAA;AACvC,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAmBvC,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,oBAAoB,CAAA;AAShG,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAA;CACzC;AAED,uDAAuD;AACvD,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAErE,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,OAAO,IAAI,SAAS,WAAW,EAAE,CAAA;IACjC,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,mDAAmD;IACnD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IACxB,uFAAuF;IACvF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,oEAAoE;IACpE,IAAI,IAAI,MAAM,CAAA;IACd,uEAAuE;IACvE,aAAa,IAAI,kBAAkB,GAAG,SAAS,CAAA;CAChD;AAED,sEAAsE;AACtE,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAA;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAuJD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAM3E;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAiBhE;AAuBD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,WAAW,EAAE,CAMf;AAMD,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAsGhF"}
1
+ {"version":3,"file":"prompt-history.d.ts","sourceRoot":"","sources":["../../src/agent/prompt-history.ts"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AAEH,mFAAmF;AACnF,eAAO,MAAM,YAAY,aAAa,CAAA;AACtC,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,SAAS,CAAA;AACvC,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,wBAAwB,CAAA;AACtD,0FAA0F;AAC1F,eAAO,MAAM,sBAAsB,IAAI,CAAA;AACvC,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,OAAO,CAAA;AACvC,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAmBvC,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,oBAAoB,CAAA;AAShG,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAA;CACzC;AAED,uDAAuD;AACvD,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAErE,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,OAAO,IAAI,SAAS,WAAW,EAAE,CAAA;IACjC,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,mDAAmD;IACnD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IACxB,uFAAuF;IACvF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,oEAAoE;IACpE,IAAI,IAAI,MAAM,CAAA;IACd,uEAAuE;IACvE,aAAa,IAAI,kBAAkB,GAAG,SAAS,CAAA;CAChD;AAED,sEAAsE;AACtE,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAA;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAuJD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAM3E;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAiBhE;AAyBD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,WAAW,EAAE,CAMf;AAMD,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAsGhF"}