diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md index 5e48a243..92bfa130 100644 --- a/docs/next/CHANGELOG.md +++ b/docs/next/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixed - New lifecycle event subscriptions now stream only events emitted after subscription begins instead of replaying retained history. (#1270) - Windows users whose endpoint security blocks the fileless PowerShell install command can now use a local `install.cmd` bootstrap; installer downloads use `curl.exe` while preserving package checksum verification. (#2751) +- Oh My Pi panes now stay working when a turn ends with an automatic continuation already scheduled, instead of briefly reporting idle and completing `agent wait` early. (#2851, thanks @taoeffect) - Retained mouse selections now copy when Ctrl+C or Cmd+C arrives before a delayed mouse release instead of forwarding the copy shortcut to the pane. (#3100, thanks @moret) - Removing a background worktree workspace no longer changes focus to its parent workspace. (#3098) - Prefix bindings such as `prefix+|` now recognize characters produced by macOS Option and custom keyboard layouts, while exact chords such as `prefix+alt+w` keep priority. (#3079, thanks @vlcinsky) diff --git a/src/integration/assets/herdr-agent-state.test.ts b/src/integration/assets/herdr-agent-state.test.ts index 94081e6d..2a86f0ca 100644 --- a/src/integration/assets/herdr-agent-state.test.ts +++ b/src/integration/assets/herdr-agent-state.test.ts @@ -498,6 +498,45 @@ test("Oh My Pi retries working before a queued idle state", async () => { expect(requestState(attemptedRequests[2])).toBe("idle"); }); +test("Oh My Pi keeps working when a turn ends with a scheduled continuation", async () => { + const requests = await startRecordingServer("omp-will-continue"); + process.env.HERDR_OMP_IDLE_DEBOUNCE_MS = "0"; + const { handlers, pi } = createExtensionHarness(); + + const { default: install } = await importFresh("./omp/herdr-agent-state.ts"); + install(pi); + + let idle = true; + const context = { + hasUI: true, + isIdle: () => idle, + sessionManager: { + getSessionFile: () => undefined, + getSessionId: () => undefined, + }, + }; + + handlers.get("session_start")?.({ reason: "startup" }, context); + await waitFor(() => requestStates(requests).length === 1); + + idle = false; + handlers.get("agent_start")?.({}, context); + await waitFor(() => requestStates(requests).length === 2); + expect(requestStates(requests)).toEqual(["idle", "working"]); + + // OMP already scheduled an automatic continuation, so this loop end is not a + // user-visible settle and must not publish idle. See issue #2851. + handlers.get("agent_end")?.({ messages: [], willContinue: true }, context); + await Bun.sleep(50); + expect(requestStates(requests)).toEqual(["idle", "working"]); + + // The real terminal end still settles the pane. + idle = true; + handlers.get("agent_end")?.({ messages: [] }, context); + await waitFor(() => requestStates(requests).length === 3); + expect(requestStates(requests)).toEqual(["idle", "working", "idle"]); +}); + test("Pi retries working state after an unanswered socket attempt", async () => { const { attemptedRequests, deliveredRequests, connectionCount } = await startDroppedFirstResponseServer("pi-retry"); diff --git a/src/integration/assets/omp/herdr-agent-state.ts b/src/integration/assets/omp/herdr-agent-state.ts index 88f804aa..0e493787 100644 --- a/src/integration/assets/omp/herdr-agent-state.ts +++ b/src/integration/assets/omp/herdr-agent-state.ts @@ -2,7 +2,7 @@ // managed by herdr; reinstalling or updating the integration overwrites this file. // add custom hooks/plugins beside this file instead of editing it. // HERDR_INTEGRATION_ID=omp -// HERDR_INTEGRATION_VERSION=8 +// HERDR_INTEGRATION_VERSION=9 // @ts-nocheck import net from "node:net"; @@ -442,6 +442,11 @@ export default function (pi) { // cancel the retry hold and publish a false Idle. return; } + if (event?.willContinue === true) { + // A continuation is already scheduled, so this end is not a settle. + // Older builds omit the field and fall through as before. + return; + } agentActive = false; diff --git a/src/integration/mod.rs b/src/integration/mod.rs index c87ccac6..33bb163d 100644 --- a/src/integration/mod.rs +++ b/src/integration/mod.rs @@ -27,7 +27,7 @@ const PI_EXTENSION_ASSET: &str = include_str!("assets/pi/herdr-agent-state.ts"); const PI_INTEGRATION_VERSION: u32 = 8; const OMP_EXTENSION_INSTALL_NAME: &str = "herdr-omp-agent-state.ts"; const OMP_EXTENSION_ASSET: &str = include_str!("assets/omp/herdr-agent-state.ts"); -const OMP_INTEGRATION_VERSION: u32 = 8; +const OMP_INTEGRATION_VERSION: u32 = 9; const CLAUDE_HOOK_INSTALL_NAME: &str = if cfg!(windows) { "herdr-agent-state.ps1" } else {