Skip to content

Commit 833e370

Browse files
committed
fix(dashboard-agent): wake self-heals a missing chat session
A chat born from the watch card (0 LLM) never had a session, so the wake's in.send 404'd and the delivery stayed pending. On 404 the wake now creates the session (idempotent on externalId) and retries once.
1 parent 263183b commit 833e370

1 file changed

Lines changed: 46 additions & 17 deletions

File tree

internal-packages/dashboard-agent/src/watch-tick.ts

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -656,24 +656,53 @@ export async function appendWakeToSession(args: {
656656
action: WatchWakeAction;
657657
watch: Watch;
658658
}): Promise<void> {
659-
await sessions.open(args.chatId).in.send({
660-
kind: "message",
661-
payload: {
662-
chatId: args.chatId,
663-
trigger: "action",
664-
action: args.action,
665-
metadata: {
666-
userId: args.watch.userId,
667-
organizationId: args.watch.organizationId,
668-
projectId: args.watch.projectId,
669-
environmentId: args.watch.environmentId,
670-
// The external ref a consented investigation is scoped by — the same
671-
// one a normal turn carries, so a follow-up turn revises that
672-
// investigation instead of opening a second one.
673-
...(args.watch.projectRef ? { projectRef: args.watch.projectRef } : {}),
659+
const metadata = {
660+
userId: args.watch.userId,
661+
organizationId: args.watch.organizationId,
662+
projectId: args.watch.projectId,
663+
environmentId: args.watch.environmentId,
664+
// The external ref a consented investigation is scoped by — the same
665+
// one a normal turn carries, so a follow-up turn revises that
666+
// investigation instead of opening a second one.
667+
...(args.watch.projectRef ? { projectRef: args.watch.projectRef } : {}),
668+
};
669+
670+
const send = () =>
671+
sessions.open(args.chatId).in.send({
672+
kind: "message",
673+
payload: {
674+
chatId: args.chatId,
675+
trigger: "action",
676+
action: args.action,
677+
metadata,
674678
},
675-
},
676-
});
679+
});
680+
681+
try {
682+
await send();
683+
} catch (error) {
684+
// A chat born from the configuration card (0 LLM) has no session yet —
685+
// the card's confirmation is a direct JSONB append. The wake is the first
686+
// thing that needs one, so create it here (idempotent on externalId) and
687+
// retry once. Any other failure keeps the claim's retry semantics.
688+
if (!isSessionNotFound(error)) throw error;
689+
await sessions.start({
690+
type: "chat.agent",
691+
externalId: args.chatId,
692+
taskIdentifier: "dashboard-agent",
693+
triggerConfig: {
694+
basePayload: { trigger: "preload", chatId: args.chatId, metadata },
695+
},
696+
});
697+
await send();
698+
}
699+
}
700+
701+
/** A 404 from a session call: no Session row exists for this chat id. */
702+
function isSessionNotFound(error: unknown): boolean {
703+
if (error === null || typeof error !== "object") return false;
704+
const e = error as { name?: string; status?: number };
705+
return e.name === "TriggerApiError" && e.status === 404;
677706
}
678707

679708
export const watchTick = task({

0 commit comments

Comments
 (0)