Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/src/ai/blocklist/action_model/execute/run_agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,8 +617,8 @@ fn resolve_request_from_config(request: &mut RunAgentsRequest, config: &Orchestr
// The approved plan config is the source of truth for these run-wide fields,
// so callers pass a mutable request and continue with the normalized value.
let mut config_state = OrchestrationConfigState::from_run_agents_fields(
&request.model_id,
&request.harness_type,
Some(&request.model_id),
Some(&request.harness_type),
&request.execution_mode,
);
config_state.override_from_approved_config(config);
Expand Down
648 changes: 244 additions & 404 deletions app/src/ai/blocklist/inline_action/orchestration_controls.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions app/src/ai/blocklist/inline_action/run_agents_card_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ pub struct RunAgentsEditState {
impl RunAgentsEditState {
pub fn from_request(req: &RunAgentsRequest) -> Self {
let mut orchestration_config_state = oc::OrchestrationConfigState::from_run_agents_fields(
&req.model_id,
&req.harness_type,
Some(&req.model_id),
Some(&req.harness_type),
&req.execution_mode,
);
// Carry the request's auth secret across the round trip. Absence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ fn make_config_state_with_orch_fields(
let request = make_request(harness, mode);
RunAgentsEditState {
orchestration_config_state: OrchestrationConfigState::from_run_agents_fields(
&request.model_id,
&request.harness_type,
Some(&request.model_id),
Some(&request.harness_type),
&request.execution_mode,
),
card: RunAgentsCardFields {
Expand Down
4 changes: 2 additions & 2 deletions app/src/ai/document/orchestration_config_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ impl OrchestrationConfigBlockView {
.unwrap_or_else(|| {
(
OrchestrationConfigState::from_run_agents_fields(
"auto",
"oz",
Some("auto"),
Some("oz"),
&RunAgentsExecutionMode::Local,
),
false,
Expand Down
10 changes: 6 additions & 4 deletions app/src/ai/orchestration/config_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,16 @@ impl OrchestrationConfigState {
}
}

/// `None` (or an empty string wrapped in `Some`) leaves the field
/// unset, matching the wire encoding where absence is emptiness.
pub fn from_run_agents_fields(
model_id: &str,
harness_type: &str,
model_id: Option<&str>,
harness_type: Option<&str>,
execution_mode: &RunAgentsExecutionMode,
) -> Self {
Self {
model_id: model_id.to_string(),
harness_type: harness_type.to_string(),
model_id: model_id.unwrap_or_default().to_string(),
harness_type: harness_type.unwrap_or_default().to_string(),
execution_mode: execution_mode.clone(),
auth_secret_selection: AuthSecretSelection::Unset,
}
Expand Down
22 changes: 14 additions & 8 deletions app/src/ai/orchestration/config_state_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ fn local_config(harness_type: &str, model_id: &str) -> OrchestrationConfig {
#[test]
fn toggle_to_local_sanitizes_disabled_codex() {
let mut state = OrchestrationConfigState::from_run_agents_fields(
"gpt-5",
"codex",
Some("gpt-5"),
Some("codex"),
&RunAgentsExecutionMode::Remote {
environment_id: "env-1".to_string(),
worker_host: "warp".to_string(),
Expand All @@ -36,8 +36,8 @@ fn toggle_to_local_sanitizes_disabled_codex() {
#[test]
fn toggle_to_local_preserves_claude() {
let mut state = OrchestrationConfigState::from_run_agents_fields(
"sonnet",
"claude",
Some("sonnet"),
Some("claude"),
&RunAgentsExecutionMode::Remote {
environment_id: "env-1".to_string(),
worker_host: "warp".to_string(),
Expand All @@ -57,8 +57,11 @@ fn toggle_to_local_preserves_claude() {

#[test]
fn resolve_from_config_preserves_local_claude() {
let mut state =
OrchestrationConfigState::from_run_agents_fields("", "", &RunAgentsExecutionMode::Local);
let mut state = OrchestrationConfigState::from_run_agents_fields(
None,
None,
&RunAgentsExecutionMode::Local,
);

state.resolve_from_config(&local_config("claude", "sonnet"));
assert_eq!(state.harness_type, "claude");
Expand All @@ -71,8 +74,11 @@ fn resolve_from_config_preserves_local_claude() {

#[test]
fn resolve_from_config_sanitizes_disabled_local_codex() {
let mut state =
OrchestrationConfigState::from_run_agents_fields("", "", &RunAgentsExecutionMode::Local);
let mut state = OrchestrationConfigState::from_run_agents_fields(
None,
None,
&RunAgentsExecutionMode::Local,
);

state.resolve_from_config(&local_config("codex", "gpt-5"));

Expand Down
53 changes: 36 additions & 17 deletions app/src/ai/orchestration/edit_state_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ fn model_valid_among<'a>(valid: &'a [&'a str]) -> impl Fn(&str, &str, bool) -> b

#[test]
fn execution_mode_change_to_local_forces_oz_and_strips_cloud_fields() {
let mut state =
OrchestrationConfigState::from_run_agents_fields("gpt-5", "codex", &remote_mode());
let mut state = OrchestrationConfigState::from_run_agents_fields(
Some("gpt-5"),
Some("codex"),
&remote_mode(),
);

state.apply_execution_mode_change_core(false, None, None, &model_valid_among(&[""]), &|_| {
Some(String::new())
Expand All @@ -37,8 +40,8 @@ fn execution_mode_change_to_local_forces_oz_and_strips_cloud_fields() {
#[test]
fn execution_mode_change_to_cloud_prefills_default_environment() {
let mut state = OrchestrationConfigState::from_run_agents_fields(
"auto",
"oz",
Some("auto"),
Some("oz"),
&RunAgentsExecutionMode::Local,
);

Expand All @@ -60,8 +63,8 @@ fn execution_mode_change_to_cloud_prefills_default_environment() {
#[test]
fn execution_mode_change_prefers_valid_fallback_over_default_model() {
let mut state = OrchestrationConfigState::from_run_agents_fields(
"stale",
"oz",
Some("stale"),
Some("oz"),
&RunAgentsExecutionMode::Local,
);

Expand All @@ -78,8 +81,11 @@ fn execution_mode_change_prefers_valid_fallback_over_default_model() {

#[test]
fn harness_change_saves_and_restores_per_harness_model_memory() {
let state =
OrchestrationConfigState::from_run_agents_fields("sonnet", "claude", &remote_mode());
let state = OrchestrationConfigState::from_run_agents_fields(
Some("sonnet"),
Some("claude"),
&remote_mode(),
);
let mut edit_state = OrchestrationEditState {
orchestration_config_state: state,
saved_model_per_harness: HashMap::from([("codex".to_string(), "gpt-5".to_string())]),
Expand All @@ -104,7 +110,8 @@ fn harness_change_saves_and_restores_per_harness_model_memory() {

#[test]
fn harness_change_applies_resolved_auth_selection() {
let mut state = OrchestrationConfigState::from_run_agents_fields("auto", "oz", &remote_mode());
let mut state =
OrchestrationConfigState::from_run_agents_fields(Some("auto"), Some("oz"), &remote_mode());
state.auth_secret_selection = AuthSecretSelection::Named("old-key".to_string());
let mut edit_state = OrchestrationEditState::new(state);

Expand All @@ -124,8 +131,11 @@ fn harness_change_applies_resolved_auth_selection() {

#[test]
fn revalidate_drops_deleted_named_secret_and_reseeds_from_resolved() {
let mut state =
OrchestrationConfigState::from_run_agents_fields("sonnet", "claude", &remote_mode());
let mut state = OrchestrationConfigState::from_run_agents_fields(
Some("sonnet"),
Some("claude"),
&remote_mode(),
);
state.auth_secret_selection = AuthSecretSelection::Named("deleted-key".to_string());

state.revalidate_after_catalog_change_core(
Expand All @@ -143,8 +153,11 @@ fn revalidate_drops_deleted_named_secret_and_reseeds_from_resolved() {

#[test]
fn revalidate_keeps_named_secret_still_present() {
let mut state =
OrchestrationConfigState::from_run_agents_fields("sonnet", "claude", &remote_mode());
let mut state = OrchestrationConfigState::from_run_agents_fields(
Some("sonnet"),
Some("claude"),
&remote_mode(),
);
state.auth_secret_selection = AuthSecretSelection::Named("my-key".to_string());

state.revalidate_after_catalog_change_core(
Expand All @@ -162,8 +175,11 @@ fn revalidate_keeps_named_secret_still_present() {

#[test]
fn revalidate_leaves_explicit_inherit_alone() {
let mut state =
OrchestrationConfigState::from_run_agents_fields("sonnet", "claude", &remote_mode());
let mut state = OrchestrationConfigState::from_run_agents_fields(
Some("sonnet"),
Some("claude"),
&remote_mode(),
);
state.auth_secret_selection = AuthSecretSelection::Inherit;

state.revalidate_after_catalog_change_core(
Expand All @@ -178,8 +194,11 @@ fn revalidate_leaves_explicit_inherit_alone() {

#[test]
fn revalidate_resets_vanished_model_to_default() {
let mut state =
OrchestrationConfigState::from_run_agents_fields("gone", "claude", &remote_mode());
let mut state = OrchestrationConfigState::from_run_agents_fields(
Some("gone"),
Some("claude"),
&remote_mode(),
);

state.revalidate_after_catalog_change_core(
None,
Expand Down
21 changes: 14 additions & 7 deletions app/src/ai/orchestration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,32 @@
mod config_state;
mod edit_state;
mod providers;
mod snapshots;
mod validation;

pub use config_state::{AuthSecretSelection, OrchestrationConfigState};
pub use edit_state::OrchestrationEditState;
#[cfg_attr(not(feature = "tui"), allow(unused_imports))]
pub use providers::ORCHESTRATION_ENV_NONE_LABEL;
pub(crate) use providers::{
can_execute_with_auth_secret, get_base_model_choices, persist_auth_secret_selection,
can_execute_with_auth_secret, persist_auth_secret_selection,
populate_default_auth_secret_for_execution,
};
pub use providers::{
persist_environment_selection, persist_host_selection,
resolve_auth_secret_selection_for_harness, resolve_default_environment_id,
resolve_default_host_slug, resolve_recent_host_slug, ORCHESTRATION_ENV_NONE_LABEL,
ORCHESTRATION_WARP_WORKER_HOST,
resolve_default_host_slug, ORCHESTRATION_WARP_WORKER_HOST,
};
// Consumed by the TUI via `tui_export`; the GUI gates Accept through
// `accept_disabled_reason_with_auth` instead.
#[cfg_attr(not(feature = "tui"), allow(unused_imports))]
pub use validation::auth_secret_selection_required;
pub use snapshots::location_snapshot;
pub(crate) use snapshots::AUTH_SECRET_INHERIT_LABEL;
pub use snapshots::{
api_key_snapshot, environment_snapshot, harness_snapshot, host_snapshot, model_snapshot,
OptionBadge, OptionFooter, OptionRow, OptionSnapshot, OptionSourceStatus,
};
pub use validation::{
accept_disabled_reason_with_auth, empty_env_recommendation_message, harness_is_selectable,
accept_disabled_reason_with_auth, empty_env_recommendation_message,
should_show_auth_secret_picker,
};
#[cfg_attr(not(feature = "tui"), allow(unused_imports))]
pub use validation::{auth_secret_selection_required, harness_is_selectable};
Loading
Loading