Preserve agent question answers and plan output
This commit is contained in:
@@ -24,7 +24,7 @@ Environment variables:
|
|||||||
- `WS_SERVER_URL` - WebSocket endpoint (default: `ws://localhost:8080/graphql/v2`)
|
- `WS_SERVER_URL` - WebSocket endpoint (default: `ws://localhost:8080/graphql/v2`)
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
- During interactive bug-fix verification, make the requested code changes first and run only `cargo run --bin galaxy-oss` for the user to verify. Leave the app running for the user; do not stop it or treat the command timeout as a failure. Do not run `cargo fmt`, `cargo check`, `cargo clippy`, presubmit, or other validation commands until the user confirms the fix.
|
- During interactive feature and bug-fix verification, make the requested code changes first and run only `cargo run --bin galaxy-oss` for the user to verify. Leave the app running for the user; do not stop it or treat the command timeout as a failure. Do not run `cargo fmt`, `cargo check`, `cargo test`, `cargo clippy`, presubmit, or other validation commands until the user confirms the behavior. Run formatting, tests, and linting only as the final cleanup step after interactive approval.
|
||||||
- `cargo nextest run --no-fail-fast --workspace --exclude command-signatures-v2` - Run tests with nextest
|
- `cargo nextest run --no-fail-fast --workspace --exclude command-signatures-v2` - Run tests with nextest
|
||||||
- `cargo nextest run -p galaxy_completer --features v2` - Run completer tests with v2 features
|
- `cargo nextest run -p galaxy_completer --features v2` - Run completer tests with v2 features
|
||||||
- `cargo test --doc` - Run doc tests
|
- `cargo test --doc` - Run doc tests
|
||||||
|
|||||||
@@ -807,7 +807,7 @@ fn build_system_prompt(
|
|||||||
match mode {
|
match mode {
|
||||||
RigRequestMode::Normal => {}
|
RigRequestMode::Normal => {}
|
||||||
RigRequestMode::Plan => prompt.push_str(
|
RigRequestMode::Plan => prompt.push_str(
|
||||||
"## Plan Mode\nInspect and produce an implementation-ready plan. Do not edit files or perform state-changing actions.\n\n",
|
"## Plan Mode\nInspect and produce an implementation-ready plan. Do not edit files or perform state-changing actions. Research as needed, then finish by calling `create_plan` to write the plan with the built-in planning tools. If a plan document already exists for this task, call `edit_plan` instead. Do not return the plan only as prose, and do not claim completion until the plan tool succeeds.\n\n",
|
||||||
),
|
),
|
||||||
RigRequestMode::Orchestrate => prompt.push_str(
|
RigRequestMode::Orchestrate => prompt.push_str(
|
||||||
"## Orchestration Mode\nDelegate only independent, bounded work where parallelism materially helps, then synthesize the results.\n\n",
|
"## Orchestration Mode\nDelegate only independent, bounded work where parallelism materially helps, then synthesize the results.\n\n",
|
||||||
|
|||||||
@@ -200,6 +200,42 @@ fn normal_turn_advertises_plan_creation_and_corrects_false_unavailability_claims
|
|||||||
assert!(prompt.contains("do not implement it until they approve"));
|
assert!(prompt.contains("do not implement it until they approve"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plan_turn_requires_a_plan_tool_result() {
|
||||||
|
let mut params = RequestParams::new_for_test();
|
||||||
|
let mut input = user_query("Design the implementation");
|
||||||
|
let AIAgentInput::UserQuery {
|
||||||
|
user_query_mode, ..
|
||||||
|
} = &mut input
|
||||||
|
else {
|
||||||
|
unreachable!("user_query returns a user query input");
|
||||||
|
};
|
||||||
|
*user_query_mode = UserQueryMode::Plan;
|
||||||
|
params.input = vec![input];
|
||||||
|
|
||||||
|
let prepared = prepare_rig_turn(
|
||||||
|
&config(),
|
||||||
|
params,
|
||||||
|
vec![ToolType::CreateDocuments, ToolType::EditDocuments],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
let prompt = prepared.request.system_prompt.expect("system prompt");
|
||||||
|
|
||||||
|
assert!(prepared
|
||||||
|
.request
|
||||||
|
.tools
|
||||||
|
.iter()
|
||||||
|
.any(|tool| tool.name == "create_plan"));
|
||||||
|
assert!(prepared
|
||||||
|
.request
|
||||||
|
.tools
|
||||||
|
.iter()
|
||||||
|
.any(|tool| tool.name == "edit_plan"));
|
||||||
|
assert!(prompt.contains("finish by calling `create_plan`"));
|
||||||
|
assert!(prompt.contains("call `edit_plan` instead"));
|
||||||
|
assert!(prompt.contains("Do not return the plan only as prose"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_tools_turn_flattens_historical_tool_protocol_messages() {
|
fn no_tools_turn_flattens_historical_tool_protocol_messages() {
|
||||||
let mut params = RequestParams::new_for_test();
|
let mut params = RequestParams::new_for_test();
|
||||||
|
|||||||
@@ -294,13 +294,44 @@ impl AIAgentActionResultType {
|
|||||||
| Self::FetchConversation(_)
|
| Self::FetchConversation(_)
|
||||||
| Self::StartAgent(_)
|
| Self::StartAgent(_)
|
||||||
| Self::SendMessageToAgent(_)
|
| Self::SendMessageToAgent(_)
|
||||||
| Self::AskUserQuestion(_)
|
|
||||||
| Self::WaitForEvents(_) => self.to_string(),
|
| Self::WaitForEvents(_) => self.to_string(),
|
||||||
|
Self::AskUserQuestion(result) => ask_user_question_result_content(result),
|
||||||
Self::RunAgents(result) => result.model_content(),
|
Self::RunAgents(result) => result.model_content(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ask_user_question_result_content(result: &AskUserQuestionResult) -> String {
|
||||||
|
match result {
|
||||||
|
AskUserQuestionResult::Success { answers } => {
|
||||||
|
let answers = answers
|
||||||
|
.iter()
|
||||||
|
.map(|answer| match answer {
|
||||||
|
AskUserQuestionAnswerItem::Answered {
|
||||||
|
question_id,
|
||||||
|
selected_options,
|
||||||
|
other_text,
|
||||||
|
} => serde_json::json!({
|
||||||
|
"question_id": question_id,
|
||||||
|
"selected_options": selected_options,
|
||||||
|
"other_text": other_text,
|
||||||
|
}),
|
||||||
|
AskUserQuestionAnswerItem::Skipped { question_id } => serde_json::json!({
|
||||||
|
"question_id": question_id,
|
||||||
|
"skipped": true,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
serde_json::json!({ "answers": answers }).to_string()
|
||||||
|
}
|
||||||
|
AskUserQuestionResult::SkippedByAutoApprove { question_ids } => serde_json::json!({
|
||||||
|
"skipped_by_auto_approve": question_ids,
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
AskUserQuestionResult::Error(_) | AskUserQuestionResult::Cancelled => result.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn command_result_content(command: Option<&str>, output: &str, exit_code: i32) -> String {
|
fn command_result_content(command: Option<&str>, output: &str, exit_code: i32) -> String {
|
||||||
let command = command
|
let command = command
|
||||||
.map(|command| format!("Command: {command}\n"))
|
.map(|command| format!("Command: {command}\n"))
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
use super::{
|
use super::{
|
||||||
AIAgentActionResultType, RequestCommandOutputResult, RunAgentsAgentOutcome,
|
AIAgentActionResultType, AskUserQuestionAnswerItem, AskUserQuestionResult,
|
||||||
RunAgentsAgentOutcomeKind, RunAgentsLaunchedExecutionMode, RunAgentsResult, StartAgentResult,
|
RequestCommandOutputResult, RunAgentsAgentOutcome, RunAgentsAgentOutcomeKind,
|
||||||
StartAgentVersion,
|
RunAgentsLaunchedExecutionMode, RunAgentsResult, StartAgentResult, StartAgentVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ask_user_question_model_content_includes_selected_answers() {
|
||||||
|
let result = AIAgentActionResultType::AskUserQuestion(AskUserQuestionResult::Success {
|
||||||
|
answers: vec![AskUserQuestionAnswerItem::Answered {
|
||||||
|
question_id: "scope".to_string(),
|
||||||
|
selected_options: vec!["Provider-neutral pane plus agent tool".to_string()],
|
||||||
|
other_text: String::new(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let content: serde_json::Value = serde_json::from_str(&result.model_content()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
content["answers"][0]["selected_options"][0],
|
||||||
|
"Provider-neutral pane plus agent tool"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shell_execution_error_is_failed_but_not_cancelled() {
|
fn shell_execution_error_is_failed_but_not_cancelled() {
|
||||||
let result =
|
let result =
|
||||||
|
|||||||
Reference in New Issue
Block a user