(
sessionId: string,
metadata: Metadata,
encryption?: {
encryptionKey: string;
encryptionVariant: 'legacy' | 'dataKey';
seq: number;
metadataVersion: number;
agentStateVersion: number;
}
)
| 60 | const SESSION_STARTED_RETRY_INTERVAL_MS = 100; |
| 61 | |
| 62 | export async function notifyDaemonSessionStarted( |
| 63 | sessionId: string, |
| 64 | metadata: Metadata, |
| 65 | encryption?: { |
| 66 | encryptionKey: string; |
| 67 | encryptionVariant: 'legacy' | 'dataKey'; |
| 68 | seq: number; |
| 69 | metadataVersion: number; |
| 70 | agentStateVersion: number; |
| 71 | } |
| 72 | ): Promise<{ error?: string } | any> { |
| 73 | // Retry briefly — ensureDaemonRunning already waits for readiness, but we may |
| 74 | // race a daemon that is mid-restart (version upgrade, crash recovery). Without |
| 75 | // this, the session's encryption data never reaches the daemon and the mobile |
| 76 | // app's resume-happy-session RPC fails with "not tracked by this daemon". |
| 77 | const payload = { sessionId, metadata, encryption }; |
| 78 | const deadline = Date.now() + SESSION_STARTED_RETRY_TIMEOUT_MS; |
| 79 | let result: { error?: string } | any; |
| 80 | |
| 81 | while (true) { |
| 82 | result = await daemonPost('/session-started', payload); |
| 83 | if (!result?.error) { |
| 84 | return result; |
| 85 | } |
| 86 | if (Date.now() >= deadline) { |
| 87 | return result; |
| 88 | } |
| 89 | await new Promise(resolve => setTimeout(resolve, SESSION_STARTED_RETRY_INTERVAL_MS)); |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | export async function listDaemonSessions(): Promise<any[]> { |
| 94 | const result = await daemonPost('/list'); |
no test coverage detected