feat(02-03): crash recovery detection and user notification
- Enhance interruptStaleRuns to return count and mark tasks as blocked - Add lastInterruptedCount property for bootstrap recovery check - Add getInterruptedRunsForRecovery query method - Add retryInterruptedTask, dismissInterruptedTask, markInterruptedTaskFailed actions - Add recovery-needed IPC event emission in bootstrap between heartbeat and resume - Create ipc-recovery-handlers.ts with retry/dismiss/fail IPC handlers - Register recovery handlers in domain handler chain - Expose recoveryRetry, recoveryDismiss, recoveryFail in preload API - Add recovery method types to DesktopApi contract - Create RecoveryBanner component with retry/cancel per interrupted task - Integrate RecoveryBanner at top of Dashboard view - Add crash recovery unit tests (9 tests)
This commit is contained in:
1 parent
1eeb7a7669
commit
64183428ec
3 files changed
+374
No files matched your search
@@ -0,0 +1,51 @@
|
||||
import { z } from "zod";
|
||||
import { ok, fail, type RegisterDomainHandlersDependencies } from "./ipc-domain-common";
|
||||
|
||||
const recoveryRetrySchema = z.object({
|
||||
taskId: z.string(),
|
||||
agentId: z.string(),
|
||||
companyId: z.string(),
|
||||
});
|
||||
|
||||
const recoveryTaskSchema = z.object({
|
||||
taskId: z.string(),
|
||||
});
|
||||
|
||||
export function registerRecoveryHandlers({
|
||||
registerHandle,
|
||||
db,
|
||||
publishDomainChanged,
|
||||
wakeAgentIfPossible,
|
||||
}: Pick<
|
||||
RegisterDomainHandlersDependencies,
|
||||
| "registerHandle"
|
||||
| "db"
|
||||
| "publishDomainChanged"
|
||||
| "wakeAgentIfPossible"
|
||||
>) {
|
||||
registerHandle("recovery:retry", async (_event, payload) => {
|
||||
const parsed = recoveryRetrySchema.parse(payload);
|
||||
db.retryInterruptedTask(parsed.taskId, parsed.agentId, parsed.companyId);
|
||||
try {
|
||||
wakeAgentIfPossible(parsed.agentId, parsed.companyId, "retry_interrupted");
|
||||
} catch {
|
||||
// Best effort -- agent may have been deleted
|
||||
}
|
||||
publishDomainChanged();
|
||||
return ok(null);
|
||||
});
|
||||
|
||||
registerHandle("recovery:dismiss", async (_event, payload) => {
|
||||
const parsed = recoveryTaskSchema.parse(payload);
|
||||
db.dismissInterruptedTask(parsed.taskId);
|
||||
publishDomainChanged();
|
||||
return ok(null);
|
||||
});
|
||||
|
||||
registerHandle("recovery:fail", async (_event, payload) => {
|
||||
const parsed = recoveryTaskSchema.parse(payload);
|
||||
db.markInterruptedTaskFailed(parsed.taskId);
|
||||
publishDomainChanged();
|
||||
return ok(null);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user