@sagmans/dsh-tui 0.5.2 → 0.6.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 (71) hide show
  1. package/README.md +32 -12
  2. package/lib/agent/host.d.ts +11 -0
  3. package/lib/agent/host.d.ts.map +1 -1
  4. package/lib/agent/host.js +16 -2
  5. package/lib/agent/host.js.map +1 -1
  6. package/lib/agent/undo.d.ts +52 -0
  7. package/lib/agent/undo.d.ts.map +1 -0
  8. package/lib/agent/undo.js +106 -0
  9. package/lib/agent/undo.js.map +1 -0
  10. package/lib/cards.d.ts +57 -2
  11. package/lib/cards.d.ts.map +1 -1
  12. package/lib/cards.js +38 -8
  13. package/lib/cards.js.map +1 -1
  14. package/lib/herdr/reporter.d.ts +9 -4
  15. package/lib/herdr/reporter.d.ts.map +1 -1
  16. package/lib/herdr/reporter.js +5 -9
  17. package/lib/herdr/reporter.js.map +1 -1
  18. package/lib/herdr/state.d.ts +35 -4
  19. package/lib/herdr/state.d.ts.map +1 -1
  20. package/lib/herdr/state.js +37 -4
  21. package/lib/herdr/state.js.map +1 -1
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +349 -14
  24. package/lib/index.js.map +1 -1
  25. package/lib/input/actions.d.ts.map +1 -1
  26. package/lib/input/actions.js +4 -0
  27. package/lib/input/actions.js.map +1 -1
  28. package/lib/input/completion.d.ts +10 -0
  29. package/lib/input/completion.d.ts.map +1 -1
  30. package/lib/input/completion.js +15 -0
  31. package/lib/input/completion.js.map +1 -1
  32. package/lib/input/keymap.d.ts.map +1 -1
  33. package/lib/input/keymap.js +2 -0
  34. package/lib/input/keymap.js.map +1 -1
  35. package/lib/input/submission.d.ts +11 -1
  36. package/lib/input/submission.d.ts.map +1 -1
  37. package/lib/input/submission.js +10 -1
  38. package/lib/input/submission.js.map +1 -1
  39. package/lib/terminal/warning-screen.d.ts +8 -1
  40. package/lib/terminal/warning-screen.d.ts.map +1 -1
  41. package/lib/terminal/warning-screen.js +9 -0
  42. package/lib/terminal/warning-screen.js.map +1 -1
  43. package/lib/theme-schema.d.ts +6 -6
  44. package/lib/theme-settings.d.ts +8 -8
  45. package/lib/theme-tokens.d.ts +1 -1
  46. package/lib/theme-tokens.d.ts.map +1 -1
  47. package/lib/theme-tokens.js +27 -1
  48. package/lib/theme-tokens.js.map +1 -1
  49. package/lib/transcript.d.ts +50 -14
  50. package/lib/transcript.d.ts.map +1 -1
  51. package/lib/transcript.js +82 -47
  52. package/lib/transcript.js.map +1 -1
  53. package/lib/ui/copy.d.ts +17 -0
  54. package/lib/ui/copy.d.ts.map +1 -0
  55. package/lib/ui/copy.js +104 -0
  56. package/lib/ui/copy.js.map +1 -0
  57. package/lib/ui/dock.d.ts +21 -1
  58. package/lib/ui/dock.d.ts.map +1 -1
  59. package/lib/ui/dock.js +75 -16
  60. package/lib/ui/dock.js.map +1 -1
  61. package/lib/ui/frame.d.ts +31 -0
  62. package/lib/ui/frame.d.ts.map +1 -1
  63. package/lib/ui/frame.js +27 -4
  64. package/lib/ui/frame.js.map +1 -1
  65. package/lib/ui/view.d.ts +73 -2
  66. package/lib/ui/view.d.ts.map +1 -1
  67. package/lib/ui/view.js +180 -20
  68. package/lib/ui/view.js.map +1 -1
  69. package/package.json +1 -1
  70. package/themes/deepseek-blue.yaml +9 -0
  71. package/themes/violet-orbit.yaml +7 -0
package/README.md CHANGED
@@ -140,6 +140,8 @@ force, and the `keys:` section moves any of them — see [Keys](#keys).
140
140
  | Ctrl+X then Y | copy the last answer to the clipboard |
141
141
  | Ctrl+X then E | edit the draft in `$VISUAL` (or `$EDITOR`) and take back what it saves |
142
142
  | Ctrl+X then ? | search the key map: every action and the keys in force, in a box over the transcript |
143
+ | Ctrl+X then U | undo the last prompt: hide its turn, land on the previous answer, and put the prompt back in the bar |
144
+ | Ctrl+X then R | redo the undone prompt |
143
145
  | `y` / `n` / Esc / Ctrl+C | allow once, reject, or cancel a pending approval |
144
146
  | 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 |
145
147
  | ↑↓ / Ctrl+P / Ctrl+N | move through the open list: a picker's rows, a question's options, or the completion menu above the bar |
@@ -160,12 +162,15 @@ force, and the `keys:` section moves any of them — see [Keys](#keys).
160
162
  | `/preset` | pick the agent mode for this session from the roster |
161
163
  | `/preset <id>` | switch to that mode, while the session is still blank |
162
164
  | `/new [title]` | start a fresh session without leaving the terminal (`ctrl+x` then `n` starts one untitled) |
165
+ | `/reload` | compose this session's agent again and replay its transcript, so an edited preset or skill file reaches the session; a running turn or a queued prompt is refused with `ctrl+c` as the way forward |
163
166
  | `/jobs` | list background jobs with their state and duration |
164
167
  | `/jobs read <id>` / `/jobs kill <id>` | show the tail of a job's output, or stop it |
165
168
  | `/subagents` | list the delegations this session started, with their provider and age |
166
169
  | `/subagents open <id\|last>` | read a child's own conversation in place; `ctrl+b` comes back |
167
170
  | `/subagents kill <id>` | stop a live child agent |
168
171
  | `/fork [title]` | branch this conversation after its last completed turn and continue in the branch |
172
+ | `/undo` | hide the newest prompt's turn and put that prompt back in the bar (`ctrl+x` then `u`) |
173
+ | `/redo` | step forward again after an undo (`ctrl+x` then `r`) |
169
174
  | `/rename <title>` | title this session; the picker shows it instead of the session id |
170
175
  | `/export [path]` | write the visible transcript as markdown (default `dsh-session-<id>.md`) |
171
176
  | `/resume` | open another stored session without leaving the terminal |
@@ -192,18 +197,20 @@ prefix alone — enough to say that a key is waiting, without reciting the map
192
197
  and a key that finishes nothing is typed as usual rather than swallowed, so a
193
198
  prefix pressed by accident costs nothing; `/help` lists the chords, `m` for the
194
199
  model picker, `p` for plan mode, `n` for a fresh session, `y` for the last
195
- answer, `s` to stash the draft, `l` for the stashes, `e` for the draft in the
196
- reader's own editor, and `?` for the key map.
200
+ answer, `u` to undo the last prompt, `r` to redo it, `s` to stash the draft,
201
+ `l` for the stashes, `e` for the draft in the reader's own editor, and `?` for
202
+ the key map.
197
203
  `keys.chord.prefix: alt+x` starts the chord with another key — or with a list of
198
204
  them, as so many ways in — and `prefixWindow: 0` waits for the next key instead
199
205
  of lapsing; every second key is a row of its own (`chord.model`, `chord.plan`,
200
206
  `chord.new`, `chord.copy`, `chord.stash`, `chord.stashes`, `chord.editor`,
201
- `chord.keys`), so a chord can be respelled whole. A prefix that is not a modifier
207
+ `chord.keys`, `chord.undo`, `chord.redo`), so a chord can be respelled whole. A prefix that is not a modifier
202
208
  chord, that the surface or the prompt bar already answers (`ctrl+c`, `ctrl+s`),
203
209
  or that the terminal keeps (`ctrl+q`) is refused with the reason, and the
204
210
  shipped keymap stays in force. The chords themselves are the commands they stand
205
- for: `m`, `p`, `n`, `y`, `s`, `l`, and `?` ask the same dispatcher `/model`,
206
- `/plan`, `/new`, `/copy`, `/stash`, `/stash-list`, and `/keys` do; `e` is the
211
+ for: `m`, `p`, `n`, `y`, `s`, `l`, `u`, `r`, and `?` ask the same dispatcher
212
+ `/model`, `/plan`, `/new`, `/copy`, `/stash`, `/stash-list`, `/undo`, `/redo`,
213
+ and `/keys` do; `e` is the
207
214
  one chord with no command behind it, because it opens a program rather than
208
215
  running a line. Plan mode is the one pair that cannot share a name: `/plan` only
209
216
  enters, so the chord names `/plan off` instead when the agent is in plan mode —
@@ -214,6 +221,8 @@ An approval or a question draws inline above the editor and takes the keyboard.
214
221
 
215
222
  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.
216
223
 
224
+ Copying is read back through what the surface drew rather than through the screen, so a selection is the words alone: dragging across a message takes its box with it on screen, and the surface takes its own frame back out before the text reaches the clipboard — the sides, the padding beside them, and the rules above and below. A selection that covers a whole message, two of them, or a part of one is read the same way, and a row the transcript did not draw — the editor's own bar, a picker's card — is copied exactly as it read, and a selection that was nothing but frame is handed back as the reader made it, because a copy is never emptied. The frame still comes back in a copy taken with the terminal's own selection — Shift held while dragging, or a terminal that keeps selection to itself — which is the one path no program can filter.
225
+
217
226
  Every submitted line is also kept in a global prompt history at
218
227
  `$DSH_HOME/prompt-history.json`. Typing the start of a prompt that was sent
219
228
  before draws the rest of the newest match after the cursor in a faint shade.
@@ -609,11 +618,11 @@ A question whose id ends in `:secret` declares its typed answer a credential: th
609
618
 
610
619
  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.
611
620
 
612
- 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.
621
+ 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 call that has not answered yet names itself in the running colour, `tool.running.title`, and ends that row with the whole seconds it has been waiting, in `tool.running.elapsed`, once it has waited one, so a reader can tell the call they are watching from the one below it that already finished; both give way to the measured facts the moment the result lands. A call that failed names itself in the failed colour, `tool.failed.title` — including a shell whose command exited non-zero or died on a signal, which is a failure whether or not the tool that ran it said so. 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.
613
622
 
614
- 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).
623
+ 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. Each of those rows names its own call in the colour of what that call is doing — `tool.subcall.running` while the program is waiting on it, `tool.failed.title` when it failed, and `tool.subcall.title` once it is back — and a settled row keeps the one line the tool itself drew about its outcome, in `tool.terminal.status` where that tool declares one. A shell call therefore reads `bash pnpm test · exit 0` when it worked and the same row in red, with `exit 1`, when it did not, so a program's work reads row by row without opening the card. The program's own row is the one card that shows no mark: its timer is what says it is still running, and it is also what the card keeps when the program answers — the total it ran for, drawn in `tool.elapsed.done`, dimmed and italic, because the work it measured is over. 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).
615
624
 
616
- 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.
625
+ 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, and so are the elements a call in flight is drawn with — hiding `tool.running.elapsed` leaves the name in its running colour, hiding `tool.running.title` leaves the seconds counting, hiding `tool.elapsed.done` takes the total off a program's settled row, and a dispatched row is toned the same way per state with `tool.subcall.running` and `tool.subcall.title`. A state is only how a name is painted, so hiding one of those colours leaves the name in the colour a call with no state is read in rather than taking the name away.
617
626
 
618
627
  The bundle also takes the base's global agent rows out of the composition, twenty-three of them. Every one is a row the shipped modes supply per session instead, so leaving it mounted registers the same tool names in two layers and doubles each prompt section it owns. What stays mounted is the host: sessions, storage, models, permissions, jobs, and the command registry.
619
628
 
@@ -649,14 +658,14 @@ The list the dock and `/todo` draw follows the same lifetime every other surface
649
658
  | This surface | Herdr |
650
659
  |---|---|
651
660
  | the screen is taken, before any session opens | `idle`, claiming the pane's agent row |
652
- | `turn/start` | `working` |
661
+ | the agent's driver starts, including a run of turns chained through pending work | `working` |
653
662
  | an approval, a question, or a picker takes the keyboard | `blocked`, with that card's title sent along |
654
- | the decision settles | `working` if a turn is open, otherwise `idle` |
655
- | `turn/end` | `idle` |
663
+ | the decision settles | `working` if the driver is running, otherwise `idle` |
664
+ | the agent's driver stops with nothing pending | `idle` |
656
665
  | a session opens, resumes, forks, or is switched to | its id and reason, plus the `dsh_session` / `dsh_cwd` pane tokens |
657
666
  | exit, signal, or boot failure | `herdr pane release-agent`, so no row is left waiting on a process that is gone |
658
667
 
659
- A wait outranks a running turn: a turn waiting on a human is not making progress, and the wait is the only thing worth acting on from a wall of panes. Reports are sequenced per source, so a delivery that arrives late cannot undo the state the surface already moved past, and a state Herdr is already showing is not sent again. A report is only counted as made when Herdr acknowledges it: one that failed is tried again — soon after, then with a growing wait — and a session identity Herdr never confirmed travels with the next report of any kind. The retry cannot wait for another state change, because the pane may have nothing left to say: a turn that ended while the socket was down produces no further event. States still waiting to be sent are collapsed into the newest one, so a socket that was down for a minute is told where the pane is rather than where it has been. The release carries the next number in that same sequence for the same reason: Herdr reads one that cannot beat the pane's last report as stale, and a stale release leaves the row waiting on a process that is gone. It also stops reporting first — a claim landing after the release would take the row back for a process that is leaving — and the report already on the wire is waited for before the row goes back, because Herdr ignores the release of a pane nothing has claimed yet and that late report would then claim it. What is still waiting behind it is dropped rather than sent.
668
+ A wait outranks a running driver: an agent waiting on a human is not making progress, and the wait is the only thing worth acting on from a wall of panes. The pane reports the agent's driver rather than each turn: the harness chains turns through a pending inbox inside one driver run, and reporting each turn's end would read as done between two turns of an agent that is still working. Reports are sequenced per source, so a delivery that arrives late cannot undo the state the surface already moved past, and a state Herdr is already showing is not sent again. A report is only counted as made when Herdr acknowledges it: one that failed is tried again — soon after, then with a growing wait — and a session identity Herdr never confirmed travels with the next report of any kind. The retry cannot wait for another state change, because the pane may have nothing left to say: a driver that stopped while the socket was down produces no further event. States still waiting to be sent are collapsed into the newest one, so a socket that was down for a minute is told where the pane is rather than where it has been. The release carries the next number in that same sequence for the same reason: Herdr reads one that cannot beat the pane's last report as stale, and a stale release leaves the row waiting on a process that is gone. It also stops reporting first — a claim landing after the release would take the row back for a process that is leaving — and the report already on the wire is waited for before the row goes back, because Herdr ignores the release of a pane nothing has claimed yet and that late report would then claim it. What is still waiting behind it is dropped rather than sent.
660
669
 
661
670
  Herdr persists a session reference only for its own built-in integrations, so this pane's session identity travels as metadata tokens instead: a script or a companion plugin reads them back with `herdr pane get <id>` and resumes that exact conversation with `dsh --profile tui --resume=<id>`. Herdr holds a token value up to 80 characters and shortens anything longer, so a session id or directory that cannot be sent whole has its token cleared instead: a shortened path would read as a different directory, and a pane must not claim one. What Herdr cannot do is identify the process itself — its detection table and its screen rules both name built-in agents only — so a pane that has not reported yet reads as an ordinary pane, and is not yet a target: `herdr agent wait` on it fails with `agent_not_found` until the first report lands. Herdr 0.9.1 also keeps the title that accompanies a `blocked` state without showing it anywhere, so a reader sees the state and reads the card on screen.
662
671
 
@@ -688,6 +697,9 @@ node tools/pty-drive.mjs --home "$S" --prompt 'Reply with exactly: pong'
688
697
 
689
698
  Test specs import plugin sources through the `@/` alias. Under this test runner the spec file is resolved with a root-relative id, so parent-relative imports (`../src/...`) do not resolve; the alias and its matching `tsconfig.test.json` path mapping avoid that.
690
699
 
700
+ The development guide — the gate, the dogfood script, and the difference between
701
+ a fresh home, a cloned home, and the real one — is [DEVELOPMENT.md](DEVELOPMENT.md).
702
+
691
703
  ## Manual acceptance
692
704
 
693
705
  The automated checks drive a real PTY, but they run on this machine's terminal. These are the checks only a terminal on your desk can answer; each line is what to do and what it should look like.
@@ -708,6 +720,12 @@ The automated checks drive a real PTY, but they run on this machine's terminal.
708
720
  | `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 |
709
721
  | 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 |
710
722
  | 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 |
723
+ | a bash command that exits non-zero | the same row as any other failure: the tool's name in the failed colour, with the `exit 1` it ended on |
724
+ | a call that runs for more than a second, watched while it runs | its name is drawn in the running colour with `~1s` counting up at the end of the row, the command still on it; both give way to the measured facts the moment the result lands |
725
+ | a PTC turn with several dispatched calls, one of them slow, one of them exiting non-zero | the call still in flight names itself in the running colour, the one that exited non-zero is red with its `exit 1`, and the rest are plain with their own outcome |
726
+ | a PTC turn whose program ran for more than a second | the card's row counts the seconds while it runs; once the program answers, the total stays on the row dimmed and italic |
727
+ | `dsh-tui: { tokens: { tool.running.title: { hidden: true } } }` in `$DSH_HOME/settings.yaml`, then a slow call | the name is drawn in the colour a settled call uses and the seconds still count, and the call still settles into its result |
728
+ | `dsh-tui: { tokens: { tool.running.elapsed: { hidden: true } } }`, then a slow call | the row keeps its running colour and reports no duration |
711
729
  | 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 |
712
730
  | `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 |
713
731
  | `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 |
@@ -734,6 +752,8 @@ The automated checks drive a real PTY, but they run on this machine's terminal.
734
752
  | a 40-column terminal | transcript and card rows end in `…` instead of wrapping into the next line |
735
753
  | 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 |
736
754
  | press `0` on a question, type an answer, press Enter | the editor under row `0` shows the text as it is edited, and the model receives it as that question's answer |
755
+ | a question with no options, then type `/` | no menu opens and the slash stays part of the answer: answers are text the model reads, so the commands this session can run are offered only in the prompt bar |
756
+ | the same question, then type `@` and a fragment | the workspace's files are offered under the answer, and the menu's own keys pick a row instead of answering the question |
737
757
  | 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 |
738
758
  | `echo hi \| dsh --profile tui` | refuses with a non-zero exit and a message naming the TTY requirement |
739
759
  | `/stash`, `/stash-pop` in one terminal | the footer shows `stash 1` after the stash and the draft returns to the editor after the pop |
@@ -61,6 +61,17 @@ export declare function agentRoute(ctx: Context, options: Pick<StartAgentOptions
61
61
  model?: string;
62
62
  reasoningEffort?: ReasoningEffortId;
63
63
  };
64
+ /**
65
+ * Whether a refused resume means the session simply has no durable log yet.
66
+ *
67
+ * Matched by the harness's stable error name rather than a class import: the
68
+ * surface mounts a compatible release RANGE, so the persistence package it
69
+ * would import is not necessarily the one behind the refusal. Any other
70
+ * failure — a composition that will not mount, an unreadable log — has to
71
+ * surface as itself, because falling back to a create hides it behind the
72
+ * identity collision of a session that does exist.
73
+ */
74
+ export declare function absentSession(error: unknown): boolean;
64
75
  /**
65
76
  * Create or resume the one agent this surface drives.
66
77
  *
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/agent/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAC9F,OAAO,EAAE,iBAAiB,EAAqB,MAAM,sBAAsB,CAAA;AAE3E,0EAA0E;AAC1E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,yDAAyD;IACzD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,8DAA8D;IAC9D,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,0DAA0D;IAC1D,SAAS,IAAI,IAAI,CAAA;IACjB,6CAA6C;IAC7C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC5C,iFAAiF;IACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAA;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,UAAU,CAAC,GACrD;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,iBAAiB,CAAA;CAAE,CAiB5E;AAMD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkC5F"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/agent/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAC9F,OAAO,EAAE,iBAAiB,EAAqB,MAAM,sBAAsB,CAAA;AAE3E,0EAA0E;AAC1E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,yDAAyD;IACzD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,8DAA8D;IAC9D,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,0DAA0D;IAC1D,SAAS,IAAI,IAAI,CAAA;IACjB,6CAA6C;IAC7C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC5C,iFAAiF;IACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAA;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,UAAU,CAAC,GACrD;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,iBAAiB,CAAA;CAAE,CAiB5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAErD;AAMD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoC5F"}
package/lib/agent/host.js CHANGED
@@ -26,6 +26,19 @@ export function agentRoute(ctx, options) {
26
26
  : { reasoningEffort: ReasoningEffortId(selection.reasoningEffort) }),
27
27
  };
28
28
  }
29
+ /**
30
+ * Whether a refused resume means the session simply has no durable log yet.
31
+ *
32
+ * Matched by the harness's stable error name rather than a class import: the
33
+ * surface mounts a compatible release RANGE, so the persistence package it
34
+ * would import is not necessarily the one behind the refusal. Any other
35
+ * failure — a composition that will not mount, an unreadable log — has to
36
+ * surface as itself, because falling back to a create hides it behind the
37
+ * identity collision of a session that does exist.
38
+ */
39
+ export function absentSession(error) {
40
+ return error instanceof Error && error.name === 'SessionPersistenceNotFoundError';
41
+ }
29
42
  function userMessage(text) {
30
43
  return createUserMessage({ content: [{ type: 'text', text }], source: { kind: 'user' } });
31
44
  }
@@ -52,11 +65,12 @@ export async function startAgent(ctx, options) {
52
65
  const identity = options.fork === undefined
53
66
  ? { ...meta, ...preset }
54
67
  : { ...meta, parentSession: options.fork.from, isSeeded: true, ...preset };
68
+ const create = () => ctx.agents.create({ sessionId: options.sessionId, meta: identity, agentOptions, ...branch, ...setup });
55
69
  const handle = options.resume
56
70
  ? await ctx.agents
57
71
  .resume({ resumeSessionId: options.sessionId, agentOptions, ...setup })
58
- .catch(() => ctx.agents.create({ sessionId: options.sessionId, meta: identity, agentOptions, ...branch, ...setup }))
59
- : await ctx.agents.create({ sessionId: options.sessionId, meta: identity, agentOptions, ...branch, ...setup });
72
+ .catch((error) => (absentSession(error) ? create() : Promise.reject(error)))
73
+ : await create();
60
74
  let disposed = false;
61
75
  return {
62
76
  sessionId: options.sessionId,
@@ -1 +1 @@
1
- {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/agent/host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/agent/host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { SessionId } from '@deepseek-ai/dsh-session';
2
+ import type { ForkEvent } from './fork.ts';
3
+ /**
4
+ * The undo cursor's arithmetic, kept out of the terminal surface.
5
+ *
6
+ * The session log is append-only, so undo cannot delete a prompt: the cursor
7
+ * only counts how many trailing turns the transcript hides and which prefix a
8
+ * divergence continues from. Reading the turns from the log on every step keeps
9
+ * the durable state a single number that can never go stale.
10
+ */
11
+ /** One closed turn that contains a direct prompt and can be undone as a unit. */
12
+ export interface TurnPoint {
13
+ /** Turn number the harness opened. */
14
+ readonly turn: number;
15
+ /** Index of the turn's `turn/start`: the prefix a divergence inherits. */
16
+ readonly seedCount: number;
17
+ /** The first direct prompt's text, which the composer gets back. */
18
+ readonly promptText: string;
19
+ }
20
+ /** The staged cursor over one session's closed turns. */
21
+ export interface UndoState {
22
+ readonly sessionId: SessionId | undefined;
23
+ /** How many trailing turns the transcript hides; 0 is the tip. */
24
+ readonly hidden: number;
25
+ /** The composer text this state wrote last, so a reader's edit is never overwritten. */
26
+ readonly lastRestored: string;
27
+ }
28
+ /** The cursor before any session is open. */
29
+ export declare const NO_UNDO: UndoState;
30
+ /**
31
+ * How long an undo waits for an interrupted turn to close before giving up.
32
+ *
33
+ * The harness closes the turn as its abort settles, so a wait is needed; a
34
+ * bounded one keeps a stuck tool from leaving the key press unanswered.
35
+ */
36
+ export declare const UNDO_TURN_SETTLE_MS = 10000;
37
+ /** The cursor at the tip of one session. */
38
+ export declare function resetUndo(sessionId: SessionId): UndoState;
39
+ /**
40
+ * The closed turns a direct prompt can be undone from, in log order.
41
+ *
42
+ * A turn is one `turn/start`...`turn/end` bracket; only a closed bracket with at least one direct prompt
43
+ * is a unit. Injected context also arrives as `user/message`, which is why the source kind is checked.
44
+ */
45
+ export declare function turnsOf(events: readonly ForkEvent[]): readonly TurnPoint[];
46
+ /** Step the cursor one turn back, or undefined when nothing is left to hide. */
47
+ export declare function undoStep(state: UndoState, turns: readonly TurnPoint[]): UndoState | undefined;
48
+ /** Step the cursor one turn forward, or undefined when it is already at the tip. */
49
+ export declare function redoStep(state: UndoState, turns: readonly TurnPoint[]): UndoState | undefined;
50
+ /** The oldest hidden turn: where a divergence forks, or undefined at the tip. */
51
+ export declare function hiddenTail(state: UndoState, turns: readonly TurnPoint[]): TurnPoint | undefined;
52
+ //# sourceMappingURL=undo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/agent/undo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1C;;;;;;;GAOG;AAEH,iFAAiF;AACjF,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,yDAAyD;AACzD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAA;IACzC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,wFAAwF;IACxF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED,6CAA6C;AAC7C,eAAO,MAAM,OAAO,EAAE,SAAiE,CAAA;AAEvF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC,4CAA4C;AAC5C,wBAAgB,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAEzD;AAqCD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,SAAS,EAAE,CA+B1E;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,GAAG,SAAS,CAI7F;AAED,oFAAoF;AACpF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,GAAG,SAAS,CAI7F;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,GAAG,SAAS,CAE/F"}
@@ -0,0 +1,106 @@
1
+ /** The cursor before any session is open. */
2
+ export const NO_UNDO = { sessionId: undefined, hidden: 0, lastRestored: '' };
3
+ /**
4
+ * How long an undo waits for an interrupted turn to close before giving up.
5
+ *
6
+ * The harness closes the turn as its abort settles, so a wait is needed; a
7
+ * bounded one keeps a stuck tool from leaving the key press unanswered.
8
+ */
9
+ export const UNDO_TURN_SETTLE_MS = 10_000;
10
+ /** The cursor at the tip of one session. */
11
+ export function resetUndo(sessionId) {
12
+ return { sessionId, hidden: 0, lastRestored: '' };
13
+ }
14
+ function asRecord(value) {
15
+ return typeof value === 'object' && value !== null ? value : undefined;
16
+ }
17
+ /** The text blocks of a direct prompt, joined the way the composer hands them back. */
18
+ function promptText(data) {
19
+ const content = asRecord(data)?.content;
20
+ const parts = [];
21
+ for (const block of Array.isArray(content) ? content : []) {
22
+ const text = asRecord(block)?.text;
23
+ if (typeof text === 'string')
24
+ parts.push(text);
25
+ }
26
+ return parts.join('\n');
27
+ }
28
+ /** Whether an event is the human's own prompt rather than injected context. */
29
+ function isDirectPrompt(event) {
30
+ return event.type === 'user/message' && asRecord(asRecord(event.data)?.source)?.kind === 'user';
31
+ }
32
+ /** Where each delivered prompt first entered the inbox, by message id. */
33
+ function deliveryIndex(events) {
34
+ const delivered = new Map();
35
+ for (let index = 0; index < events.length; index += 1) {
36
+ const event = events[index];
37
+ if (event?.type !== 'agent/inbox/spliced')
38
+ continue;
39
+ const inserted = asRecord(event.data)?.inserted;
40
+ for (const message of Array.isArray(inserted) ? inserted : []) {
41
+ const id = asRecord(message)?.id;
42
+ if (typeof id === 'string' && !delivered.has(id))
43
+ delivered.set(id, index);
44
+ }
45
+ }
46
+ return delivered;
47
+ }
48
+ /**
49
+ * The closed turns a direct prompt can be undone from, in log order.
50
+ *
51
+ * A turn is one `turn/start`...`turn/end` bracket; only a closed bracket with at least one direct prompt
52
+ * is a unit. Injected context also arrives as `user/message`, which is why the source kind is checked.
53
+ */
54
+ export function turnsOf(events) {
55
+ const delivered = deliveryIndex(events);
56
+ const turns = [];
57
+ let open;
58
+ for (let index = 0; index < events.length; index += 1) {
59
+ const event = events[index];
60
+ if (event === undefined)
61
+ continue;
62
+ if (event.type === 'turn/start') {
63
+ const turn = asRecord(event.data)?.turn;
64
+ open = { turn: typeof turn === 'number' ? turn : turns.length + 1, startIndex: index, seedCount: index };
65
+ continue;
66
+ }
67
+ if (open === undefined)
68
+ continue;
69
+ if (open.promptText === undefined && isDirectPrompt(event)) {
70
+ open.promptText = promptText(event.data);
71
+ const id = asRecord(event.data)?.id;
72
+ // A prompt enters the log as an inbox splice just before the turn it
73
+ // opens; a seed that kept that splice would make the branch deliver the
74
+ // very prompt the reader undid, so the cut is the splice, not the turn.
75
+ const splice = typeof id === 'string' ? delivered.get(id) : undefined;
76
+ open.seedCount = Math.min(open.startIndex, splice ?? open.startIndex);
77
+ continue;
78
+ }
79
+ if (event.type === 'turn/end') {
80
+ if (open.promptText !== undefined) {
81
+ turns.push({ turn: open.turn, seedCount: open.seedCount, promptText: open.promptText });
82
+ }
83
+ open = undefined;
84
+ }
85
+ }
86
+ return turns;
87
+ }
88
+ /** Step the cursor one turn back, or undefined when nothing is left to hide. */
89
+ export function undoStep(state, turns) {
90
+ if (state.hidden >= turns.length)
91
+ return undefined;
92
+ const hidden = state.hidden + 1;
93
+ return { ...state, hidden, lastRestored: turns[turns.length - hidden]?.promptText ?? state.lastRestored };
94
+ }
95
+ /** Step the cursor one turn forward, or undefined when it is already at the tip. */
96
+ export function redoStep(state, turns) {
97
+ if (state.hidden === 0)
98
+ return undefined;
99
+ const hidden = state.hidden - 1;
100
+ return { ...state, hidden, lastRestored: hidden === 0 ? '' : turns[turns.length - hidden]?.promptText ?? '' };
101
+ }
102
+ /** The oldest hidden turn: where a divergence forks, or undefined at the tip. */
103
+ export function hiddenTail(state, turns) {
104
+ return state.hidden === 0 ? undefined : turns[turns.length - state.hidden];
105
+ }
106
+ //# sourceMappingURL=undo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.js","sourceRoot":"","sources":["../../src/agent/undo.ts"],"names":[],"mappings":"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"}
package/lib/cards.d.ts CHANGED
@@ -63,6 +63,24 @@ export interface ToolSubCall {
63
63
  readonly title: string;
64
64
  readonly argument?: string;
65
65
  readonly failed: boolean;
66
+ /**
67
+ * Whether the call is dispatched and still running.
68
+ *
69
+ * A program's calls are the only work a reader sees between the moment the
70
+ * program starts and its return value, so a call that has settled already and
71
+ * one still in flight have to be told apart: the card they sit under is one
72
+ * row either way until the program comes back.
73
+ */
74
+ readonly running: boolean;
75
+ /**
76
+ * How the call finished, in the words the tool that ran it reported.
77
+ *
78
+ * A program's calls all look alike once they are back, so the row keeps the
79
+ * one line the tool itself drew about its outcome — a shell's exit status — and
80
+ * a reader can tell a command that worked from one that did not without
81
+ * opening anything.
82
+ */
83
+ readonly status?: string;
66
84
  /**
67
85
  * What the tool's own presenter drew for the call — a diff derived from the
68
86
  * arguments, a raw input — kept only for the reader who opens the row.
@@ -126,6 +144,18 @@ export interface ToolCard {
126
144
  /** Rows retained for rendering, already capped at CARD_DETAIL_MAX. */
127
145
  readonly detail: readonly CardRow[];
128
146
  readonly failed: boolean;
147
+ /**
148
+ * Whether the call is logged and still unanswered, filled in at draw time.
149
+ *
150
+ * The surface is told when a call is requested and when its result lands, and
151
+ * nothing in between: between those two events the call is what the model is
152
+ * waiting on, which is the one thing a reader watching a long run needs to see.
153
+ * Only a frame sets these, and it sets them on a copy of the folded card, so a
154
+ * stored card never claims a call that has since answered is still running.
155
+ */
156
+ readonly running?: boolean;
157
+ /** Whole seconds the call has been in flight, drawn only while it is. */
158
+ readonly elapsed?: number;
129
159
  /** Rows the tool actually presented, which retention may have cut short. */
130
160
  readonly totalLines: number;
131
161
  /**
@@ -185,13 +215,38 @@ export declare function clip(text: string, limit: number): string;
185
215
  */
186
216
  export declare function oneLine(text: string): string;
187
217
  /**
188
- * The one-line row for one nested call.
218
+ * What the fold learned about one dispatched call, in the order it learned it.
219
+ *
220
+ * A call is logged twice — once when the program asks for it, once with what it
221
+ * answered — and each moment supplies part of the row: the call view names it,
222
+ * the result view says how it ended and what it produced.
223
+ */
224
+ export interface SubCallFacts {
225
+ /** The card the call declared, or nothing when no presenter answered for it. */
226
+ readonly view: ToolCard | undefined;
227
+ /** The rows the row opens to, once the call reported them. */
228
+ readonly output: ToolSubCallRows | undefined;
229
+ /** How the call finished, as the tool that ran it reported. */
230
+ readonly status: string | undefined;
231
+ /** Whether the call is logged and still unanswered. */
232
+ readonly running: boolean;
233
+ /** Whether the call is one the log marked as an error. */
234
+ readonly failed: boolean;
235
+ }
236
+ /**
237
+ * The one-line row for one nested call, as its start or its settle leaves it.
189
238
  *
190
239
  * A tool that declares a call view is drawn exactly as its own header would be;
191
240
  * only a tool with no view at all falls back to its registry name and raw call,
192
241
  * because the surface cannot name a salient argument for a schema it never saw.
242
+ * A settle logs the same call again, and the presenter that answered the first
243
+ * time is asked again for the view it would draw — so the row's identity comes
244
+ * from the row already on screen wherever there is one, and only a call that was
245
+ * dropped earlier is named from the log. The row also keeps how the call ended,
246
+ * because a program's calls are told apart by their outcomes rather than by
247
+ * anything the program itself says about them.
193
248
  */
194
- export declare function subCallOf(id: string, name: string, argumentsJson: string, view: ToolCard | undefined): ToolSubCall;
249
+ export declare function subCallRow(id: string, name: string, argumentsJson: string, facts: SubCallFacts, existing?: ToolSubCall): ToolSubCall;
195
250
  /**
196
251
  * The rows one dispatched call keeps, from the card that drew them.
197
252
  *
@@ -1 +1 @@
1
- {"version":3,"file":"cards.d.ts","sourceRoot":"","sources":["../src/cards.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cards.d.ts","sourceRoot":"","sources":["../src/cards.ts"],"names":[],"mappings":"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"}
package/lib/cards.js CHANGED
@@ -80,18 +80,41 @@ export function oneLine(text) {
80
80
  return text.replace(/\s+/gu, ' ').trim();
81
81
  }
82
82
  /**
83
- * The one-line row for one nested call.
83
+ * The one-line row for one nested call, as its start or its settle leaves it.
84
84
  *
85
85
  * A tool that declares a call view is drawn exactly as its own header would be;
86
86
  * only a tool with no view at all falls back to its registry name and raw call,
87
87
  * because the surface cannot name a salient argument for a schema it never saw.
88
+ * A settle logs the same call again, and the presenter that answered the first
89
+ * time is asked again for the view it would draw — so the row's identity comes
90
+ * from the row already on screen wherever there is one, and only a call that was
91
+ * dropped earlier is named from the log. The row also keeps how the call ended,
92
+ * because a program's calls are told apart by their outcomes rather than by
93
+ * anything the program itself says about them.
88
94
  */
89
- export function subCallOf(id, name, argumentsJson, view) {
90
- if (view !== undefined) {
91
- return { id, title: view.title, ...(view.argument === undefined ? {} : { argument: view.argument }), failed: false };
92
- }
93
- const raw = argumentsJson === '' ? '' : clipLine(argumentsJson);
94
- return { id, title: name, ...(raw === '' ? {} : { argument: raw }), failed: false };
95
+ export function subCallRow(id, name, argumentsJson, facts, existing) {
96
+ const { view, output, status, running, failed } = facts;
97
+ const title = view?.title ?? existing?.title ?? name;
98
+ const raw = view !== undefined || existing !== undefined ? undefined : clipLine(argumentsJson);
99
+ const argument = view?.argument ?? existing?.argument ?? raw;
100
+ // A failed call keeps only what still holds: the change it declared never
101
+ // happened, so the rows that drew as applied go with the outcome that never
102
+ // came. Its outcome stays, because the reason it failed is what a reader opens
103
+ // the row to read.
104
+ const presented = failed ? undefined : subCallRows(view) ?? existing?.presented;
105
+ // A settle that reports nothing keeps what the row already said about its own
106
+ // outcome, because a call does not stop having finished a certain way.
107
+ const ended = status ?? existing?.status;
108
+ return {
109
+ id,
110
+ title,
111
+ ...(argument === undefined || argument === '' ? {} : { argument }),
112
+ ...(ended === undefined || ended === '' ? {} : { status: ended }),
113
+ failed,
114
+ running,
115
+ ...(presented === undefined ? {} : { presented }),
116
+ ...(output === undefined ? {} : { output }),
117
+ };
95
118
  }
96
119
  /**
97
120
  * The rows one dispatched call keeps, from the card that drew them.
@@ -478,6 +501,13 @@ export function cardOfResult(view, input) {
478
501
  const status = terminal.signal !== undefined && terminal.signal !== ''
479
502
  ? `signal ${terminal.signal}`
480
503
  : terminal.exitCode === undefined ? undefined : `exit ${terminal.exitCode}`;
504
+ // A command that exited non-zero, or died on a signal, is a call that
505
+ // failed, whatever the log's own flag said: the harness reports a shell's
506
+ // exit code as its result rather than as an error, so that flag is silent
507
+ // about how the command actually ended.
508
+ const endedBadly = terminal.exitCode === undefined
509
+ ? terminal.signal !== undefined && terminal.signal !== ''
510
+ : terminal.exitCode !== 0;
481
511
  // The output is bounded from its END: a command that printed far more
482
512
  // than retention keeps must still show how it finished, and the folded
483
513
  // preview tails what is retained again.
@@ -493,7 +523,7 @@ export function cardOfResult(view, input) {
493
523
  ...(command === '' ? {} : { argument: command }),
494
524
  ...(status === undefined ? {} : { status }),
495
525
  detail: bounded.detail,
496
- failed,
526
+ failed: failed || endedBadly,
497
527
  totalLines: bounded.totalLines,
498
528
  };
499
529
  }