diff --git a/src/main/persistence/leasing-ssh-ptys/ssh-pty-consumer-recovery.ts b/src/main/persistence/leasing-ssh-ptys/ssh-pty-consumer-recovery.ts index f7c8f07f24a..2606e879936 100644 --- a/src/main/persistence/leasing-ssh-ptys/ssh-pty-consumer-recovery.ts +++ b/src/main/persistence/leasing-ssh-ptys/ssh-pty-consumer-recovery.ts @@ -7,6 +7,7 @@ import { normalizeSshPtyConsumerRecovery } from './ssh-normalization' export type SshPtyConsumerRecoveryOperations = { state: StoreOwnedPersistedState protectedSecrets: Pick + scheduleSave: () => void flushDurableStateOrThrowAsync: () => Promise } @@ -51,6 +52,7 @@ export async function upsertSshPtyConsumerRecovery( ...recoveries.filter((candidate) => candidate.targetId !== normalized.targetId), normalized ] + operations.scheduleSave() await flushSshPtyConsumerRecovery(operations) } @@ -65,5 +67,6 @@ export async function removeSshPtyConsumerRecovery( } operations.state.sshPtyConsumerRecoveries = next operations.protectedSecrets.removeRetainedBlob(sshPtyOwnerLeaseSecretSlot(targetId)) + operations.scheduleSave() await flushSshPtyConsumerRecovery(operations) } diff --git a/src/main/persistence/leasing-ssh-ptys/ssh-pty-lease-operations.ts b/src/main/persistence/leasing-ssh-ptys/ssh-pty-lease-operations.ts index ef8fd642f24..8d262ab6b71 100644 --- a/src/main/persistence/leasing-ssh-ptys/ssh-pty-lease-operations.ts +++ b/src/main/persistence/leasing-ssh-ptys/ssh-pty-lease-operations.ts @@ -8,6 +8,7 @@ export type SshPtyLeaseOperations = { toStoredPtyId: (targetId: string, ptyId: string) => string clearBindingsForTarget: (targetId: string) => void clearBindingsForLeases: (targetId: string, leases: SshRemotePtyLease[]) => boolean + scheduleSave: () => void flush: () => void flushDurableStateOrThrowAsync: () => Promise } @@ -54,6 +55,7 @@ export function upsertSshRemotePtyLease( } else { operations.state.sshRemotePtyLeases.push(next) } + operations.scheduleSave() operations.flush() } @@ -104,6 +106,7 @@ export function markSshRemotePtyLeases( state: SshRemotePtyLease['state'] ): void { if (updateSshRemotePtyLeaseStates(operations, targetId, state)) { + operations.scheduleSave() operations.flush() } } @@ -125,6 +128,7 @@ export async function markSshRemotePtyLeasesAsync( state: SshRemotePtyLease['state'] ): Promise { if (updateSshRemotePtyLeaseStates(operations, targetId, state)) { + operations.scheduleSave() await operations.flushDurableStateOrThrowAsync() } } @@ -136,6 +140,7 @@ export async function markSshRemotePtyLeasesAttachedAsync( ): Promise { const relayPtyIds = new Set(ptyIds.map((ptyId) => operations.toStoredPtyId(targetId, ptyId))) if (updateSshRemotePtyLeaseStates(operations, targetId, 'attached', relayPtyIds)) { + operations.scheduleSave() await operations.flushDurableStateOrThrowAsync() } } @@ -156,6 +161,7 @@ export function markSshRemotePtyLease( const shouldClearBindings = state === 'terminated' || state === 'expired' if (lease.state === state) { if (shouldClearBindings && operations.clearBindingsForLeases(targetId, [lease])) { + operations.scheduleSave() operations.flush() } return @@ -171,6 +177,7 @@ export function markSshRemotePtyLease( if (shouldClearBindings) { operations.clearBindingsForLeases(targetId, [lease]) } + operations.scheduleSave() operations.flush() } @@ -189,6 +196,7 @@ export function removeSshRemotePtyLease( (lease) => lease.targetId !== targetId || lease.ptyId !== relayPtyId ) if (operations.state.sshRemotePtyLeases.length !== before) { + operations.scheduleSave() operations.flush() } } @@ -204,6 +212,7 @@ export function removeSshRemotePtyLeases( (lease) => lease.targetId !== targetId ) if (operations.state.sshRemotePtyLeases.length !== before) { + operations.scheduleSave() operations.flush() } } diff --git a/src/main/persistence/leasing-ssh-ptys/ssh-target-state.ts b/src/main/persistence/leasing-ssh-ptys/ssh-target-state.ts index dafd76b1b6d..9a9ebc574d1 100644 --- a/src/main/persistence/leasing-ssh-ptys/ssh-target-state.ts +++ b/src/main/persistence/leasing-ssh-ptys/ssh-target-state.ts @@ -107,6 +107,7 @@ export function addClaudeLivePtySessionId( operations.state.claudeLivePtySessionIds = [...ids, sessionId].slice( -MAX_CLAUDE_LIVE_PTY_SESSION_IDS ) + operations.scheduleSave() // Why: flush sync so a force-quit right after a Claude spawn still seeds the live-PTY gate next launch. operations.flush() } diff --git a/src/main/persistence/loading-store/store.ts b/src/main/persistence/loading-store/store.ts index 53daf7ac546..7dcd26e89ac 100644 --- a/src/main/persistence/loading-store/store.ts +++ b/src/main/persistence/loading-store/store.ts @@ -3856,6 +3856,7 @@ export class Store { return { state: this.state, protectedSecrets: this.protectedSecrets, + scheduleSave: () => this.scheduleSave(), flushDurableStateOrThrowAsync: () => this.flushDurableStateOrThrowAsync() } } @@ -3909,6 +3910,7 @@ export class Store { targetId, leases ), + scheduleSave: () => this.scheduleSave(), flush: () => this.flush(), flushDurableStateOrThrowAsync: () => this.flushDurableStateOrThrowAsync() }