Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ describe('useSessionTileDelegate resumeTile', () => {
expect(requestGateway).toHaveBeenCalledWith('session.resume', {
session_id: 'stored-x',
cols: 96,
profile: 'ai-engineer'
profile: 'ai-engineer',
omit_messages: true
})
})

Expand All @@ -94,7 +95,8 @@ describe('useSessionTileDelegate resumeTile', () => {
expect(requestGateway).toHaveBeenCalledWith('session.resume', {
session_id: 'stored-y',
cols: 96,
profile: 'default'
profile: 'default',
omit_messages: true
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export function useSessionTileDelegate({
requestGateway<SessionResumeResponse>('session.resume', {
session_id: storedSessionId,
cols: 96,
omit_messages: true,
...(profile ? { profile } : {})
})
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1797,7 +1797,8 @@ describe('usePromptActions submit / queue drain semantics', () => {
expect(accepted).toBe(true)
expect(requestGateway).toHaveBeenCalledWith('session.resume', {
session_id: 'stored-session-b',
source: 'desktop'
source: 'desktop',
omit_messages: true
})
expect(requestGateway).toHaveBeenCalledWith(
'prompt.submit',
Expand Down Expand Up @@ -1933,7 +1934,8 @@ describe('usePromptActions submit / queue drain semantics', () => {
// Must resume the correct stored session to get the right runtime id.
expect(requestGateway).toHaveBeenCalledWith('session.resume', {
session_id: 'stored-session-a',
source: 'desktop'
source: 'desktop',
omit_messages: true
})
// The prompt must land in the resumed session, NOT the foreground.
expect(requestGateway).toHaveBeenCalledWith(
Expand Down Expand Up @@ -2194,7 +2196,7 @@ describe('usePromptActions redirectPrompt', () => {
expect(await handle!.redirectPrompt('reconnect nudge')).toBe(true)
expect(calls.map(c => c.method)).toEqual(['session.redirect', 'session.resume', 'session.redirect'])
expect(calls[0]?.params).toEqual({ session_id: RUNTIME_SESSION_ID, text: 'reconnect nudge' })
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop' })
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop', omit_messages: true })
expect(calls[2]?.params).toEqual({ session_id: RECOVERED_SESSION_ID, text: 'reconnect nudge' })
expect(handle!.activeSessionIdRef.current).toBe(RECOVERED_SESSION_ID)
})
Expand Down Expand Up @@ -2735,7 +2737,7 @@ describe('usePromptActions sleep/wake session recovery', () => {
expect(ok).toBe(true)
// First submit (stale id) → session.resume (stored id) → retry submit (fresh id).
expect(calls.map(c => c.method)).toEqual(['prompt.submit', 'session.resume', 'prompt.submit'])
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop' })
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop', omit_messages: true })
expect(calls[2]?.params).toEqual({ session_id: RECOVERED_SESSION_ID, text: 'message after wake' })
})

Expand Down Expand Up @@ -2779,7 +2781,12 @@ describe('usePromptActions sleep/wake session recovery', () => {
)

expect(await handle!.submitText('message after wake')).toBe(true)
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop', profile: 'work' })
expect(calls[1]?.params).toEqual({
session_id: STORED_SESSION_ID,
source: 'desktop',
omit_messages: true,
profile: 'work'
})

setSessions(() => [])
})
Expand Down Expand Up @@ -2826,7 +2833,12 @@ describe('usePromptActions sleep/wake session recovery', () => {
)

expect(await handle!.submitText('message after wake')).toBe(true)
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop', profile: 'work' })
expect(calls[1]?.params).toEqual({
session_id: STORED_SESSION_ID,
source: 'desktop',
omit_messages: true,
profile: 'work'
})

vi.mocked(getSession).mockReset()
setSessions(() => [])
Expand Down Expand Up @@ -2887,7 +2899,11 @@ describe('usePromptActions sleep/wake session recovery', () => {
session_id: 'rt-background-stale',
text: 'queued background message after wake'
})
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop' })
expect(calls[1]?.params).toEqual({
session_id: STORED_SESSION_ID,
source: 'desktop',
omit_messages: true
})
expect(calls[2]?.params).toEqual({
queued: true,
session_id: RECOVERED_SESSION_ID,
Expand Down Expand Up @@ -2935,7 +2951,11 @@ describe('usePromptActions sleep/wake session recovery', () => {

expect(calls.map(c => c.method)).toEqual(['session.interrupt', 'session.resume', 'session.interrupt'])
expect(calls[0]?.params).toEqual({ session_id: RUNTIME_SESSION_ID })
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop' })
expect(calls[1]?.params).toEqual({
session_id: STORED_SESSION_ID,
source: 'desktop',
omit_messages: true
})
expect(calls[2]?.params).toEqual({ session_id: RECOVERED_SESSION_ID })
})

Expand Down Expand Up @@ -3067,7 +3087,11 @@ describe('usePromptActions sleep/wake session recovery', () => {

expect(ok).toBe(true)
expect(calls.map(c => c.method)).toEqual(['prompt.submit', 'session.resume', 'prompt.submit'])
expect(calls[1]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop' })
expect(calls[1]?.params).toEqual({
session_id: STORED_SESSION_ID,
source: 'desktop',
omit_messages: true
})
expect(calls[2]?.params).toEqual({
session_id: RECOVERED_SESSION_ID,
text: 'message during starved loop'
Expand Down Expand Up @@ -3110,7 +3134,11 @@ describe('usePromptActions sleep/wake session recovery', () => {
expect(ok).toBe(true)
expect(createBackendSessionForSend).not.toHaveBeenCalled()
expect(calls.map(c => c.method)).toEqual(['session.resume', 'prompt.submit'])
expect(calls[0]?.params).toEqual({ session_id: STORED_SESSION_ID, source: 'desktop' })
expect(calls[0]?.params).toEqual({
session_id: STORED_SESSION_ID,
source: 'desktop',
omit_messages: true
})
expect(calls[1]?.params).toMatchObject({ session_id: RECOVERED_SESSION_ID })
})

Expand Down Expand Up @@ -3421,7 +3449,8 @@ describe('usePromptActions submit session-context isolation (#54527)', () => {
expect(calls.some(c => c.method === 'prompt.submit')).toBe(false)
expect(calls.find(c => c.method === 'session.resume')?.params).toEqual({
session_id: STORED_SESSION_A,
source: 'desktop'
source: 'desktop',
omit_messages: true
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ export function usePromptActions({
const resumed = await requestGateway<{ session_id: string }>('session.resume', {
session_id: selectedStoredSessionIdRef.current,
source: 'desktop',
omit_messages: true,
...(resumeProfile ? { profile: resumeProfile } : {})
})

Expand Down Expand Up @@ -744,6 +745,7 @@ export function usePromptActions({
const resumed = await requestGateway<{ session_id: string }>('session.resume', {
session_id: selectedStoredSessionIdRef.current,
source: 'desktop',
omit_messages: true,
...(resumeProfile ? { profile: resumeProfile } : {})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ export function useSubmitPrompt(deps: SubmitPromptDeps) {
const resumed = await requestGateway<{ session_id: string }>('session.resume', {
session_id: targetStoredSessionId,
source: 'desktop',
omit_messages: true,
...(resumeProfile ? { profile: resumeProfile } : {})
})

Expand Down Expand Up @@ -637,6 +638,7 @@ export function useSubmitPrompt(deps: SubmitPromptDeps) {
const resumed = await requestGateway<{ session_id: string }>('session.resume', {
session_id: recoverStoredSessionId,
source: 'desktop',
omit_messages: true,
...(resumeProfile ? { profile: resumeProfile } : {})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,7 @@ describe('resumeSession failure recovery', () => {

expect(resumeParams).not.toHaveProperty('lazy')
expect(resumeParams).not.toHaveProperty('eager_build')
expect(resumeParams).toMatchObject({ source: 'desktop' })
expect(resumeParams).toMatchObject({ source: 'desktop', omit_messages: true })
})

it('arms the failure latch when resume succeeds with an empty transcript for a non-empty stored session', async () => {
Expand Down Expand Up @@ -1431,6 +1431,10 @@ describe('resumeSession warm-cache mapping integrity', () => {
expect(methods).toContain('session.activate')
expect(methods).not.toContain('session.resume')
expect(getSessionMessages).toHaveBeenCalledWith('stored-A', undefined)
expect(requestGateway).toHaveBeenCalledWith(
'session.activate',
expect.objectContaining({ omit_messages: true, session_id: 'rt-A' })
)
expect(runtimeIdByStoredSessionIdRef.current.get('stored-A')).toBe('rt-A')
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,8 @@ export function useSessionActions({
try {
activated = await requestGateway<SessionResumeResponse>('session.activate', {
session_id: cachedRuntimeId,
cols: 96
cols: 96,
omit_messages: true
})
} catch (error) {
// Compatibility for older backends. Modern backends require
Expand Down Expand Up @@ -866,12 +867,14 @@ export function useSessionActions({
session_id: storedSessionId,
cols: 96,
source: 'desktop',
// REST is the transcript authority for Desktop. Avoid duplicating a
// potentially huge compression lineage in the WebSocket response.
// Watch windows attach lazily (live mirror). Every other cold resume
// gets the gateway's default deferred build: the RPC returns the
// transcript immediately instead of blocking the switch on _make_agent
// (MCP discovery / prompt build), and the agent pre-warms in the
// background while the prefetch above paints the transcript.
...(watchWindow ? { lazy: true } : {}),
...(watchWindow ? { lazy: true } : { omit_messages: true }),
...(sessionProfile ? { profile: sessionProfile } : {})
})

Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/types/hermes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ export interface SessionResumeResponse {
info?: SessionRuntimeInfo
message_count: number
messages: SessionMessage[]
messages_omitted?: boolean
resumed: string
running?: boolean
session_id: string
Expand Down
47 changes: 43 additions & 4 deletions tests/test_tui_gateway_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2415,8 +2415,10 @@ def test_history_to_messages_keeps_real_user_bracket_text():
]


def test_session_resume_uses_parent_lineage_for_display(monkeypatch):
@pytest.mark.parametrize("omit_messages", [False, True])
def test_session_resume_uses_parent_lineage_for_display(monkeypatch, omit_messages):
captured = {}
target = "tip-omit" if omit_messages else "tip-full"

class FakeDB:
def get_session(self, target):
Expand Down Expand Up @@ -2466,15 +2468,25 @@ def get_messages_as_conversation(self, target, include_ancestors=False, repair_a
# _neuter_agent_prewarm_timer fixture; this test only asserts the
# returned display history.

params = {"session_id": target}
if omit_messages:
params["omit_messages"] = True
resp = server.handle_request(
{"id": "1", "method": "session.resume", "params": {"session_id": "tip"}}
{"id": "1", "method": "session.resume", "params": params}
)

assert resp["result"]["messages"] == [
expected = [] if omit_messages else [
{"role": "user", "text": "root prompt"},
{"role": "assistant", "text": "root answer"},
]
assert captured["history_calls"] == [("tip", False), ("tip", True)]
assert resp["result"]["messages"] == expected
assert resp["result"]["message_count"] == (1 if omit_messages else 2)
assert resp["result"]["messages_omitted"] is omit_messages
expected_calls = [(target, False)] if omit_messages else [
(target, False),
(target, True),
]
assert captured["history_calls"] == expected_calls


def test_live_visible_history_prefers_db_display_with_candidate():
Expand Down Expand Up @@ -12281,6 +12293,33 @@ def test_session_activate_switches_live_session_without_closing_siblings(monkeyp
server._sessions.pop("sid-b", None)


def test_session_activate_can_omit_duplicate_desktop_transcript(monkeypatch):
monkeypatch.setattr(server, "_session_info", lambda agent: {"model": agent.model})
server._sessions["sid-large"] = _session(
agent=types.SimpleNamespace(model="model-large"),
history=[
{"role": "user", "content": "large prompt"},
{"role": "assistant", "content": "large answer"},
],
session_key="key-large",
)
try:
resp = server.handle_request(
{
"id": "1",
"method": "session.activate",
"params": {"session_id": "sid-large", "omit_messages": True},
}
)

assert resp["result"]["messages"] == []
assert resp["result"]["message_count"] == 2
assert resp["result"]["messages_omitted"] is True
assert resp["result"]["session_key"] == "key-large"
finally:
server._sessions.pop("sid-large", None)


# ── session.most_recent ──────────────────────────────────────────────


Expand Down
Loading
Loading