Make recommendation pane routing host-owned - #534
Conversation
Capture the source pane used to build each prompt and force recommendation actions to use that host-resolved target instead of model-generated pane IDs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9186409-ee54-4e89-8a75-51e00eacaf2f
There was a problem hiding this comment.
Pull request overview
This PR makes recommendation execution routing host-owned by capturing the authoritative target pane used when assembling a planner prompt, then binding send / panel actions to that host-resolved target at execution time (instead of trusting model-provided pane IDs like "parent": "10").
Changes:
- Capture and persist a per-turn
target_pane_id(resolved from Terminal Context) and plumb it through prompt building + app turn state. - Bind/override action targets at the executor boundary (
bind_choice_target) and reject target-requiring actions when no host target is available. - Update the planner prompt guidance to omit model-owned
parentfields, and extend tests to cover host-owned target behavior.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tools/wta/src/ui/chat.rs | Updates test fixtures for the new SubmittedPrompt.target_pane_id field. |
| tools/wta/src/protocol/acp/prompt_context.rs | Refactors planner terminal context building to return both JSON + authoritative target pane; plumbs this into provider context. |
| tools/wta/src/protocol/acp/prompt_builder.rs | Includes injected planner terminal context in prompt assembly and returns a resolved target pane for turn binding. |
| tools/wta/src/protocol/acp/client.rs | Emits AppEvent::PromptTargetResolved to bind the authoritative target pane to the matching turn. |
| tools/wta/src/coordinator.rs | Adds ChoiceExecution.target_pane_id and introduces bind_choice_target to override/reject target-requiring actions at execution time. |
| tools/wta/src/app/turn_state.rs | Adds SubmittedPrompt.target_pane_id to persist the host-resolved target associated with a prompt. |
| tools/wta/src/app/autofix.rs | Ensures autofix-submitted prompts carry the authoritative pane target and execution passes it to the coordinator. |
| tools/wta/src/app.rs | Renames and broadens the target-binding event handling (PromptTargetResolved) and binds targets by prompt id (robust across tab renames). |
| tools/wta/src/app_turn.rs | Stops filtering recommendation sets by model-provided coordinator targets; execution now relies on host binding. |
| tools/wta/src/app_tests.rs | Updates fixtures and adds coverage for /fix and prompt target binding behavior. |
| tools/wta/src/app_keys.rs | Ensures planner prompts carry the captured source pane session id for stable routing across focus changes. |
| tools/wta/src/app_events.rs | Routes PromptTargetResolved to the new binding handler. |
| tools/wta/src/app_contracts/event.rs | Renames the event variant to PromptTargetResolved. |
| tools/wta/prompts/terminal-agent.md | Updates planner instructions/examples so the model omits parent; host binds to captured activeTarget. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Suppressed comments (1)
tools/wta/src/protocol/acp/prompt_context.rs:311
build_terminal_contextalways falls back toresolve_pane_by_session_idwhenpane_context.source_pane_idis present. That helper enumerates windows/tabs/panes, which is significantly more expensive thanwt_get_active_pane()and will now run for most planner prompts (sincesource_pane_idis set at submission). Consider short-circuiting when the current active pane already matches the capturedsource_pane_id, and only doing the full enumeration when focus actually changed.
let active = match pane_context.and_then(|context| context.source_pane_id.as_deref()) {
Some(source) => resolve_pane_by_session_id(shell_mgr, source).await?,
None => shell_mgr.wt_get_active_pane().await.ok()?,
};
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f3637cc5-ad00-400b-a426-92a7ddfc94e5
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Suppressed comments (2)
tools/wta/src/app_keys.rs:811
source_pane_idis being set fromself.source_session_id, butsource_session_idis only read once from theWTA_SOURCE_SESSION_IDenv var at helper startup (see helper/runtime.rs). That means planner prompts can get “stuck” routing to the tab’s initial pane instead of the pane that was active when the user actually submitted the prompt (e.g., after splitting panes or changing focus within the tab). This undermines the goal of capturing an authoritative per-prompt target.
tab_id: self.tab_id.clone(),
window_id: self.window_id.clone(),
cwd: self.source_cwd.clone(),
source_pane_id: self.source_session_id.clone(),
};
tools/wta/src/app.rs:4426
/fixnow seedsPaneContext.source_pane_id(and the prompt’starget_pane_id) fromself.source_session_id, but that value is only initialized fromWTA_SOURCE_SESSION_IDat helper startup. If the user’s working pane changes after the helper is spawned (pane splits/focus changes), manual/fixcan resolve context/output and route execution to the wrong pane.
pane_id: self.pane_id.clone(),
tab_id: Some(target_tab_id.clone()),
window_id: self.window_id.clone(),
cwd: None,
source_pane_id: source_pane_id.clone(),
Summary
/fixRoot cause
The planner prompt asked the model to return
parent, and its JSON example hard-coded"parent": "10". The parser accepted that non-empty value and passed it unchanged towtcli, even though pane addressing requires a WT session GUID.Tests
cargo test --manifest-path tools\wta\Cargo.toml(1221 passed)