Reload daemon configuration without restarting (#3365)

* feat(daemon): reload configuration without restarting

Keep config validation, reloadability classification, override handling, and atomic runtime application inside the daemon so local and remote clients share one authoritative workflow.

* fix(daemon): preserve reload resource ownership

Keep every materialized provider client under daemon-lifetime ownership and classify speech overrides from resolved providers. Restore the complete WebSocket config-store test contract exposed by CI.
This commit is contained in:
Mohamed Boudra
2026-08-14 22:46:32 +02:00
committed by GitHub
parent 75bbca4a1b
commit 4748aad103
36 changed files with 2590 additions and 246 deletions
+40 -1
View File
@@ -194,9 +194,26 @@ export const MutableDaemonConfigSchema = z
relay: MutableRelayConfigSchema.optional(),
mcp: z
.object({
enabled: z.boolean().optional(),
injectIntoAgents: z.boolean(),
})
.passthrough(),
hostnames: z.union([z.literal(true), z.array(z.string())]).optional(),
cors: z
.object({
allowedOrigins: z.array(z.string()),
})
.passthrough()
.optional(),
trustedProxies: z.union([z.literal(true), z.array(z.string())]).optional(),
git: z
.object({
maxProcessesPerSecond: z.number().int().positive(),
maxProcessConcurrency: z.number().int().positive(),
})
.optional(),
app: z.object({ baseUrl: z.string() }).optional(),
catalogRefreshTimeoutMs: z.number().int().positive().optional(),
browserTools: MutableBrowserToolsConfigSchema.default({ enabled: false }),
providers: z.record(z.string(), MutableDaemonProviderConfigSchema).default({}),
metadataGeneration: MutableMetadataGenerationConfigSchema.default({ providers: [] }),
@@ -213,7 +230,7 @@ export const MutableDaemonConfigSchema = z
export const MutableDaemonConfigPatchSchema = z
.object({
relay: MutableRelayConfigSchema.partial().optional(),
mcp: MutableDaemonConfigSchema.shape.mcp.partial().optional(),
mcp: z.object({ injectIntoAgents: z.boolean().optional() }).passthrough().optional(),
browserTools: MutableBrowserToolsConfigSchema.partial().optional(),
providers: z
.record(z.string(), MutableDaemonProviderConfigSchema.partial().passthrough())
@@ -1300,6 +1317,11 @@ export const DaemonGetPairingOfferRequestSchema = z.object({
requestId: z.string(),
});
export const DaemonConfigReloadRequestSchema = z.object({
type: z.literal("daemon.config.reload.request"),
requestId: z.string(),
});
export const HubManagementDaemonConnectRequestSchema = z.object({
type: z.literal("hub.management.daemon.connect.request"),
requestId: z.string(),
@@ -2826,6 +2848,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
WaitForFinishRequestSchema,
DaemonGetStatusRequestSchema,
DaemonGetPairingOfferRequestSchema,
DaemonConfigReloadRequestSchema,
HubManagementDaemonConnectRequestSchema,
HubManagementDaemonGetStatusRequestSchema,
HubManagementDaemonDisconnectRequestSchema,
@@ -3157,6 +3180,8 @@ export const ServerInfoStatusPayloadSchema = z
forgeSearch: z.boolean().optional(),
// COMPAT(daemonStatusRpc): added in v0.1.76, remove gate after 2026-11-18.
daemonStatusRpc: z.boolean().optional(),
// COMPAT(daemonConfigReload): added in v0.4.0, remove gate after 2027-02-14.
daemonConfigReload: z.boolean().optional(),
// COMPAT(relayConfig): added in v0.2.6, remove gate after 2027-01-31.
relayConfig: z.boolean().optional(),
// COMPAT(pushTokenRevocation): added in v0.3.2, remove gate after 2027-02-10.
@@ -4336,6 +4361,18 @@ export const DaemonGetPairingOfferResponseSchema = z.object({
.passthrough(),
});
export const DaemonConfigReloadResponseSchema = z.object({
type: z.literal("daemon.config.reload.response"),
payload: z
.object({
requestId: z.string(),
appliedPaths: z.array(z.string()),
restartRequiredPaths: z.array(z.string()),
overrideControlledPaths: z.array(z.string()),
})
.passthrough(),
});
export const DiagnosticsResponseSchema = z.object({
type: z.literal("diagnostics.response"),
payload: z
@@ -5932,6 +5969,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
SetVoiceModeResponseMessageSchema,
DaemonGetStatusResponseSchema,
DaemonGetPairingOfferResponseSchema,
DaemonConfigReloadResponseSchema,
HubManagementDaemonConnectResponseSchema,
HubManagementDaemonGetStatusResponseSchema,
HubManagementDaemonDisconnectResponseSchema,
@@ -6179,6 +6217,7 @@ export type ListProviderFeaturesResponseMessage = z.infer<
export type ListAvailableProvidersResponse = z.infer<typeof ListAvailableProvidersResponseSchema>;
export type DaemonGetStatusResponse = z.infer<typeof DaemonGetStatusResponseSchema>;
export type DaemonGetPairingOfferResponse = z.infer<typeof DaemonGetPairingOfferResponseSchema>;
export type DaemonConfigReloadResponse = z.infer<typeof DaemonConfigReloadResponseSchema>;
export type DiagnosticsResponse = z.infer<typeof DiagnosticsResponseSchema>;
export type GetProvidersSnapshotResponseMessage = z.infer<
typeof GetProvidersSnapshotResponseMessageSchema