fix(ai-vault): preserve resume env deletion through drag

This commit is contained in:
Brennan Benson
2026-07-16 11:04:09 -07:00
parent 18e7414761
commit 26e7ef6787
4 changed files with 34 additions and 1 deletions
@@ -100,6 +100,7 @@ export function VaultSessionRow({
sessionFilePath: session.filePath,
sessionExecutionHostId: session.executionHostId,
...(resumeStartup.env ? { env: resumeStartup.env } : {}),
...(resumeStartup.envToDelete ? { envToDelete: resumeStartup.envToDelete } : {}),
...(resumeStartup.launchConfig ? { launchConfig: resumeStartup.launchConfig } : {})
})
window.dispatchEvent(new Event(AI_VAULT_SESSION_DRAG_START_EVENT))
@@ -204,6 +204,7 @@ export default function AiVaultSessionDropLayer({
worktreeId,
command: payload.command,
...(payload.env ? { env: payload.env } : {}),
...(payload.envToDelete ? { envToDelete: payload.envToDelete } : {}),
...(payload.launchConfig ? { launchConfig: payload.launchConfig } : {}),
targetGroupId: dropTarget.groupId,
splitDirection: dropTarget.zone === 'center' ? undefined : dropTarget.zone
@@ -50,6 +50,7 @@ describe('Session History session drag data', () => {
command: "cd '/repo' && claude --resume session-1",
sessionFilePath: '/Users/ada/.claude/projects/-repo/session-1.jsonl',
env: { ANTHROPIC_BASE_URL: 'https://claude.example.test' },
envToDelete: ['CODEX_HOME', 'ORCA_CODEX_HOME'],
launchConfig: {
agentCommand: 'claude --dangerously-skip-permissions',
agentArgs: '--dangerously-skip-permissions',
@@ -110,6 +111,24 @@ describe('Session History session drag data', () => {
expect(readAiVaultSessionDragData(transfer)).toBeNull()
})
it('rejects malformed env deletion lists', () => {
const transfer = createTransfer()
transfer.setData(
AI_VAULT_SESSION_DRAG_TYPE,
JSON.stringify({
kind: 'ai-vault-session',
version: 1,
agent: 'codex',
sessionId: 'session-1',
title: 'Malformed env deletion',
command: 'codex resume session-1',
envToDelete: ['CODEX_HOME', '']
})
)
expect(readAiVaultSessionDragData(transfer)).toBeNull()
})
it('rejects array-shaped launch config env records', () => {
const transfer = createTransfer()
transfer.setData(
+13 -1
View File
@@ -17,8 +17,9 @@ export type AiVaultSessionDragPayload = {
// WSL) to reject SSH panes that cannot reach it.
sessionFilePath?: string
sessionExecutionHostId?: ExecutionHostId
// Why: drag/drop resume must preserve planned env/default args, not just the shell command.
// Why: drag/drop resume must preserve planned env mutations/default args, not just the command.
env?: Record<string, string>
envToDelete?: string[]
launchConfig?: SleepingAgentLaunchConfig
}
@@ -44,6 +45,14 @@ function isStringRecord(value: unknown): value is Record<string, string> {
return Object.values(value).every((entry) => typeof entry === 'string')
}
function isEnvDeletionList(value: unknown): value is string[] {
return (
Array.isArray(value) &&
value.length <= 32 &&
value.every((entry) => typeof entry === 'string' && entry.length > 0 && entry.length <= 256)
)
}
function isLaunchConfig(value: unknown): value is SleepingAgentLaunchConfig {
if (!value || typeof value !== 'object') {
return false
@@ -72,6 +81,7 @@ function isSerializedPayload(value: unknown): value is SerializedAiVaultSessionD
(payload.sessionExecutionHostId === undefined ||
Boolean(normalizeExecutionHostId(payload.sessionExecutionHostId))) &&
(payload.env === undefined || isStringRecord(payload.env)) &&
(payload.envToDelete === undefined || isEnvDeletionList(payload.envToDelete)) &&
(payload.launchConfig === undefined || isLaunchConfig(payload.launchConfig))
)
}
@@ -126,6 +136,7 @@ export function readAiVaultSessionDragData(
sessionFilePath,
sessionExecutionHostId,
env,
envToDelete,
launchConfig
} = parsed
return {
@@ -136,6 +147,7 @@ export function readAiVaultSessionDragData(
...(sessionFilePath ? { sessionFilePath } : {}),
...(sessionExecutionHostId ? { sessionExecutionHostId } : {}),
...(env ? { env } : {}),
...(envToDelete ? { envToDelete } : {}),
...(launchConfig ? { launchConfig } : {})
}
} catch {