@@ -35,6 +35,7 @@ import { isNotification, POSTHOG_NOTIFICATIONS } from "@posthog/agent";
3535import { DEFAULT_GATEWAY_MODEL } from "@posthog/agent/gateway-models" ;
3636import { getIsOnline } from "@renderer/stores/connectivityStore" ;
3737import { trpcClient } from "@renderer/trpc/client" ;
38+ import { getGhUserTokenOrThrow } from "@renderer/utils/github" ;
3839import { toast } from "@renderer/utils/toast" ;
3940import { getCloudUrlFromRegion } from "@shared/constants/oauth" ;
4041import {
@@ -45,6 +46,7 @@ import {
4546 type Task ,
4647} from "@shared/types" ;
4748import { ANALYTICS_EVENTS } from "@shared/types/analytics" ;
49+ import type { CloudRunSource , PrAuthorshipMode } from "@shared/types/cloud" ;
4850import type { AcpMessage , StoredLogEntry } from "@shared/types/session-events" ;
4951import { isJsonRpcRequest } from "@shared/types/session-events" ;
5052import { buildPermissionToolMetadata , track } from "@utils/analytics" ;
@@ -1342,6 +1344,35 @@ export class SessionService {
13421344
13431345 const { blocks, promptText } = await this . prepareCloudPrompt ( prompt ) ;
13441346
1347+ const [ previousRun , task ] = await Promise . all ( [
1348+ client . getTaskRun ( session . taskId , session . taskRunId ) ,
1349+ client . getTask ( session . taskId ) ,
1350+ ] ) ;
1351+ const hasGitHubRepo = ! ! task . repository && ! ! task . github_integration ;
1352+ const previousState = previousRun . state as Record < string , unknown > ;
1353+ const previousOutput = ( previousRun . output ?? { } ) as Record <
1354+ string ,
1355+ unknown
1356+ > ;
1357+ // Prefer the actual working branch the agent last pushed to (synced by
1358+ // agent-server after each turn), then the run-level branch field, then
1359+ // the original base branch from state. This preserves unmerged work when
1360+ // the snapshot has expired and the sandbox is rebuilt from scratch.
1361+ const previousBaseBranch =
1362+ ( typeof previousOutput . head_branch === "string"
1363+ ? previousOutput . head_branch
1364+ : null ) ??
1365+ previousRun . branch ??
1366+ ( typeof previousState . pr_base_branch === "string"
1367+ ? previousState . pr_base_branch
1368+ : null ) ??
1369+ session . cloudBranch ;
1370+ const prAuthorshipMode = this . getCloudPrAuthorshipMode ( previousState ) ;
1371+ const githubUserToken =
1372+ prAuthorshipMode === "user" && hasGitHubRepo
1373+ ? await getGhUserTokenOrThrow ( )
1374+ : undefined ;
1375+
13451376 log . info ( "Creating resume run for terminal cloud task" , {
13461377 taskId : session . taskId ,
13471378 previousRunId : session . taskRunId ,
@@ -1353,10 +1384,17 @@ export class SessionService {
13531384 // The agent will load conversation history and restore the sandbox snapshot.
13541385 const updatedTask = await client . runTaskInCloud (
13551386 session . taskId ,
1356- session . cloudBranch ,
1387+ previousBaseBranch ,
13571388 {
13581389 resumeFromRunId : session . taskRunId ,
13591390 pendingUserMessage : serializeCloudPrompt ( blocks ) ,
1391+ prAuthorshipMode,
1392+ runSource : this . getCloudRunSource ( previousState ) ,
1393+ signalReportId :
1394+ typeof previousState . signal_report_id === "string"
1395+ ? previousState . signal_report_id
1396+ : undefined ,
1397+ githubUserToken,
13601398 } ,
13611399 ) ;
13621400 const newRun = updatedTask . latest_run ;
@@ -2081,6 +2119,20 @@ export class SessionService {
20812119 }
20822120 }
20832121
2122+ private getCloudPrAuthorshipMode (
2123+ state : Record < string , unknown > ,
2124+ ) : PrAuthorshipMode {
2125+ const explicitMode = state . pr_authorship_mode ;
2126+ if ( explicitMode === "user" || explicitMode === "bot" ) {
2127+ return explicitMode ;
2128+ }
2129+ return state . run_source === "signal_report" ? "bot" : "user" ;
2130+ }
2131+
2132+ private getCloudRunSource ( state : Record < string , unknown > ) : CloudRunSource {
2133+ return state . run_source === "signal_report" ? "signal_report" : "manual" ;
2134+ }
2135+
20842136 /**
20852137 * Filter out session/prompt events that should be skipped during resume.
20862138 * When resuming a cloud run, the initial session/prompt from the new run's
0 commit comments