From 5d95f3e2e67792edc387f124c95d6faeac81fcfc Mon Sep 17 00:00:00 2001 From: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com> Date: Wed, 12 Aug 2026 13:30:23 +1000 Subject: [PATCH] fix(system-agent): replace expired QR prompt --- src/system-agent/chat-wizard-host.test.ts | 25 +++++++++++++++++++++++ src/system-agent/chat-wizard-host.ts | 14 +++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/system-agent/chat-wizard-host.test.ts b/src/system-agent/chat-wizard-host.test.ts index 76729a51a42b..8f6e7afceb60 100644 --- a/src/system-agent/chat-wizard-host.test.ts +++ b/src/system-agent/chat-wizard-host.test.ts @@ -342,6 +342,31 @@ describe("SystemAgentChatEngine wizard", () => { expect(engine.hasPendingQrCode()).toBe(true); await engine.handle("cancel"); }); + + it("replaces scan instructions when the QR expires before its first projection", async () => { + vi.useFakeTimers(); + vi.setSystemTime(1_800_000_000_000); + const engine = createQrEngine(async (_channel, prompter) => { + await prompter.qrCode?.({ + title: "Link a device", + message: "Scan this QR code and approve the device.", + text: QR_TEXT, + dismissed: new Promise(() => {}), + expiresAtMs: 1_800_000_000_000, + }); + }); + + const prompt = await engine.handle("connect telegram"); + + expect(prompt.text).toBe( + "This setup QR code expired. Setup is still finishing the attempt automatically.", + ); + expect(prompt.text).not.toContain("Scan"); + expect(prompt.step).toBeUndefined(); + expect(prompt.wizardSettling).toBe(true); + await engine.handle("cancel"); + }); + it("recommends the confirm option matching the initial value", async () => { let enabled: boolean | undefined; const engine = new SystemAgentChatEngine({ diff --git a/src/system-agent/chat-wizard-host.ts b/src/system-agent/chat-wizard-host.ts index 0e95273312c1..5234172feb14 100644 --- a/src/system-agent/chat-wizard-host.ts +++ b/src/system-agent/chat-wizard-host.ts @@ -100,6 +100,8 @@ type ActiveWizardBridge = { const log = createSubsystemLogger("system-agent/chat-wizard-host"); const SYSTEM_AGENT_HOSTED_WIZARD_TIMEOUT_MS = 25 * 60 * 1000; const WIZARD_CANCEL_HINT = "Say `cancel` to stop this setup."; +const WIZARD_QR_EXPIRED_MESSAGE = + "This setup QR code expired. Setup is still finishing the attempt automatically."; let hostedRuntimePromise: Promise | undefined; function loadHostedRuntime(): Promise { @@ -147,10 +149,14 @@ export class ChatWizardHost { decorateReply(reply: SystemAgentChatReply): SystemAgentChatReply { this.expireActiveQrIfNeeded(); const step = this.bridge?.step ?? null; - const completedReply = - reply.text && step && step.type !== "qr" && wizardStepAwaitsInput(step) - ? { ...reply, text: `${reply.text}\n${WIZARD_CANCEL_HINT}` } + const projectedReply = + this.bridge?.qrExpired === true && this.bridge.session.hasExternalQrPresentationOwner() + ? { ...reply, text: WIZARD_QR_EXPIRED_MESSAGE } : reply; + const completedReply = + projectedReply.text && step && step.type !== "qr" && wizardStepAwaitsInput(step) + ? { ...projectedReply, text: `${projectedReply.text}\n${WIZARD_CANCEL_HINT}` } + : projectedReply; const question = wizardStepChatQuestion(step); const clientStep = step ? sanitizeWizardStepForClient( @@ -241,7 +247,7 @@ export class ChatWizardHost { if (bridge.qrExpired) { if (bridge.session.hasExternalQrPresentationOwner()) { return { - text: "This setup QR code expired. Setup is still finishing the attempt automatically.", + text: WIZARD_QR_EXPIRED_MESSAGE, configWritten: false, }; }