(sessionId: string, namespace: string)
| 1275 | } |
| 1276 | |
| 1277 | async handoffSessionToLocal(sessionId: string, namespace: string): Promise<LocalHandoffResult> { |
| 1278 | const access = this.sessionCache.resolveSessionAccess(sessionId, namespace) |
| 1279 | if (!access.ok) { |
| 1280 | return { |
| 1281 | type: 'error', |
| 1282 | message: access.reason === 'access-denied' ? 'Session access denied' : 'Session not found', |
| 1283 | code: access.reason === 'access-denied' ? 'access_denied' : 'session_not_found' |
| 1284 | } |
| 1285 | } |
| 1286 | |
| 1287 | if (!access.session.active) { |
| 1288 | return { type: 'success' } |
| 1289 | } |
| 1290 | |
| 1291 | if (access.session.agentState?.controlledByUser === true) { |
| 1292 | return { |
| 1293 | type: 'error', |
| 1294 | message: 'Session is already controlled by a local terminal', |
| 1295 | code: 'already_local' |
| 1296 | } |
| 1297 | } |
| 1298 | |
| 1299 | try { |
| 1300 | await this.rpcGateway.handoffSessionToLocal(access.sessionId) |
| 1301 | } catch (error) { |
| 1302 | return { |
| 1303 | type: 'error', |
| 1304 | message: error instanceof Error ? error.message : String(error), |
| 1305 | code: 'handoff_failed' |
| 1306 | } |
| 1307 | } |
| 1308 | |
| 1309 | const inactive = await this.waitForSessionInactive(access.sessionId) |
| 1310 | if (!inactive) { |
| 1311 | return { |
| 1312 | type: 'error', |
| 1313 | message: 'Timed out waiting for remote session to hand off', |
| 1314 | code: 'handoff_failed' |
| 1315 | } |
| 1316 | } |
| 1317 | |
| 1318 | return { type: 'success' } |
| 1319 | } |
| 1320 | |
| 1321 | private recoverClaudeSessionIdFromMessages(sessionId: string, namespace: string): string | null { |
| 1322 | const messages = this.messageService.getMessages(sessionId, 200) |
nothing calls this directly
no test coverage detected