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
//! Managed-spawn POST + progress spinner for the guided `tm` on-ramp (#1904).
//!
//! Why: split out of `guided.rs` (issue #610 SLOC cap) — this is the one
//! blocking network call in the guided flow, so it carries its own response
//! DTO, spinner-message formatter, and the POST-and-attach driver as a
//! self-contained unit.
//! What: [`launch_new_session_and_attach`] POSTs a new managed session and
//! attaches to it, showing a [`trusty_progress::ProgressHandle`] spinner for
//! the full duration of the blocking call. [`spawn_progress_message`] is the
//! pure helper that formats the spinner's initial (fallback) message. A
//! background task from [`super::guided_launch_sse::watch_provisioning_stages`]
//! upgrades that static spinner into real step-by-step progress when the
//! daemon streams `provisioning_stage` SSE events; when it cannot (older
//! daemon, connection failure), the spinner simply keeps its initial message —
//! the exact minimum-bar behaviour this module shipped before the #1904
//! stretch goal, so that path is never regressed.
//! Test: `spawn_progress_message_first_run`, `spawn_progress_message_reuse` in
//! `tests_behavior_c_tests.rs`; `launch_new_session_and_attach` is covered by
//! the `POST /api/v1/sessions/managed` integration tests in
//! `tests/session_manager_mvp.rs`.
use Context as _;
use Deserialize;
use needs_first_run_clone;
use watch_provisioning_stages;
/// Response shape for `POST /api/v1/sessions/managed` (subset we need).
///
/// Why: a local type avoids depending on the daemon's internal DTO from the
/// CLI binary crate; the fields we care about are `name` and `state`.
/// What: mirrors `daemon::managed_routes::SpawnResponse` for the two fields
/// the guided default uses.
/// Test: covered indirectly by `launch_new_session_and_attach`.
/// Build the spinner message shown while the blocking managed-spawn POST is
/// in flight (issue #1904).
///
/// Why: a first-run clone triggers minutes of server-side work (git clone,
/// agent/skill deploy, MCP injection, tmux + runtime spawn) behind one opaque
/// HTTP call; the operator needs a distinct message for that case versus the
/// (much faster) worktree-reuse launch, without duplicating the formatting
/// logic at each call site.
/// What: returns the first-run "cloning into <path>…" message when `first_run`
/// is `Some`, else the generic "launching new session…" message.
/// Test: `spawn_progress_message_first_run`, `spawn_progress_message_reuse`.
pub
/// POST a new managed session to the daemon and attach to it.
///
/// Why: "launch new" in the picker must use the daemon's protected managed-clone
/// spawn path — NEVER write framework files into the live checkout (#1724).
/// Issue #1904: the daemon does git clone, TASK.md write, home-trust preseed,
/// agent/skill deployment, CLAUDE.md/instructions composition, MCP injection,
/// tmux session creation, and runtime spawn — all inside this single blocking
/// POST, which can take minutes on a first-run clone. Previously the client
/// printed two static lines and then went silent for the entire duration; a
/// spinner (via the shared `trusty-progress` crate, matching the rest of the
/// trusty-* ecosystem's rustup/cargo-style progress display) now runs for the
/// full duration of the call so the operator always has visible feedback
/// instead of a silent hang.
/// What: POSTs `{"repo_url": repo_url, "ref": "HEAD", "task": ""}` to
/// `/api/v1/sessions/managed` while a [`trusty_progress::ProgressHandle`]
/// spinner runs (auto-hidden in non-TTY/plain contexts, so piped/CI output is
/// unaffected). `repo_url` MUST be the git working-tree root (not a
/// subdirectory), so the daemon finds `.git` and sets `source_id` correctly —
/// enabling future `list_project_sessions` to show this session. The spinner
/// is cleared on every exit path (success or error) before any further output
/// is printed. The daemon provisions a per-session worktree in the base clone
/// and returns the session name. Then attaches via
/// `crate::commands::tmux_attach::tmux_attach`.
///
/// Issue #1904 stretch goal: alongside the spinner, a background task
/// subscribes to the daemon's `GET /events` SSE stream and upgrades the
/// spinner's message to the real in-flight stage (cloning → deploying agents
/// → deploying skills → building instructions → configuring MCP → creating
/// session → launching runtime) as `provisioning_stage` events arrive,
/// correlated by `repo_url` (see
/// `guided_launch_sse::watch_provisioning_stages` for why session-id
/// correlation is not possible here). The task is aborted as soon as the POST
/// returns — success or failure — so it never outlives this call.
/// Test: covered by the `POST /api/v1/sessions/managed` integration tests in
/// `tests/session_manager_mvp.rs`; the spinner-message formatting is covered
/// by `spawn_progress_message_*` above; the SSE frame parsing is covered by
/// `guided_launch_sse`'s unit tests.
pub async