From 3952fb4b72535463724fa051c18063c915f07240 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sun, 9 Aug 2026 21:12:57 +0800 Subject: [PATCH] fix(schedule): follow latest session and chat contracts --- apps/web/tests/schedule-after.e2e.ts | 20 ++++++++++++------- .../schedule-after/conversation.expected.md | 5 ----- packages/schedule/tool-schedule/src/types.ts | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/apps/web/tests/schedule-after.e2e.ts b/apps/web/tests/schedule-after.e2e.ts index a466bc92b0..dd47bca4a8 100644 --- a/apps/web/tests/schedule-after.e2e.ts +++ b/apps/web/tests/schedule-after.e2e.ts @@ -10,6 +10,7 @@ import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm' import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm' import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' import { RpcId } from '@deepseek-ai/dsh-host-apiproxy/api/rpc' +import { conversationContextKey } from '@deepseek-ai/dsh-client-runtime/client' import { assertFixtureInventory, captureStableAria, @@ -50,14 +51,14 @@ function assistantText(event: Extract { +/** Wait for and return the exact durable scheduled assistant reply. */ +async function waitForReply(handle: AgentHandle, timeoutMs: number): Promise> { const deadline = Date.now() + timeoutMs while (true) { const event = handle.agent.session.events.find((candidate): candidate is SessionEvent<'assistant/message'> => ( candidate.type === 'assistant/message' && assistantText(candidate) === REPLY )) - if (event !== undefined) return event.seq + if (event !== undefined) return event if (Date.now() >= deadline) throw new Error(`scheduled assistant reply did not arrive within ${timeoutMs}ms`) await new Promise(resolve => setTimeout(resolve, 20)) } @@ -69,7 +70,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational after reminder', () let adapter: ReminderAdapter let browser: Browser let page: Page - let assistantSeq = -1 + let assistantReply: SessionEvent<'assistant/message'> | undefined let tripwire: ReturnType beforeAll(async () => { @@ -118,7 +119,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational after reminder', () state: 'scheduled', deliveryMode: 'session-local', }) - assistantSeq = await waitForReply(agentHandle, 15_000) + assistantReply = await waitForReply(agentHandle, 15_000) await agentHandle.agent.whenIdle() const reminder = adapter.requests.at(-1)?.messages.find(message => ( message.source.kind === 'plugin' && message.source.plugin === 'tool-schedule' @@ -162,10 +163,15 @@ describe.skipIf(MODE === 'record')('web e2e: conversational after reminder', () await session.waitFor({ timeout: 15_000 }) await session.click() - const selector = `[data-chat-anchor-key="node:${String(assistantSeq)}"]` + if (assistantReply === undefined) throw new Error('scheduled assistant reply was not captured') + const key = conversationContextKey( + 'assistant-step', + `${String(assistantReply.data.turn)}:${String(assistantReply.data.step)}`, + ) + const selector = `[data-chat-anchor-key="${key}"]` const row = page.locator(selector) await row.waitFor({ timeout: 15_000 }) - expect(await row.getAttribute('data-chat-flow-kind')).toBe('assistant') + expect(await row.getAttribute('data-chat-flow-kind')).toBe('assistant-step') expect(await row.textContent()).toContain(REPLY) await compareOrRefreshGolden( CONVERSATION_EXPECTED, diff --git a/apps/web/tests/snapshots/schedule-after/conversation.expected.md b/apps/web/tests/snapshots/schedule-after/conversation.expected.md index c8847cfb7f..995cfb9044 100644 --- a/apps/web/tests/snapshots/schedule-after/conversation.expected.md +++ b/apps/web/tests/snapshots/schedule-after/conversation.expected.md @@ -1,6 +1 @@ - paragraph: "Reminder: Check the deployment log." -- button "Copy": - - img -- button "Branch into a new conversation": - - img -- text: {{clock}} Ran for {{duration}} diff --git a/packages/schedule/tool-schedule/src/types.ts b/packages/schedule/tool-schedule/src/types.ts index 2afb189eb2..2d7b695cef 100644 --- a/packages/schedule/tool-schedule/src/types.ts +++ b/packages/schedule/tool-schedule/src/types.ts @@ -4,7 +4,7 @@ */ import type { Branded } from '@deepseek-ai/dsh-brand' -import type {} from '@deepseek-ai/dsh-session' +import type {} from '@deepseek-ai/dsh-session/types' /** Stable reminder identity that is unique and never reused within one session. */ export type ScheduleId = Branded<'ScheduleId'> @@ -135,7 +135,7 @@ export type ScheduleDeleteResult = /** Canonical `schedule_delete` value. */ export type ScheduleDeleteValue = ScheduleDeleteResult | ScheduleToolError -declare module '@deepseek-ai/dsh-session' { +declare module '@deepseek-ai/dsh-session/types' { interface SessionEventMap { /** * Versioned Schedule mutation. The owning package validates the complete