Fix direct-provider command monitoring

This commit is contained in:
2026-08-15 15:49:07 -05:00
parent 2730712179
commit 4b20952cec
9 changed files with 152 additions and 56 deletions
+25 -15
View File
@@ -80,7 +80,18 @@ pub(crate) async fn prepare_provider_run(
let skill_path_origin = params.session_context.skill_path_origin();
let max_context_tokens = params.context_window_limit;
let mut cli_params = params.clone();
cli_params.model = params.cli_agent_model.clone();
let cli_provider_config = match cli_provider_config {
crate::ai::provider::ProviderConfig::None => {
// The CLI model can be absent from a model-specific provider routing table even when
// the base model is usable. Keep monitoring available through the base provider/model.
cli_params.model = params.model.clone();
base_provider_config.clone()
}
provider_config => {
cli_params.model = params.cli_agent_model.clone();
provider_config
}
};
let (base_runtime, prepared) = prepare_provider_profile(
base_provider_config,
@@ -90,20 +101,15 @@ pub(crate) async fn prepare_provider_run(
None,
)
.await?;
let cli_monitor_profile = match cli_provider_config {
crate::ai::provider::ProviderConfig::None => None,
provider_config => {
let (runtime, prepared) = prepare_provider_profile(
provider_config,
cli_params,
supported_tools,
supported_cli_agent_tools,
Some(RigRequestMode::Cli),
)
.await?;
Some(ProviderRunProfile::new(runtime, prepared.request))
}
};
let (cli_runtime, cli_prepared) = prepare_provider_profile(
cli_provider_config,
cli_params,
supported_tools,
supported_cli_agent_tools,
Some(RigRequestMode::Cli),
)
.await?;
let cli_monitor_profile = Some(ProviderRunProfile::new(cli_runtime, cli_prepared.request));
let PreparedRigTurn {
task_id,
@@ -250,3 +256,7 @@ pub(crate) async fn provider_runtime_for_request(
};
Ok(runtime)
}
#[cfg(test)]
#[path = "rig_tests.rs"]
mod tests;
+1 -1
View File
@@ -816,7 +816,7 @@ fn build_system_prompt(
"## Orchestration Mode\nDelegate only independent, bounded work where parallelism materially helps, then synthesize the results.\n\n",
),
RigRequestMode::Cli => prompt.push_str(
"## Running Command Monitor\nThis turn concerns a running or just-finished shell command. Act as its dedicated monitor while still following the user's steering messages. Use the command ID from the tool result for every read/write operation. If the result says the command finished, report its outcome and stop polling. If it says the command is still running, the next assistant output MUST be a tool call: use `read_shell_command_output` with a short delay, or use `interrupt_shell_command` immediately when the user's explicit stop condition is met. Do not end a still-running monitor turn with prose, a status message, or a request for the user to say continue. Never choose a poll interval that crosses a user-specified deadline or stop condition. After an interrupt, poll briefly to verify the outcome. Never start a duplicate command merely to check its state, and never report completion while a result says it is still running.\n\n",
"## Running Command Monitor\nThis turn concerns a running or just-finished shell command. Act as its dedicated monitor while still following the user's steering messages. Use the command ID from the tool result for every read/write operation. If the result says the command finished, report its outcome and stop polling. If it says the command is still running, the next assistant output MUST be a tool call. Use `read_shell_command_output` with a short delay for normal progress. If the snapshot clearly shows an interactive pager or editor, do not keep polling: an alternate screen containing `(END)` is `less`, so call `write_to_long_running_shell_command` with input `q` and mode `raw`; for a clearly identified Vim screen, send input `:q` with mode `line`. Poll briefly after sending quit input to verify the outcome. Use `interrupt_shell_command` immediately when the user's explicit stop condition is met. Do not end a still-running monitor turn with prose, a status message, or a request for the user to say continue. Never choose a poll interval that crosses a user-specified deadline or stop condition. After an interrupt, poll briefly to verify the outcome. Never start a duplicate command merely to check its state, and never report completion while a result says it is still running.\n\n",
),
RigRequestMode::CompletedCommandAssessment => prompt.push_str(
"## Completed Command Assessment\nThe monitored command has finished. Use its command, command ID, final terminal output, and the assessment instruction in the latest hidden input to provide the final user-facing outcome. Do not continue polling, request more terminal output, or call tools.\n\n",
+2
View File
@@ -429,6 +429,8 @@ fn lrc_snapshot_follow_up_uses_the_cli_monitor_prompt_and_tools() {
assert!(prompt.contains("## Running Command Monitor"));
assert!(prompt.contains("`read_shell_command_output` with a short delay"));
assert!(prompt.contains("next assistant output MUST be a tool call"));
assert!(prompt.contains("alternate screen containing `(END)` is `less`"));
assert!(prompt.contains("`write_to_long_running_shell_command` with input `q` and mode `raw`"));
assert!(prompt.contains("Do not end a still-running monitor turn with prose"));
assert!(prepared
.request
+57
View File
@@ -0,0 +1,57 @@
use super::prepare_provider_run;
use crate::ai::agent::api::RequestParams;
use crate::ai::llms::LLMId;
use crate::ai::openai::client::OpenAIClientConfig;
use crate::ai::provider::ProviderConfig;
use crate::settings::OpenAIProviderKind;
fn openai_config(model: &str) -> OpenAIClientConfig {
OpenAIClientConfig {
kind: OpenAIProviderKind::LiteLLM,
base_url: "http://localhost:4000/v1".to_string(),
api_key: None,
project_id: None,
location: None,
model: Some(model.to_string()),
reasoning_effort: None,
max_input_tokens: Some(128_000),
max_output_tokens: Some(8_192),
use_rig: true,
supports_system_messages: true,
}
}
#[tokio::test]
async fn missing_cli_provider_route_falls_back_to_base_provider_profile() {
let mut params = RequestParams::new_for_test();
params.model = LLMId::from("base-selection");
params.cli_agent_model = LLMId::from("unroutable-cli-selection");
let prepared = prepare_provider_run(
ProviderConfig::OpenAI(openai_config("base-provider-model")),
ProviderConfig::None,
params,
)
.await
.unwrap();
let cli_profile = prepared
.cli_monitor_profile
.expect("base provider should supply the CLI monitor fallback");
assert_eq!(cli_profile.request.model.as_str(), "base-provider-model");
assert!(cli_profile
.request
.system_prompt
.as_deref()
.is_some_and(|prompt| prompt.contains("## Running Command Monitor")));
assert!(cli_profile
.request
.tools
.iter()
.any(|tool| tool.name == "write_to_long_running_shell_command"));
assert!(cli_profile
.request
.tools
.iter()
.any(|tool| tool.name == "read_shell_command_output"));
}