fix(tui): start the session a sessionless model switch defers into

Launching kigi in a non-project directory (~/Downloads, ~, /tmp — anywhere
`is_project_dir` rejects) leaves the agent view session-less behind the
project-picker question, which only a PLAIN prompt can open. Slash
commands still execute there, so `/model <name> <effort>` stashed its
switch in `deferred_model_switch` — a stash that assumes a create is in
flight — and with none pending it dangled forever with zero feedback:
the picker rendered, the user chose model and effort, and nothing
changed. Not a 0.1.10 regression: 0.1.9 pty-reproduces identically; the
report correlated with the upgrade only because every earlier launch
happened to be from a project directory.

Apply the QueueCommand precedent ("queued slash work bypasses the
picker, so create the deferred session or it never drains") to both
deferral sites — Action::SwitchModel in the router and
set_default_model's no-session branch — via
skip_picker_and_create_session, whose in-flight guard already makes it a
no-op while a create is pending, so the racing-create case is unchanged.
SessionCreated then applies the stash through the existing
apply_deferred_model_switch path; pty-verified end-to-end from
~/Downloads (session created, model changed, effort applied).
This commit is contained in:
ZacharyZhang-NY committed 2026-07-27 15:55:57 -04:00
1 parent 11c3ca9803
commit f656627c10
4 files changed
+108 -10

No files matched your search

@@ -1519,12 +1519,20 @@ pub(in crate::app::dispatch) fn set_default_model(
effort: None,
prev_model_id: prev_id.clone(),
});
} else if let Some(agent) = app.agents.get_mut(&aid) {
// No session id yet — stash for
// `EventLoop::on_session_created` to apply once the session
// id materialises. Mirrors the deferred-switch handling in
// `Action::SwitchModel`.
agent.session.deferred_model_switch = Some((new_id, None));
} else {
if let Some(agent) = app.agents.get_mut(&aid) {
// No session id yet — stash for
// `EventLoop::on_session_created` to apply once the session
// id materialises. Mirrors the deferred-switch handling in
// `Action::SwitchModel`.
agent.session.deferred_model_switch = Some((new_id, None));
}
// With no create in flight (project question pending), the stash
// would dangle forever — start the session it drains into. No-op
// when a create is already pending.
effects.extend(
crate::app::dispatch::session::lifecycle::skip_picker_and_create_session(app, aid),
);
}
effects
}