fix(x): notify-user derives its use-case fallback from the turn record

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 <noreply@anthropic.com>
This commit is contained in:
Ramnique Singh 2026-07-10 16:40:44 +05:30
parent 599b7d13fb
commit b6c2905af7

View file

@ -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<typeof BuiltinToolsSchema> = {
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<ITurnRepo>('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
}