Preserve completed command assessments
This commit is contained in:
@@ -110,18 +110,29 @@ fn prepare_rig_turn_for_provider(
|
||||
let mode = request_mode(&input);
|
||||
let available_tools = match mode {
|
||||
RigRequestMode::Cli => supported_cli_agent_tools,
|
||||
RigRequestMode::CompletedCommandAssessment => Vec::new(),
|
||||
RigRequestMode::Normal | RigRequestMode::Plan | RigRequestMode::Orchestrate => {
|
||||
supported_tools
|
||||
}
|
||||
};
|
||||
let (mut tools, mcp_tool_aliases) = tool_definitions(&available_tools, mcp_context.as_ref());
|
||||
if matches!(mode, RigRequestMode::Cli) {
|
||||
// History recall cannot advance a running command and is handled inline by the Rig
|
||||
// adapter (without producing a client action that can trigger another turn). Keeping it
|
||||
// in the CLI tool list lets the model spend its entire monitor turn recalling the prior
|
||||
// snapshot instead of scheduling `read_shell_command_output`, so make polling the only
|
||||
// way to inspect the active command here.
|
||||
tools.retain(|tool| tool.name != "recall_tool_history");
|
||||
let (mut tools, mut mcp_tool_aliases) =
|
||||
tool_definitions(&available_tools, mcp_context.as_ref());
|
||||
match mode {
|
||||
RigRequestMode::Cli => {
|
||||
// History recall cannot advance a running command and is handled inline by the Rig
|
||||
// adapter (without producing a client action that can trigger another turn). Keeping it
|
||||
// in the CLI tool list lets the model spend its entire monitor turn recalling the prior
|
||||
// snapshot instead of scheduling `read_shell_command_output`, so make polling the only
|
||||
// way to inspect the active command here.
|
||||
tools.retain(|tool| tool.name != "recall_tool_history");
|
||||
}
|
||||
RigRequestMode::CompletedCommandAssessment => {
|
||||
// The caller deliberately disables tools for the final assessment. The inline history
|
||||
// tool is added independently of supported tool types, so remove it explicitly too.
|
||||
tools.clear();
|
||||
mcp_tool_aliases.clear();
|
||||
}
|
||||
RigRequestMode::Normal | RigRequestMode::Plan | RigRequestMode::Orchestrate => {}
|
||||
}
|
||||
let system_prompt = build_system_prompt(&input, &tools, &global_rules, mode);
|
||||
|
||||
@@ -243,6 +254,20 @@ fn input_message(input: AIAgentInput) -> Option<ConversationMessage> {
|
||||
};
|
||||
(text, image_parts(&context))
|
||||
}
|
||||
AIAgentInput::CommandCompletionAssessment {
|
||||
prompt,
|
||||
context,
|
||||
completed_command,
|
||||
} => (
|
||||
format!(
|
||||
"[Completed command: {}]\n[Command ID: {}]\n[Final terminal output:\n{}\n]\n{}",
|
||||
completed_command.command,
|
||||
completed_command.block_id,
|
||||
completed_command.grid_contents,
|
||||
prompt
|
||||
),
|
||||
image_parts(&context),
|
||||
),
|
||||
AIAgentInput::ActionResult { .. } => return None,
|
||||
AIAgentInput::AutoCodeDiffQuery { query, .. } => (query, Vec::new()),
|
||||
AIAgentInput::ResumeConversation { .. } => (
|
||||
@@ -389,7 +414,8 @@ fn input_user_query(input: &AIAgentInput) -> Option<String> {
|
||||
match input {
|
||||
AIAgentInput::UserQuery { query, .. } => Some(query.clone()),
|
||||
AIAgentInput::InvokeSkill { skill, .. } => Some(format!("/{}", skill.name)),
|
||||
AIAgentInput::AutoCodeDiffQuery { .. }
|
||||
AIAgentInput::CommandCompletionAssessment { .. }
|
||||
| AIAgentInput::AutoCodeDiffQuery { .. }
|
||||
| AIAgentInput::ResumeConversation { .. }
|
||||
| AIAgentInput::InitProjectRules { .. }
|
||||
| AIAgentInput::CreateEnvironment { .. }
|
||||
@@ -414,9 +440,17 @@ enum RigRequestMode {
|
||||
Plan,
|
||||
Orchestrate,
|
||||
Cli,
|
||||
CompletedCommandAssessment,
|
||||
}
|
||||
|
||||
fn request_mode(inputs: &[AIAgentInput]) -> RigRequestMode {
|
||||
if inputs
|
||||
.iter()
|
||||
.any(|input| matches!(input, AIAgentInput::CommandCompletionAssessment { .. }))
|
||||
{
|
||||
return RigRequestMode::CompletedCommandAssessment;
|
||||
}
|
||||
|
||||
for input in inputs {
|
||||
// A direct-provider follow-up carries an LRC snapshot as an action result rather than
|
||||
// as a user query with `running_command`. Treat that result as a CLI-monitor turn so the
|
||||
@@ -747,6 +781,9 @@ fn build_system_prompt(
|
||||
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",
|
||||
),
|
||||
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",
|
||||
),
|
||||
}
|
||||
prompt.push_str("## Available Tools\n");
|
||||
if tools.is_empty() {
|
||||
|
||||
@@ -248,6 +248,77 @@ fn rig_prompt_requires_follow_through_without_manual_continue_prompts() {
|
||||
assert!(prompt.contains("After each tool result, choose and perform the next necessary step"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn completed_command_assessment_uses_root_history_without_tools_or_monitor_instructions() {
|
||||
let block_id: galaxy_terminal::model::BlockId = "completed-lrc-test".to_string().into();
|
||||
let mcp_tool = serde_json::from_value(serde_json::json!({
|
||||
"name": "echo",
|
||||
"description": "Echo input",
|
||||
"inputSchema": {"type": "object"}
|
||||
}))
|
||||
.unwrap();
|
||||
let mut params = RequestParams::new_for_test();
|
||||
params.root_task_id = Some("root-task".to_string());
|
||||
params.message_history = vec![galaxy_agent_core::ConversationMessage {
|
||||
role: MessageRole::User,
|
||||
content: MessageContent::Text("Prior root conversation".to_string()),
|
||||
}];
|
||||
params.mcp_context = Some(MCPContext {
|
||||
resources: Vec::new(),
|
||||
tools: Vec::new(),
|
||||
servers: vec![MCPServer {
|
||||
id: "11111111-1111-4111-8111-111111111111".to_string(),
|
||||
name: "Echo".to_string(),
|
||||
description: String::new(),
|
||||
resources: Vec::new(),
|
||||
tools: vec![mcp_tool],
|
||||
}],
|
||||
});
|
||||
params.input = vec![AIAgentInput::CommandCompletionAssessment {
|
||||
prompt: "Report the final result to the user.".to_string(),
|
||||
context: Arc::from([]),
|
||||
completed_command: crate::ai::agent::RunningCommand {
|
||||
command: "bash loop.sh".to_string(),
|
||||
block_id,
|
||||
grid_contents: "All 42 checks passed.".to_string(),
|
||||
cursor: String::new(),
|
||||
requested_command_id: None,
|
||||
is_alt_screen_active: false,
|
||||
},
|
||||
}];
|
||||
|
||||
let prepared = prepare_rig_turn(
|
||||
&config(),
|
||||
params,
|
||||
vec![ToolType::RunShellCommand, ToolType::CallMcpTool],
|
||||
vec![ToolType::ReadShellCommandOutput],
|
||||
);
|
||||
let prompt = prepared.request.system_prompt.expect("system prompt");
|
||||
|
||||
assert_eq!(prepared.task_id, "root-task");
|
||||
assert_eq!(prepared.user_query, None);
|
||||
assert!(prepared.request.tools.is_empty());
|
||||
assert!(prepared.mcp_tool_aliases.is_empty());
|
||||
assert!(prompt.contains("## Completed Command Assessment"));
|
||||
assert!(prompt.contains("No tools are available"));
|
||||
assert!(!prompt.contains("## Running Command Monitor"));
|
||||
assert!(!prompt.contains("next assistant output MUST be a tool call"));
|
||||
assert!(prepared.persistent_messages.iter().any(|message| matches!(
|
||||
&message.content,
|
||||
MessageContent::Text(text) if text == "Prior root conversation"
|
||||
)));
|
||||
assert!(prepared.persistent_messages.iter().any(|message| matches!(
|
||||
&message.content,
|
||||
MessageContent::Text(text)
|
||||
if text.contains("[Completed command: bash loop.sh]")
|
||||
&& text.contains("[Command ID: completed-lrc-test]")
|
||||
&& text.contains("[Final terminal output:\nAll 42 checks passed.")
|
||||
&& text.contains("Report the final result to the user.")
|
||||
)));
|
||||
assert_eq!(prepared.request.messages, prepared.persistent_messages);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lrc_snapshot_follow_up_uses_the_cli_monitor_prompt_and_tools() {
|
||||
let block_id: galaxy_terminal::model::BlockId = "precmd-lrc-test".to_string().into();
|
||||
|
||||
Reference in New Issue
Block a user