feat: add remote daemon self-update from client

Adds a daemon.update.request RPC that lets the client trigger a remote
daemon update over WebSocket. The daemon runs npm update -g @getpaseo/cli,
reports progress via daemon_update_progress status events, then restarts
through the supervisor to load the new code.

New feature flag server_info.features.daemonSelfUpdate gates the UI.
The UpdateDaemonCard appears on the host settings page when the daemon
supports self-update and the app/daemon versions differ.
This commit is contained in:
Davy
2026-06-26 12:53:23 +07:00
committed by Mohamed Boudra
parent 8c3b709794
commit c6c030ab26
11 changed files with 528 additions and 0 deletions
+33
View File
@@ -1252,6 +1252,11 @@ export const ShutdownServerRequestMessageSchema = z.object({
requestId: z.string(),
});
export const DaemonUpdateRequestMessageSchema = z.object({
type: z.literal("daemon.update.request"),
requestId: z.string(),
});
export const AgentTimelineCursorSchema = z.object({
epoch: z.string(),
seq: z.number().int().nonnegative(),
@@ -2053,6 +2058,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
CancelAgentRequestMessageSchema,
ShutdownServerRequestMessageSchema,
RestartServerRequestMessageSchema,
DaemonUpdateRequestMessageSchema,
FetchAgentTimelineRequestMessageSchema,
SetAgentModeRequestMessageSchema,
SetAgentModelRequestMessageSchema,
@@ -2327,6 +2333,8 @@ export const ServerInfoStatusPayloadSchema = z
agentDetach: z.boolean().optional(),
// COMPAT(daemonDiagnostics): added in v0.1.100, remove gate after 2026-12-25 once daemon floor >= v0.1.100.
daemonDiagnostics: z.boolean().optional(),
// COMPAT(daemonSelfUpdate): added in v0.1.93, remove gate after 2026-12-13.
daemonSelfUpdate: z.boolean().optional(),
})
.optional(),
})
@@ -2415,6 +2423,16 @@ export const ShutdownRequestedStatusPayloadSchema = z.object({
requestId: z.string(),
});
export const DaemonUpdateProgressStatusPayloadSchema = z.object({
status: z.literal("daemon_update_progress"),
requestId: z.string(),
phase: z.enum(["starting", "downloading", "installing", "complete"]),
});
export type DaemonUpdateProgressStatusPayload = z.infer<
typeof DaemonUpdateProgressStatusPayloadSchema
>;
export const DaemonConfigChangedStatusPayloadSchema = z
.object({
status: z.literal("daemon_config_changed"),
@@ -2429,6 +2447,7 @@ export const KnownStatusPayloadSchema = z.discriminatedUnion("status", [
AgentRefreshedStatusPayloadSchema,
ShutdownRequestedStatusPayloadSchema,
RestartRequestedStatusPayloadSchema,
DaemonUpdateProgressStatusPayloadSchema,
DaemonConfigChangedStatusPayloadSchema,
]);
@@ -4093,6 +4112,19 @@ export const TerminalAttentionRequiredSchema = z.object({
}),
});
export const DaemonUpdateResponseSchema = z.object({
type: z.literal("daemon.update.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
error: z.string().nullable(),
previousVersion: z.string().nullable(),
newVersion: z.string().nullable(),
}),
});
export type DaemonUpdateResponse = z.infer<typeof DaemonUpdateResponseSchema>;
export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
ActivityLogMessageSchema,
AssistantChunkMessageSchema,
@@ -4228,6 +4260,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
LoopInspectResponseSchema,
LoopLogsResponseSchema,
LoopStopResponseSchema,
DaemonUpdateResponseSchema,
]);
export type SessionOutboundMessage = z.infer<typeof SessionOutboundMessageSchema>;