diff --git a/core/engine.go b/core/engine.go index a366033..cecd73e 100644 --- a/core/engine.go +++ b/core/engine.go @@ -1932,16 +1932,22 @@ func (e *Engine) getOrCreateInteractiveStateWith(sessionKey string, p Platform, return state } - // On first real-user connection after engine startup (or workspace re-creation - // after idle reap), use --continue to pick up the most recent CLI session. - // Subsequent connections respect the stored session ID (or "" for fresh). + // On first real-user connection after engine startup, use --continue to pick + // up the most recent CLI session. For workspace-scoped reconnects after idle + // reap, keep that behavior only when we do not already have a concrete saved + // agent session ID; otherwise exact resume is more precise than continuing + // whatever the CLI considers "latest" in that workspace. startSessionID := session.GetAgentSessionID() once := &e.hasConnectedOnce if connectedOnce != nil { once = connectedOnce } - if consumeFirstUserContinueBridge && !once.Swap(true) { - startSessionID = ContinueSession + if consumeFirstUserContinueBridge { + if connectedOnce != nil && startSessionID != "" { + once.Store(true) + } else if !once.Swap(true) { + startSessionID = ContinueSession + } } isResume := startSessionID != "" diff --git a/core/engine_test.go b/core/engine_test.go index 0f61034..7992c78 100644 --- a/core/engine_test.go +++ b/core/engine_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" "sync" + "sync/atomic" "testing" "time" ) @@ -4450,6 +4451,47 @@ func TestFreshSessionRespectedAfterFirstConnection(t *testing.T) { } } +func TestWorkspaceReconnectWithSavedSessionIDSkipsContinueBridge(t *testing.T) { + agent := &stubStartSessionAgent{} + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + e := &Engine{ + agent: agent, + sessions: NewSessionManager(""), + ctx: ctx, + i18n: NewI18n("en"), + interactiveStates: make(map[string]*interactiveState), + display: DisplayCfg{}, + } + + session := e.sessions.GetOrCreateActive("test:user3") + session.SetAgentSessionID("saved-session-id", "stub") + + var wsConnectedOnce atomic.Bool + p := &stubPlatformEngine{n: "test"} + state := e.getOrCreateInteractiveStateWith("test:user3", p, "ctx", session, e.sessions, nil, "", true, &wsConnectedOnce) + + if state.agentSession == nil { + t.Fatal("expected agentSession to be non-nil") + } + if !wsConnectedOnce.Load() { + t.Fatal("workspace connectedOnce flag should be marked after exact resume") + } + + agent.mu.Lock() + calls := append([]string{}, agent.calls...) + agent.mu.Unlock() + + if len(calls) != 1 { + t.Fatalf("expected 1 StartSession call, got %d: %v", len(calls), calls) + } + if calls[0] != "saved-session-id" { + t.Fatalf("StartSession call = %q, want saved session id", calls[0]) + } +} + func TestParseSelfReportedCtx(t *testing.T) { tests := []struct { input string