fix: ACP session/prompt now drives the full tool-execution loop
run_prompt_turn previously performed a single completion and returned,
leaving tool calls unexecuted. It now mirrors start_directive's loop:
call -> execute tools -> merge results -> continue until no tool results,
then check the pending-agents guardrail before returning.
The pending-agents guardrail injects a reminder prompt when sub-agents
are pending, matching the same loop termination semantics as --headless.
The session is NOT exited between turns (unlike start_directive) so
multi-turn ACP conversations retain history across session/prompt calls.
Manual gate (tool-probe agent, fs_write + fs_read tools):
id 3 result: {"output":"DONE:probe.txt","stopReason":"end_turn"}
probe.txt exists: YES, content: hello
This commit is contained in:
+23
-5
@@ -1,6 +1,7 @@
|
|||||||
use super::types::{METHOD_NOT_FOUND, PARSE_ERROR, Request, Response};
|
use super::types::{METHOD_NOT_FOUND, PARSE_ERROR, Request, Response};
|
||||||
use crate::client::call_chat_completions_streaming;
|
use crate::client::call_chat_completions_streaming;
|
||||||
use crate::config::{Input, RenderMode, RequestContext};
|
use crate::config::{Input, RenderMode, RequestContext};
|
||||||
|
use crate::function::supervisor::{GuardrailAction, check_pending_agents_guardrail};
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use crate::utils::AbortSignal;
|
use crate::utils::AbortSignal;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@@ -198,16 +199,33 @@ async fn run_prompt_turn(
|
|||||||
abort: AbortSignal,
|
abort: AbortSignal,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
ctx.render_mode = RenderMode::Silent;
|
ctx.render_mode = RenderMode::Silent;
|
||||||
let input = Input::from_str(ctx, text, None)?;
|
let mut input = Input::from_str(ctx, text, None)?;
|
||||||
|
loop {
|
||||||
ctx.before_chat_completion(&input)?;
|
ctx.before_chat_completion(&input)?;
|
||||||
let client = input.create_client()?;
|
let client = input.create_client()?;
|
||||||
let (output, tool_results) =
|
let (output, tool_results) =
|
||||||
call_chat_completions_streaming(&input, client.as_ref(), ctx, abort).await?;
|
call_chat_completions_streaming(&input, client.as_ref(), ctx, abort.clone()).await?;
|
||||||
let app = Arc::clone(&ctx.app.config);
|
let app = Arc::clone(&ctx.app.config);
|
||||||
|
|
||||||
ctx.after_chat_completion(app.as_ref(), &input, &output, &tool_results)?;
|
ctx.after_chat_completion(app.as_ref(), &input, &output, &tool_results)?;
|
||||||
|
if !tool_results.is_empty() {
|
||||||
Ok(output)
|
input = input.merge_tool_results(output, tool_results);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match check_pending_agents_guardrail(ctx) {
|
||||||
|
GuardrailAction::Inject(prompt) => {
|
||||||
|
input = Input::from_str(ctx, &prompt, None)?;
|
||||||
|
}
|
||||||
|
GuardrailAction::ForceTerminate(ids) => {
|
||||||
|
warn!(
|
||||||
|
"Pending-agent guardrail force-cancelled {} agent(s): {:?}",
|
||||||
|
ids.len(),
|
||||||
|
ids
|
||||||
|
);
|
||||||
|
return Ok(output);
|
||||||
|
}
|
||||||
|
GuardrailAction::NoAction => return Ok(output),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_session_cancel(state: &mut AcpServerState) {
|
fn handle_session_cancel(state: &mut AcpServerState) {
|
||||||
|
|||||||
Reference in New Issue
Block a user