import { resolveAnnounceTargetFromKey } from "../agents/tools/sessions-send-helpers.js"; import { normalizeChannelId } from "../channels/plugins/index.js"; import type { CliDeps } from "../cli/deps.js"; import { agentCommand } from "../commands/agent.js"; import { resolveMainSessionKeyFromConfig } from "../config/sessions.js"; import { resolveOutboundTarget } from "../infra/outbound/targets.js"; import { consumeRestartSentinel, formatRestartSentinelMessage, summarizeRestartSentinel, } from "../infra/restart-sentinel.js"; import { enqueueSystemEvent } from "../infra/system-events.js"; import { defaultRuntime } from "../runtime.js"; import { deliveryContextFromSession, mergeDeliveryContext } from "../utils/delivery-context.js"; import { loadSessionEntry } from "./session-utils.js"; export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) { const sentinel = await consumeRestartSentinel(); if (!sentinel) return; const payload = sentinel.payload; const sessionKey = payload.sessionKey?.trim(); const message = formatRestartSentinelMessage(payload); const summary = summarizeRestartSentinel(payload); if (!sessionKey) { const mainSessionKey = resolveMainSessionKeyFromConfig(); enqueueSystemEvent(message, { sessionKey: mainSessionKey }); return; } const threadMarker = ":thread:"; const threadIndex = sessionKey.lastIndexOf(threadMarker); const baseSessionKey = threadIndex === -1 ? sessionKey : sessionKey.slice(0, threadIndex); const threadIdRaw = threadIndex === -1 ? undefined : sessionKey.slice(threadIndex + threadMarker.length); const sessionThreadId = threadIdRaw?.trim() || undefined; const { cfg, entry } = loadSessionEntry(sessionKey); const parsedTarget = resolveAnnounceTargetFromKey(baseSessionKey); // Prefer delivery context from sentinel (captured at restart) over session store // Handles race condition where store wasn't flushed before restart const sentinelContext = payload.deliveryContext; let sessionDeliveryContext = deliveryContextFromSession(entry); if (!sessionDeliveryContext && threadIndex !== -1 && baseSessionKey) { const { entry: baseEntry } = loadSessionEntry(baseSessionKey); sessionDeliveryContext = deliveryContextFromSession(baseEntry); } const origin = mergeDeliveryContext( sentinelContext, mergeDeliveryContext(sessionDeliveryContext, parsedTarget ?? undefined), ); const channelRaw = origin?.channel; const channel = channelRaw ? normalizeChannelId(channelRaw) : null; const to = origin?.to; if (!channel || !to) { enqueueSystemEvent(message, { sessionKey }); return; } const resolved = resolveOutboundTarget({ channel, to, cfg, accountId: origin?.accountId, mode: "implicit", }); if (!resolved.ok) { enqueueSystemEvent(message, { sessionKey }); return; } const threadId = payload.threadId ?? sessionThreadId ?? (origin?.threadId != null ? String(origin.threadId) : undefined); try { await agentCommand( { message, sessionKey, to: resolved.to, channel, deliver: true, bestEffortDeliver: true, messageChannel: channel, threadId, }, defaultRuntime, params.deps, ); } catch (err) { enqueueSystemEvent(`${summary}\n${String(err)}`, { sessionKey }); } } export function shouldWakeFromRestartSentinel() { return !process.env.VITEST && process.env.NODE_ENV !== "test"; }