From a8da26508757bc710dc083a6a216b6bd47f29d7d Mon Sep 17 00:00:00 2001 From: netcatty Date: Sun, 13 Sep 2026 15:47:07 +0800 Subject: [PATCH] Add Pi UI-prompt events so the pane reports waiting-for-user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shared Pi / Oh My Pi bridge only subscribes to four lifecycle events, so a pane running Pi never shows "waiting for you" while a dialog is open — `ask_user_question`, permission gates and any other extension prompt all run under the hood with the status dot still reading "working". tty7 already has the vocabulary for this (`question-asked`, `permission-request`), and `AgentEventKind::QuestionAsked` / `PermissionRequest` already map to `AgentStatus::Waiting` in `cli_agent.rs`. The Pi extension seam to feed them is `pi.on("ui_prompt_start")`, which fires around every blocking user-facing prompt with `event.kind` telling select / confirm / input / editor / custom apart. Two handlers, each guarded on its own so an Oh My Pi fork that does not expose the hook loses only that event rather than the whole bridge: - `ui_prompt_start` → `permission-request` for `kind === "confirm"` (a permission or destructive-action gate), `question-asked` otherwise. - `ui_prompt_end` → `prompt-submit` so the status returns to working once the dialog closes. Without it the pane would stay on "waiting" until the next stop, which is wrong for the model's continued work after an answer. Deliberately not included: `tool-complete`. The Pi bridge emits with `spawnSync`, so one event per tool call would block the extension host for the duration of a process spawn on every read/grep/edit. The test that asserts the bridge's subscriptions now covers both new event names. Verified: the `format!` template still compiles and renders both new handlers, and the bridge contains every string the test asserts. --- crates/tty7-core/src/core/agent_hooks.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/tty7-core/src/core/agent_hooks.rs b/crates/tty7-core/src/core/agent_hooks.rs index e8865107..97cac9a6 100644 --- a/crates/tty7-core/src/core/agent_hooks.rs +++ b/crates/tty7-core/src/core/agent_hooks.rs @@ -1449,6 +1449,21 @@ export default function (pi: ExtensionAPI) {{ try {{ pi.on("session_start", (_event, ctx) => emit("session-start", ctx)); }} catch {{}} + // Pi-only: Oh My Pi may not expose the UI-prompt hooks, so each one is + // guarded on its own — a fork that rejects the event name must not take the + // rest of the bridge down with it. Without these the pane never reports + // "waiting for you" while a dialog is open, and the event vocabulary already + // carries question-asked / permission-request for exactly that. + try {{ + pi.on("ui_prompt_start", (event, ctx) => {{ + emit(event.kind === "confirm" ? "permission-request" : "question-asked", ctx); + }}); + }} catch {{}} + // The dialog closed, so the agent is working again. Without this the pane + // would stay on "waiting" until the next stop. + try {{ + pi.on("ui_prompt_end", (_event, ctx) => emit("prompt-submit", ctx)); + }} catch {{}} }} "# )) @@ -2205,6 +2220,8 @@ mod tests { "agent_start", "agent_end", "session_shutdown", + "ui_prompt_start", + "ui_prompt_end", ] { assert!( bridge.contains(&format!(r#"pi.on("{event}""#)),