From e04f4dc8a9ecf2da598e9393e46cbfb28bfe4d6d Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 25 Oct 2026 12:39:50 -0700 Subject: [PATCH] fix(mobile): keep a hook status that postdates the shell title on the session tab strip (#33614) The mobile session tab strip no longer shows Done while the workspace list shows Working: once the PTY-title renewal has kept a newer non-done status against the same clocked OSC title, the mobile projection no longer re-demotes it without the clock (seen on Windows ConPTY where the spawn-time cmd.exe title lingers). Supersedes #32472 Co-authored-by: zhangxaochen <3416677+zhangxaochen@users.noreply.github.com> --- ...le-agent-status-title-truthfulness.test.ts | 34 +++++++++++++++++++ .../runtime-mobile-session-projection.ts | 9 ++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/runtime/orca-runtime-mobile-agent-status-title-truthfulness.test.ts b/src/main/runtime/orca-runtime-mobile-agent-status-title-truthfulness.test.ts index 9534d7a3ddd..80858345aa9 100644 --- a/src/main/runtime/orca-runtime-mobile-agent-status-title-truthfulness.test.ts +++ b/src/main/runtime/orca-runtime-mobile-agent-status-title-truthfulness.test.ts @@ -207,6 +207,40 @@ describe('mobile session tabs: live-title evidence vs published agent status', ( expect.objectContaining({ state: 'done', prompt: '' }) ) }) + + it('keeps a hook-backed working status that postdates an initial Windows ConPTY shell title', async () => { + vi.useFakeTimers() + try { + vi.setSystemTime(T0 - 1_000) + const runtime = await createRuntime() + runtime.onPtyData(PTY_ID, '\x1b]0;C:\\WINDOWS\\system32\\cmd.exe\x07', T0 - 1_000) + + vi.setSystemTime(T0) + publishRendererWorkingPane(runtime, 'C:\\WINDOWS\\system32\\cmd.exe') + + expect(await projectAgentStatus(runtime)).toEqual( + expect.objectContaining({ + state: 'working', + prompt: 'Configurar y entender los comprobantes', + updatedAt: T0 + }) + ) + + // Once a newer shell title arrives after the hook, it retires the working status to done. + vi.setSystemTime(T0 + 1_000) + runtime.onPtyData(PTY_ID, '\x1b]0;C:\\WINDOWS\\system32\\cmd.exe\x07', T0 + 1_000) + + expect(await projectAgentStatus(runtime)).toEqual( + expect.objectContaining({ + state: 'done', + prompt: '', + updatedAt: T0 + 1_000 + }) + ) + } finally { + vi.useRealTimers() + } + }) }) describe('mobile session tabs: identity-only fallback freshness', () => { diff --git a/src/main/runtime/runtime-mobile-session-projection.ts b/src/main/runtime/runtime-mobile-session-projection.ts index 61ad14e22b9..8819b44dfcf 100644 --- a/src/main/runtime/runtime-mobile-session-projection.ts +++ b/src/main/runtime/runtime-mobile-session-projection.ts @@ -226,10 +226,17 @@ export function projectRuntimeMobileSessionTabs( const hasLiveAgentSignal = normalizedTabAgentStatus?.interactivePrompt != null || normalizedTabAgentStatus?.toolName != null + // A title renew already dated against the hook (e.g. ConPTY's spawn-time shell title) cannot re-demote it (#32467). + const hookOutlivedAdjudicatedTitle = + normalizedTabAgentStatus?.state !== 'done' && + statusPty?.lastOscTitleEpochMs != null && + liveTitleEvidence === (statusPty.lastOscTitle?.trim() || null) // Only shell/management proves release; neutral Terminal/editor/cwd titles cannot justify done that fights clients' live status. const keepFullAgentStatus = normalizedTabAgentStatus && - (!terminalTitleBlocksExplicitAgentStatus(liveTitleEvidence) || hasLiveAgentSignal) + (hookOutlivedAdjudicatedTitle || + !terminalTitleBlocksExplicitAgentStatus(liveTitleEvidence) || + hasLiveAgentSignal) const agentStatus = keepFullAgentStatus ? { agentStatus: normalizedTabAgentStatus } : // Idle titles drop stale working spinners but retain transcript identity for native chat.