(args: LifecycleArgs)
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * Stop the sandbox families (primary and `-proxy`) that `sessions` provably own. |
| 273 | * Each local session has its own `ses-…` sandbox (`PER_SESSION_SANDBOX_ORG_IDS` |
| 274 | * is `*` in the dev config), and its proxy is named after it, so once the |
| 275 | * gate has deleted the sessions nothing can address that sandbox again. One |
| 276 | * sandbox is stopped once even when several sessions share it (a worktree's |
| 277 | * chats); the sessions' own worktrees are the only extra directories the |
| 278 | * exclusivity proof accepts. A sandbox whose ownership cannot be proven is left |
| 279 | * running and reported, never killed. Never throws. |
| 280 | */ |
| 281 | export async function reclaimOwnedSandboxes( |
| 282 | sessions: readonly ReclaimSession[], |
| 283 | deps: { |
| 284 | executeDocker?: DockerCommandExecutor; |
| 285 | familyGoneTimeoutMs?: number; |
| 286 | retryMs?: number; |
| 287 | } = {} |
| 288 | ): Promise<SandboxReclaimReport> { |
| 289 | const { executeDocker } = deps; |
| 290 | const retryMs = deps.retryMs ?? RECLAIM_DOCKER_RETRY_MS; |
| 291 | const failures: string[] = []; |
| 292 | const groups = new Map< |
| 293 | string, |
| 294 | { container: SandboxContainer; sessions: ReclaimSession[]; directories: string[] } |
| 295 | >(); |
| 296 | for (const session of sessions) { |
| 297 | try { |
| 298 | const located = await retryTransientDocker( |
| 299 | () => locateOwnedSandbox(session, executeDocker), |
| 300 | retryMs |
| 301 | ); |
| 302 | if (!located) continue; |
| 303 | const group = groups.get(located.container.id) ?? { |
| 304 | container: located.container, |
| 305 | sessions: [], |
| 306 | directories: [], |
| 307 | }; |
| 308 | group.sessions.push(session); |
| 309 | if (located.directory !== undefined) group.directories.push(located.directory); |
| 310 | groups.set(located.container.id, group); |
| 311 | } catch (error) { |
| 312 | failures.push(`${session.sessionId}: ${errorText(error)}`); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | const stopped: string[] = []; |
| 317 | for (const group of groups.values()) { |
| 318 | const [owner] = group.sessions; |
| 319 | if (!owner) continue; |
| 320 | try { |
| 321 | const killed = await retryTransientDocker( |
| 322 | () => |
| 323 | stopOwnedSandboxFamily(group.container, owner.sessionId, owner.kiloSessionId, { |
| 324 | ...(executeDocker ? { executeDocker } : {}), |
| 325 | familyGoneTimeoutMs: deps.familyGoneTimeoutMs ?? RECLAIM_FAMILY_GONE_TIMEOUT_MS, |
| 326 | allowedDirectories: group.directories, |
nothing calls this directly
no test coverage detected