1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//! Coordinator TUI — a Claude-Code-like input box over a live session list.
//!
//! Why: operators want one conversational surface to talk to a coordinator and
//! watch a fleet of sessions (DOC-13). This module is the NEW coordinator screen
//! built alongside (not on top of) the existing `tui/dashboard` — keeping it
//! separate avoids touching the at-cap dashboard/health files. Child #1 landed
//! the skeleton (layout, selection, input editing, panic-safe terminal); Child
//! #2 wired live session-list polling + daemon-down handling; STUI-1 (#1278)
//! adds the numbered, scrollable list (ratatui `ListState`), Page-Up/Page-Down,
//! selection + scroll-offset preservation across refreshes, and the
//! immediate-re-poll-after-mutation hook (see [`nav`] and
//! [`state::CoordinatorState::request_repoll`]).
//! What: thin façade re-exporting the submodules and exposing [`run`], the
//! `tm session tui` entry point. [`run`] polls the daemon's
//! coordinator-context endpoint on the `--interval-ms` cadence and maps each
//! session into the live list (see [`poll`]). The daemon-backed `last_summary`
//! column and slash-command dispatch are deferred to later children; the summary
//! is derived client-side from `recent_output` until then.
//! Test: the pure pieces (rows, state, events, layout text) are unit-tested in
//! `tests`; [`run`] is the thin terminal glue exercised by launching the TUI.
use Stdout;
use ;
use ;
use ;
use crate;
use ;
use ;
use coord_poll_daemon;
use render_result;
use CoordinatorState;
// #7224: the panic-safe terminal guard this screen has used since its first
// cut now lives in `tui::terminal`, shared with `project_ctl` and the `tm ls`
// session TUI. Three surfaces enter the alternate screen, and a per-file copy
// of the restore sequence is three things to keep in step instead of one.
use crateTerminalGuard;
/// How long [`run_loop`] blocks waiting for a key before redrawing.
///
/// Why: a short key poll keeps input responsive while still yielding the CPU;
/// it is independent of the daemon refresh cadence (`interval_ms`), so input
/// never waits a whole poll interval to register.
/// What: 50ms, matching the dashboard's input cadence.
const KEY_POLL: Duration = from_millis;
/// Launch the coordinator TUI against `url`, polling every `interval_ms`.
///
/// Why: the `tm session tui` subcommand needs one entry point that owns the
/// terminal lifecycle. Now async (Child #2 added live daemon IO) so it runs on
/// the same tokio runtime as `tui::run`.
/// What: builds a [`DaemonClient`] for `url`, enters raw mode + the alternate
/// screen, installs a [`TerminalGuard`] whose `Drop` restores the terminal, then
/// runs the event loop polling the daemon on the `interval_ms` cadence. Because
/// the guard restores on unwind as well as on normal return, a panic or early
/// exit never leaves the operator's terminal in raw mode / the alternate screen.
/// Test: terminal glue is exercised by launching the TUI; the loop's pure
/// pieces (key dispatch, layout text, session mapping) are unit-tested.
pub async
/// [`run`] against a caller-built [`DaemonClient`].
///
/// Why (#9556): `tm session tui` pins an operator-chosen `--url` /
/// `TRUSTY_MPM_URL` on the client, and only a caller-built client carries
/// that pin into [`coord_poll_daemon`].
/// What: the same banner, terminal and loop sequence as [`run`].
/// Test: `coord_poll_keeps_explicit_url` covers the pinned poll; this
/// terminal glue is exercised by launching the TUI.
pub async
/// The live render + poll + input loop.
///
/// Why: kept separate from [`run`] so terminal setup/teardown wraps it cleanly
/// and the loop body stays focused on poll → draw → poll-key → dispatch.
/// What: takes the already-primed [`CoordinatorState`] (the STUI-0 banner poll
/// in [`run`] populated it so the list is ready before the first frame), then
/// each iteration draws the frame, polls the keyboard for [`KEY_POLL`] and
/// dispatches any key via [`handle_key`], and re-polls the daemon once at least
/// `interval_ms` has elapsed since the last poll (tokio [`Instant`] timer).
/// Exits once `should_exit` is set (`q` / Ctrl-C).
/// Test: the dispatch, layout text, and session mapping are unit-tested; this
/// terminal-bound loop is exercised by launching the TUI.
async
/// Route one submitted input line through chat-core and append the result.
///
/// Why: this is the Phase 1C dispatch seam — it replaces the old echo-only stub.
/// Keeping it OUT of [`dispatch::route`] (which is pure) isolates the async/IO
/// half: build the executor for the client's current URL, await the command, and
/// append the rendered result to the output log. Running it inline in the loop
/// (rather than on a background task) keeps the render thread's view of `state`
/// single-owned — the executor call is brief and the loop already awaits the
/// daemon for polling, so there is no separate thread to block.
/// What: echoes `line` into the output log, routes it via [`route`] against the
/// focused session, and for a [`Dispatch::Command`] runs it through a
/// [`CommandExecutor`] (built for `client.base_url()` so it tracks the TUI's
/// self-healed URL) and appends [`render_result`]'s lines. A [`Dispatch::Hint`]
/// appends the hint; a [`Dispatch::Echo`] records nothing further. After a
/// SUCCESSFUL mutating command it requests an immediate re-poll so the list
/// reflects the change now.
/// Test: the routing decision is unit-tested in [`dispatch`] and the rendering in
/// [`render`]; this async glue is exercised by launching the TUI.
async