@@ -1144,6 +1144,7 @@ void AcpSessionView::rebind(AcpSessionModel *model, AcpConnection *connection)
11441144 m_model = model;
11451145 m_connection = connection;
11461146 m_savedPrefsApplied = false ;
1147+ m_pendingEffort = {};
11471148 // The conversation is gone: a restarted session is a draft again.
11481149 m_draft = true ;
11491150 m_carry = {};
@@ -1288,6 +1289,7 @@ void AcpSessionView::rebindConnection(AcpConnection *connection)
12881289 disconnect (m_registry, nullptr , this , nullptr );
12891290 m_connection = connection;
12901291 m_savedPrefsApplied = false ;
1292+ m_pendingEffort = {};
12911293 ++m_scheduledRev;
12921294 m_parked = {};
12931295 m_resumeBlocked = false ;
@@ -2653,6 +2655,7 @@ void AcpSessionView::onMetadataChanged()
26532655 // Once the agent's catalogs have arrived, push any saved per-agent
26542656 // selections back into the session (one-shot per session).
26552657 applySavedPreferences ();
2658+ applyPendingEffort ();
26562659
26572660 // After the initial restore, persist any agent-side config changes
26582661 // (e.g. Codex CLI's config_option_update for model/mode/effort) so the
@@ -2674,7 +2677,7 @@ void AcpSessionView::onMetadataChanged()
26742677 if (!curMode.isEmpty ())
26752678 m_registry->setAgentPreference (agentId, QStringLiteral (" mode" ), curMode);
26762679
2677- if (!m_effortConfigOptionId.isEmpty ()) {
2680+ if (!m_effortConfigOptionId.isEmpty () && m_pendingEffort. effort . isEmpty () ) {
26782681 for (const auto &opt : m_model->configOptions ()) {
26792682 if (opt.id == m_effortConfigOptionId) {
26802683 const QString val = opt.currentValue .toString ();
@@ -2708,7 +2711,8 @@ void AcpSessionView::sendAdvisorCommand(bool on)
27082711 if (!m_connection || !m_model || !m_modelCombo) return ;
27092712 const int idx = m_modelCombo->currentIndex ();
27102713 submitSlashCommand (AcpProtocol::advisorCommandText (
2711- on, m_modelCombo->itemData (idx).toString (), m_modelCombo->itemText (idx)));
2714+ on, m_model->availableCommands (), m_modelCombo->itemData (idx).toString (),
2715+ m_modelCombo->itemText (idx)));
27122716}
27132717
27142718// Same routing as typing the command and pressing Send: queued while a turn
@@ -3748,6 +3752,7 @@ void AcpSessionView::onModelComboChanged(int index)
37483752 if (id.isEmpty ()) return ;
37493753 const QString prefKey = m_modelConfigOptionId.isEmpty ()
37503754 ? QStringLiteral (" model" ) : m_modelConfigOptionId;
3755+ m_pendingEffort = {}; // the user's pick replaces the restore
37513756 if (!m_connection->isSessionReady ()) {
37523757 // Agent not started yet (restored session): applied once it is.
37533758 m_carry.model = id;
@@ -3764,8 +3769,10 @@ void AcpSessionView::onModelComboChanged(int index)
37643769 if (m_registry && m_draft) {
37653770 m_registry->setAgentPreference (m_connection->definition ().id , prefKey, id);
37663771 }
3767- // The advisor tracks the selected model's level: re-point it.
3768- if (m_advisorBtn && m_advisorBtn->isVisible () && m_advisorBtn->isChecked ())
3772+ // The advisor tracks the selected model's level: re-point it. An on/off
3773+ // advisor (omp) names no model, so there is nothing to re-point.
3774+ if (m_advisorBtn && m_advisorBtn->isVisible () && m_advisorBtn->isChecked () && m_model
3775+ && !AcpProtocol::advisorIsToggle (m_model->availableCommands ()))
37693776 sendAdvisorCommand (true );
37703777}
37713778
@@ -3792,6 +3799,7 @@ void AcpSessionView::onEffortComboChanged(int index)
37923799 if (m_updatingSelectors || !m_connection || index < 0 ) return ;
37933800 if (m_effortConfigOptionId.isEmpty ()) return ;
37943801 const QString val = m_effortCombo->itemData (index).toString ();
3802+ m_pendingEffort = {};
37953803 if (m_connection->isSessionReady ()) {
37963804 m_connection->setConfigOption (m_effortConfigOptionId, val);
37973805 } else {
@@ -3846,26 +3854,32 @@ void AcpSessionView::applySavedPreferences()
38463854 const QString savedModel = want.model ;
38473855 const QString savedModelBase = baseModelId (savedModel);
38483856 const QString currentModelBase = baseModelId (m_model->currentModelId ());
3849- if (!savedModel.isEmpty () && savedModelBase != currentModelBase) {
3850- if (!m_modelConfigOptionId.isEmpty ()) {
3851- for (const auto &opt : m_model->configOptions ()) {
3852- if (opt.id != m_modelConfigOptionId) continue ;
3853- for (const auto &ch : opt.options ) {
3854- if (ch.value == savedModel || ch.value == savedModelBase) {
3855- m_connection->setConfigOption (m_modelConfigOptionId, ch.value );
3856- break ;
3857- }
3858- }
3859- break ;
3857+ QString modelSent;
3858+ if (!savedModel.isEmpty () && !m_modelConfigOptionId.isEmpty ()) {
3859+ // A choice equal to the saved id wins: omp ids are "provider/model",
3860+ // where the part before the slash is no model. Only a saved Codex
3861+ // "model/effort" id falls back to its base model.
3862+ for (const auto &opt : m_model->configOptions ()) {
3863+ if (opt.id != m_modelConfigOptionId) continue ;
3864+ const auto has = [&opt](const QString &v) {
3865+ return std::any_of (opt.options .cbegin (), opt.options .cend (),
3866+ [&v](const auto &ch) { return ch.value == v; });
3867+ };
3868+ const QString target = has (savedModel) ? savedModel
3869+ : has (savedModelBase) ? savedModelBase
3870+ : QString ();
3871+ if (!target.isEmpty () && target != opt.currentValue .toString ()) {
3872+ modelSent = target;
3873+ m_connection->setConfigOption (m_modelConfigOptionId, target);
38603874 }
3861- } else {
3862- for ( const auto &m : m_model-> availableModels ()) {
3863- if (m. id == savedModel || baseModelId (m. id ) == savedModelBase) {
3864- m_connection-> setModel (m. id . contains ( QLatin1Char ( ' / ' ))
3865- ? baseModelId (m.id )
3866- : m.id ) ;
3867- break ;
3868- }
3875+ break ;
3876+ }
3877+ } else if (! savedModel. isEmpty () && savedModelBase != currentModelBase ) {
3878+ for ( const auto &m : m_model-> availableModels ()) {
3879+ if (m. id == savedModel || baseModelId (m.id ) == savedModelBase) {
3880+ modelSent = m. id . contains ( QLatin1Char ( ' / ' )) ? baseModelId (m. id ) : m.id ;
3881+ m_connection-> setModel (modelSent) ;
3882+ break ;
38693883 }
38703884 }
38713885 }
@@ -3881,25 +3895,56 @@ void AcpSessionView::applySavedPreferences()
38813895 }
38823896 }
38833897
3884- // Effort / reasoning level: keyed by the matched config-option id.
3898+ // Effort / reasoning level: keyed by the matched config-option id. A level
3899+ // the current model lacks may be the restored model's: wait for its switch.
38853900 if (!m_effortConfigOptionId.isEmpty ()) {
38863901 const QString savedEffort = want.effort ;
38873902 if (!savedEffort.isEmpty ()) {
38883903 for (const auto &opt : m_model->configOptions ()) {
38893904 if (opt.id != m_effortConfigOptionId) continue ;
38903905 if (opt.currentValue .toString () == savedEffort) break ;
3891- for ( const auto &ch : opt.options ) {
3892- if (ch .value == savedEffort) {
3893- m_connection-> setConfigOption (m_effortConfigOptionId, savedEffort);
3894- break ;
3895- }
3896- }
3906+ const bool offered = std::any_of ( opt.options . cbegin (), opt. options . cend (),
3907+ [&]( const auto &ch) { return ch .value == savedEffort; });
3908+ if (offered)
3909+ m_connection-> setConfigOption (m_effortConfigOptionId, savedEffort) ;
3910+ else if (!modelSent. isEmpty ())
3911+ m_pendingEffort = {savedEffort, modelSent, false };
38973912 break ;
38983913 }
38993914 }
39003915 }
39013916}
39023917
3918+ void AcpSessionView::applyPendingEffort ()
3919+ {
3920+ if (m_pendingEffort.effort .isEmpty () || !m_connection || !m_model) return ;
3921+ const QString current = m_model->currentModelId ();
3922+ const bool modelLanded = current == m_pendingEffort.model
3923+ || baseModelId (current) == m_pendingEffort.model ;
3924+ const auto &opts = m_model->configOptions ();
3925+ const auto opt = std::find_if (opts.cbegin (), opts.cend (), [this ](const auto &o) {
3926+ return !m_effortConfigOptionId.isEmpty () && o.id == m_effortConfigOptionId;
3927+ });
3928+ if (opt == opts.cend ()) {
3929+ if (modelLanded) m_pendingEffort = {};
3930+ return ;
3931+ }
3932+ if (opt->currentValue .toString () == m_pendingEffort.effort ) {
3933+ m_pendingEffort = {}; // landed: what the agent reports is the default again
3934+ return ;
3935+ }
3936+ if (m_pendingEffort.sent ) return ;
3937+ const bool offered = std::any_of (opt->options .cbegin (), opt->options .cend (), [this ](const auto &ch) {
3938+ return ch.value == m_pendingEffort.effort ;
3939+ });
3940+ if (offered) {
3941+ m_connection->setConfigOption (m_effortConfigOptionId, m_pendingEffort.effort );
3942+ m_pendingEffort.sent = true ;
3943+ } else if (modelLanded) {
3944+ m_pendingEffort = {}; // the restored model does not offer it either
3945+ }
3946+ }
3947+
39033948void AcpSessionView::onJumpToBottomClicked ()
39043949{
39053950 if (!m_scroll) return ;
0 commit comments