Skip to content

Commit 37d625e

Browse files
committed
Revert "fix(acp): Handle ACP cancel-tail prompt ordering (#442)"
This reverts commit 959dedf.
1 parent b765e54 commit 37d625e

23 files changed

Lines changed: 15 additions & 1874 deletions

nori-rs/acp/docs.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -846,8 +846,6 @@ When `Op::Interrupt` fires, the ACP backend now only submits `InboundEvent::Canc
846846
- `SessionPhaseChanged(Idle)` and `PromptCompleted { stop_reason, last_agent_message }` are emitted only when that prompt response is reduced
847847
- queued follow-up prompts remain in the reducer-owned outbound queue until an eligible drain point (`stop_reason: end_turn`)
848848

849-
`SacpConnection::prompt()` also carries a small amount of session-local transport state so cancellation tails can be absorbed without widening the public phase model. If the previous prompt ended with `Cancelled`, the next prompt request may receive one or more immediate empty `end_turn` responses before the agent starts working on the user's real follow-up prompt. The connection layer now treats those empty terminal responses as stale cancel-tail cleanup and retries the same ACP prompt request until either streamed updates arrive or a non-stale stop reason is observed. That keeps the reducer contract unchanged: it still only sees the final logical completion for the user-facing prompt turn.
850-
851849
This removes the old synthetic interrupt-abort fast-path that treated cancel as immediate idle. The TUI now renders ACP interrupt state from reducer-owned phase/completion projections instead of inferring prompt ownership from interrupt timing.
852850

853851
**Tool Classification System:**

nori-rs/acp/src/backend/session_reducer.rs

Lines changed: 0 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use nori_protocol::session_runtime::SessionPhase;
1818
use nori_protocol::session_runtime::SessionRuntime;
1919
use nori_protocol::session_runtime::TranscriptMessage;
2020
use nori_protocol::session_runtime::TranscriptRole;
21-
use tracing::debug;
2221

2322
/// Everything that can affect [`SessionRuntime`] state.
2423
#[derive(Debug)]
@@ -41,32 +40,6 @@ pub enum InboundEvent {
4140
LoadSubmit { request_id: String },
4241
}
4342

44-
pub(super) fn inbound_event_kind(event: &InboundEvent) -> &'static str {
45-
match event {
46-
InboundEvent::Notification(update) => crate::connection::session_update_kind(update),
47-
InboundEvent::PromptResponse { .. } => "prompt_response",
48-
InboundEvent::PromptFailed => "prompt_failed",
49-
InboundEvent::LoadResponse => "load_response",
50-
InboundEvent::PermissionRequest { .. } => "permission_request",
51-
InboundEvent::PromptSubmit(_) => "prompt_submit",
52-
InboundEvent::CancelSubmit => "cancel_submit",
53-
InboundEvent::LoadSubmit { .. } => "load_submit",
54-
}
55-
}
56-
57-
pub(super) fn session_phase_label(phase: &SessionPhase) -> &'static str {
58-
match phase {
59-
SessionPhase::Idle => "idle",
60-
SessionPhase::Loading { .. } => "loading",
61-
SessionPhase::Prompt {
62-
cancelling: true, ..
63-
} => "cancelling",
64-
SessionPhase::Prompt {
65-
cancelling: false, ..
66-
} => "prompt",
67-
}
68-
}
69-
7043
/// Side effects the caller must execute after reduction.
7144
#[derive(Debug, PartialEq)]
7245
pub enum SideEffect {
@@ -143,12 +116,6 @@ fn reduce_prompt_submit(
143116
) {
144117
if runtime.phase != SessionPhase::Idle {
145118
runtime.queue.push_back(prompt);
146-
debug!(
147-
target: "acp_event_flow",
148-
phase = session_phase_label(&runtime.phase),
149-
queue_len = runtime.queue.len(),
150-
"Queued prompt while another session request is active"
151-
);
152119
out.events.push(ClientEvent::QueueChanged(QueueChanged {
153120
prompts: queued_prompt_texts(runtime),
154121
}));
@@ -159,7 +126,6 @@ fn reduce_prompt_submit(
159126
}
160127

161128
fn start_prompt(runtime: &mut SessionRuntime, prompt: QueuedPrompt, out: &mut ReduceOutput) {
162-
let phase_before = session_phase_label(&runtime.phase);
163129
let request_id = new_request_id();
164130

165131
// Build ACP content blocks from the queued prompt.
@@ -189,15 +155,6 @@ fn start_prompt(runtime: &mut SessionRuntime, prompt: QueuedPrompt, out: &mut Re
189155
});
190156
}
191157

192-
debug!(
193-
target: "acp_event_flow",
194-
request_id = %request_id,
195-
prompt_kind = ?prompt.kind,
196-
phase_before,
197-
queue_len = runtime.queue.len(),
198-
"Reducer started prompt and emitted session/prompt side effect"
199-
);
200-
201158
out.events
202159
.push(ClientEvent::SessionPhaseChanged(runtime.phase_view()));
203160
out.side_effects.push(SideEffect::SendPrompt {
@@ -242,20 +199,6 @@ fn reduce_cancel_submit(runtime: &mut SessionRuntime, out: &mut ReduceOutput) {
242199
}
243200
}
244201

245-
debug!(
246-
target: "acp_event_flow",
247-
request_id = %owner_id,
248-
pending_permission_requests = runtime
249-
.active
250-
.as_ref()
251-
.map_or(0, |active| active.pending_permission_requests.len()),
252-
tool_calls = runtime
253-
.active
254-
.as_ref()
255-
.map_or(0, |active| active.tool_call_ids.len()),
256-
"Reducer marked the active prompt as cancelling"
257-
);
258-
259202
out.events
260203
.push(ClientEvent::SessionPhaseChanged(runtime.phase_view()));
261204
out.side_effects.push(SideEffect::SendCancel);
@@ -271,22 +214,6 @@ fn reduce_prompt_response(
271214
stop_reason: acp::StopReason,
272215
out: &mut ReduceOutput,
273216
) {
274-
let active_request_id = runtime
275-
.active
276-
.as_ref()
277-
.map(|active| active.request_id.clone())
278-
.unwrap_or_else(|| "<none>".to_string());
279-
let phase_before = session_phase_label(&runtime.phase);
280-
let queue_len_before = runtime.queue.len();
281-
debug!(
282-
target: "acp_event_flow",
283-
active_request_id,
284-
phase_before,
285-
queue_len_before,
286-
?stop_reason,
287-
"Reducer received prompt response"
288-
);
289-
290217
if !matches!(runtime.phase, SessionPhase::Prompt { .. }) {
291218
out.events.push(ClientEvent::Warning(WarningInfo {
292219
message: "Received prompt response while not in Prompt phase".to_string(),
@@ -299,15 +226,6 @@ fn reduce_prompt_response(
299226

300227
runtime.phase = SessionPhase::Idle;
301228

302-
debug!(
303-
target: "acp_event_flow",
304-
active_request_id,
305-
?stop_reason,
306-
should_drain_queue,
307-
queue_len_after_finalize = runtime.queue.len(),
308-
"Reducer finalized prompt response"
309-
);
310-
311229
out.events
312230
.push(ClientEvent::SessionPhaseChanged(runtime.phase_view()));
313231
out.events
@@ -325,18 +243,6 @@ fn reduce_prompt_response(
325243
}
326244

327245
fn reduce_prompt_failed(runtime: &mut SessionRuntime, out: &mut ReduceOutput) {
328-
let active_request_id = runtime
329-
.active
330-
.as_ref()
331-
.map(|active| active.request_id.clone())
332-
.unwrap_or_else(|| "<none>".to_string());
333-
debug!(
334-
target: "acp_event_flow",
335-
active_request_id,
336-
phase = session_phase_label(&runtime.phase),
337-
"Reducer received prompt failure"
338-
);
339-
340246
if !matches!(runtime.phase, SessionPhase::Prompt { .. }) {
341247
out.events.push(ClientEvent::Warning(WarningInfo {
342248
message: "Received prompt failure while not in Prompt phase".to_string(),
@@ -404,18 +310,6 @@ fn reduce_notification(
404310
normalizer: &mut ClientEventNormalizer,
405311
out: &mut ReduceOutput,
406312
) {
407-
debug!(
408-
target: "acp_event_flow",
409-
update_kind = crate::connection::session_update_kind(&update),
410-
phase = session_phase_label(&runtime.phase),
411-
active_request_id = runtime
412-
.active
413-
.as_ref()
414-
.map(|active| active.request_id.as_str())
415-
.unwrap_or("<none>"),
416-
"Reducer received session/update"
417-
);
418-
419313
// Session metadata updates are accepted in any phase.
420314
if is_session_metadata_update(&update) {
421315
reduce_metadata_update(runtime, &update, normalizer, out);

nori-rs/acp/src/backend/session_reducer/tests.rs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ use pretty_assertions::assert_eq;
1111

1212
use super::InboundEvent;
1313
use super::SideEffect;
14-
use super::inbound_event_kind;
1514
use super::reduce;
16-
use super::session_phase_label;
1715

1816
fn new_runtime() -> SessionRuntime {
1917
SessionRuntime::new()
@@ -127,41 +125,6 @@ fn prompt_response_transitions_to_idle() {
127125
)));
128126
}
129127

130-
#[test]
131-
fn inbound_event_kind_labels_prompt_response() {
132-
assert_eq!(
133-
inbound_event_kind(&InboundEvent::PromptResponse {
134-
stop_reason: acp::StopReason::Cancelled,
135-
}),
136-
"prompt_response"
137-
);
138-
}
139-
140-
#[test]
141-
fn session_phase_label_labels_known_phases() {
142-
assert_eq!(session_phase_label(&SessionPhase::Idle), "idle");
143-
assert_eq!(
144-
session_phase_label(&SessionPhase::Prompt {
145-
request_id: "req-1".to_string(),
146-
cancelling: false,
147-
}),
148-
"prompt"
149-
);
150-
assert_eq!(
151-
session_phase_label(&SessionPhase::Prompt {
152-
request_id: "req-1".to_string(),
153-
cancelling: true,
154-
}),
155-
"cancelling"
156-
);
157-
assert_eq!(
158-
session_phase_label(&SessionPhase::Loading {
159-
request_id: "req-2".to_string(),
160-
}),
161-
"loading"
162-
);
163-
}
164-
165128
// =========================================================================
166129
// 2. Cancel semantics
167130
// =========================================================================

0 commit comments

Comments
 (0)