From b6c2905af719d22f03dbf981698bc08d1374f9d1 Mon Sep 17 00:00:00 2001 From: Ramnique Singh <30795890+ramnique@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:40:44 +0530 Subject: [PATCH] fix(x): notify-user derives its use-case fallback from the turn record MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ALS-miss fallback still imported fetchRun from the legacy runs store, but ctx.runId has been a turn id since the runs->turns migration — the lookup always threw and fell through, so a background-task notification delivered outside its starting ALS context (crash recovery, resume paths) lost its background_task gating and fired as a plain notification. The durable equivalent lives on the turn itself: turn_created's agent.resolved.agentId, and only the background-task runner starts 'background-task-agent' turns. Also removes one of the last live imports into runtime/legacy/. Co-Authored-By: Claude Fable 5 --- .../src/runtime/tools/domains/notifications.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/x/packages/core/src/runtime/tools/domains/notifications.ts b/apps/x/packages/core/src/runtime/tools/domains/notifications.ts index 5e488c11..45e23013 100644 --- a/apps/x/packages/core/src/runtime/tools/domains/notifications.ts +++ b/apps/x/packages/core/src/runtime/tools/domains/notifications.ts @@ -7,6 +7,7 @@ import container from "../../../di/container.js"; import type { ToolContext } from "../exec-tool.js"; import { getCurrentUseCase } from "../../../analytics/use_case.js"; import type { INotificationService } from "../../../application/notification/service.js"; +import type { ITurnRepo } from "../../turns/repo.js"; import { notifyIfEnabled } from "../../../application/notification/notifier.js"; import { BuiltinToolsSchema } from "../types.js"; @@ -42,14 +43,19 @@ export const notificationTools: z.infer = { return { success: false, error: 'Notifications are not supported on this system' }; } let uc = getCurrentUseCase()?.useCase; - // ALS doesn't reliably propagate across the run's async generator, - // so when the in-context use-case is missing, fall back to the - // persisted use case on the run record via ctx.runId. + // The ALS context can be missing when the turn is advanced + // outside the chain that started it (crash recovery, resume + // paths). The durable signal is the agent id on the turn + // record — only the background-task runner starts + // 'background-task-agent' turns. ctx.runId is the turn id. if (!uc && ctx?.runId) { try { - const { fetchRun } = await import("../../legacy/runs.js"); - const run = await fetchRun(ctx.runId); - uc = run.useCase; + const turnRepo = container.resolve('turnRepo'); + const [created] = await turnRepo.read(ctx.runId); + if (created?.type === 'turn_created' + && created.agent.resolved.agentId === 'background-task-agent') { + uc = 'background_task_agent'; + } } catch { // best effort — fall through to the default branch }