refactor(gateway): share config restart sentinel builder

This commit is contained in:
Peter Steinberger
2026-02-15 01:04:52 +00:00
parent f58d4cad8e
commit 4104229996

View File

@@ -93,6 +93,74 @@ function requireConfigBaseHash(
return true; return true;
} }
function resolveConfigRestartRequest(params: unknown): {
sessionKey: string | undefined;
note: string | undefined;
restartDelayMs: number | undefined;
deliveryContext: ReturnType<typeof extractDeliveryInfo>["deliveryContext"];
threadId: ReturnType<typeof extractDeliveryInfo>["threadId"];
} {
const sessionKey =
typeof (params as { sessionKey?: unknown }).sessionKey === "string"
? (params as { sessionKey?: string }).sessionKey?.trim() || undefined
: undefined;
const note =
typeof (params as { note?: unknown }).note === "string"
? (params as { note?: string }).note?.trim() || undefined
: undefined;
const restartDelayMsRaw = (params as { restartDelayMs?: unknown }).restartDelayMs;
const restartDelayMs =
typeof restartDelayMsRaw === "number" && Number.isFinite(restartDelayMsRaw)
? Math.max(0, Math.floor(restartDelayMsRaw))
: undefined;
// Extract deliveryContext + threadId for routing after restart
// Supports both :thread: (most channels) and :topic: (Telegram)
const { deliveryContext, threadId } = extractDeliveryInfo(sessionKey);
return {
sessionKey,
note,
restartDelayMs,
deliveryContext,
threadId,
};
}
function buildConfigRestartSentinelPayload(params: {
kind: RestartSentinelPayload["kind"];
mode: string;
sessionKey: string | undefined;
deliveryContext: ReturnType<typeof extractDeliveryInfo>["deliveryContext"];
threadId: ReturnType<typeof extractDeliveryInfo>["threadId"];
note: string | undefined;
}): RestartSentinelPayload {
return {
kind: params.kind,
status: "ok",
ts: Date.now(),
sessionKey: params.sessionKey,
deliveryContext: params.deliveryContext,
threadId: params.threadId,
message: params.note ?? null,
doctorHint: formatDoctorNonInteractiveHint(),
stats: {
mode: params.mode,
root: CONFIG_PATH,
},
};
}
async function tryWriteRestartSentinelPayload(
payload: RestartSentinelPayload,
): Promise<string | null> {
try {
return await writeRestartSentinel(payload);
} catch {
return null;
}
}
function loadSchemaWithPlugins(): ConfigSchemaResponse { function loadSchemaWithPlugins(): ConfigSchemaResponse {
const cfg = loadConfig(); const cfg = loadConfig();
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)); const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
@@ -303,44 +371,17 @@ export const configHandlers: GatewayRequestHandlers = {
} }
await writeConfigFile(validated.config, writeOptions); await writeConfigFile(validated.config, writeOptions);
const sessionKey = const { sessionKey, note, restartDelayMs, deliveryContext, threadId } =
typeof (params as { sessionKey?: unknown }).sessionKey === "string" resolveConfigRestartRequest(params);
? (params as { sessionKey?: string }).sessionKey?.trim() || undefined const payload = buildConfigRestartSentinelPayload({
: undefined;
const note =
typeof (params as { note?: unknown }).note === "string"
? (params as { note?: string }).note?.trim() || undefined
: undefined;
const restartDelayMsRaw = (params as { restartDelayMs?: unknown }).restartDelayMs;
const restartDelayMs =
typeof restartDelayMsRaw === "number" && Number.isFinite(restartDelayMsRaw)
? Math.max(0, Math.floor(restartDelayMsRaw))
: undefined;
// Extract deliveryContext + threadId for routing after restart
// Supports both :thread: (most channels) and :topic: (Telegram)
const { deliveryContext, threadId } = extractDeliveryInfo(sessionKey);
const payload: RestartSentinelPayload = {
kind: "config-patch", kind: "config-patch",
status: "ok", mode: "config.patch",
ts: Date.now(),
sessionKey, sessionKey,
deliveryContext, deliveryContext,
threadId, threadId,
message: note ?? null, note,
doctorHint: formatDoctorNonInteractiveHint(), });
stats: { const sentinelPath = await tryWriteRestartSentinelPayload(payload);
mode: "config.patch",
root: CONFIG_PATH,
},
};
let sentinelPath: string | null = null;
try {
sentinelPath = await writeRestartSentinel(payload);
} catch {
sentinelPath = null;
}
const restart = scheduleGatewaySigusr1Restart({ const restart = scheduleGatewaySigusr1Restart({
delayMs: restartDelayMs, delayMs: restartDelayMs,
reason: "config.patch", reason: "config.patch",
@@ -416,45 +457,17 @@ export const configHandlers: GatewayRequestHandlers = {
} }
await writeConfigFile(validated.config, writeOptions); await writeConfigFile(validated.config, writeOptions);
const sessionKey = const { sessionKey, note, restartDelayMs, deliveryContext, threadId } =
typeof (params as { sessionKey?: unknown }).sessionKey === "string" resolveConfigRestartRequest(params);
? (params as { sessionKey?: string }).sessionKey?.trim() || undefined const payload = buildConfigRestartSentinelPayload({
: undefined;
const note =
typeof (params as { note?: unknown }).note === "string"
? (params as { note?: string }).note?.trim() || undefined
: undefined;
const restartDelayMsRaw = (params as { restartDelayMs?: unknown }).restartDelayMs;
const restartDelayMs =
typeof restartDelayMsRaw === "number" && Number.isFinite(restartDelayMsRaw)
? Math.max(0, Math.floor(restartDelayMsRaw))
: undefined;
// Extract deliveryContext + threadId for routing after restart
// Supports both :thread: (most channels) and :topic: (Telegram)
const { deliveryContext: deliveryContextApply, threadId: threadIdApply } =
extractDeliveryInfo(sessionKey);
const payload: RestartSentinelPayload = {
kind: "config-apply", kind: "config-apply",
status: "ok", mode: "config.apply",
ts: Date.now(),
sessionKey, sessionKey,
deliveryContext: deliveryContextApply, deliveryContext,
threadId: threadIdApply, threadId,
message: note ?? null, note,
doctorHint: formatDoctorNonInteractiveHint(), });
stats: { const sentinelPath = await tryWriteRestartSentinelPayload(payload);
mode: "config.apply",
root: CONFIG_PATH,
},
};
let sentinelPath: string | null = null;
try {
sentinelPath = await writeRestartSentinel(payload);
} catch {
sentinelPath = null;
}
const restart = scheduleGatewaySigusr1Restart({ const restart = scheduleGatewaySigusr1Restart({
delayMs: restartDelayMs, delayMs: restartDelayMs,
reason: "config.apply", reason: "config.apply",