From 149e63c2f77e194c49509f117146def7308829c7 Mon Sep 17 00:00:00 2001 From: coso Date: Sun, 29 Mar 2026 14:34:26 +0800 Subject: [PATCH] fix: unblock v0.98.0 release workflow --- src/components/AppPageContent.test.tsx | 126 ++++++++++++++++++ src/components/AppPageContent.tsx | 1 + .../chat/components/AgentThreadTimeline.tsx | 18 +-- .../components/EmptyStateComposerPanel.tsx | 5 +- .../ServiceSkillLaunchDialog.tsx | 10 +- .../agent/chat/service-skills/types.ts | 1 + .../useWorkspaceBrowserAssistRuntime.ts | 23 +--- .../useWorkspaceConversationSceneRuntime.tsx | 11 +- src/lib/dev-bridge/http-client.test.ts | 2 +- 9 files changed, 147 insertions(+), 50 deletions(-) create mode 100644 src/components/AppPageContent.test.tsx diff --git a/src/components/AppPageContent.test.tsx b/src/components/AppPageContent.test.tsx new file mode 100644 index 000000000..3446cf661 --- /dev/null +++ b/src/components/AppPageContent.test.tsx @@ -0,0 +1,126 @@ +import { Suspense } from "react"; +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { AgentPageParams, Page } from "@/types/page"; +import { AppPageContent } from "./AppPageContent"; + +const latestAgentChatProps = vi.hoisted( + () => + ({ + value: null as Record | null, + }) as { value: Record | null }, +); + +vi.mock("./agent/chat", () => ({ + AgentChatPage: (props: Record) => { + latestAgentChatProps.value = props; + return
; + }, +})); + +interface MountedContent { + container: HTMLDivElement; + root: Root; +} + +const mountedContents: MountedContent[] = []; + +function renderContent(pageParams: AgentPageParams) { + const container = document.createElement("div"); + document.body.appendChild(container); + const root = createRoot(container); + + act(() => { + root.render( + }> + void} + onAgentHasMessagesChange={vi.fn()} + /> + , + ); + }); + + mountedContents.push({ container, root }); + return container; +} + +async function flushEffects(times = 6) { + await act(async () => { + for (let index = 0; index < times; index += 1) { + await Promise.resolve(); + } + }); +} + +describe("AppPageContent", () => { + beforeEach(() => { + vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); + latestAgentChatProps.value = null; + }); + + afterEach(() => { + while (mountedContents.length > 0) { + const mounted = mountedContents.pop(); + if (!mounted) { + continue; + } + + act(() => { + mounted.root.unmount(); + }); + mounted.container.remove(); + } + + vi.clearAllMocks(); + vi.unstubAllGlobals(); + }); + + it("agent 页面应把 initialSiteSkillLaunch 透传给 AgentChatPage", async () => { + const pageParams: AgentPageParams = { + agentEntry: "claw", + projectId: "project-1", + contentId: "content-1", + theme: "general", + newChatAt: 1234567890, + initialSiteSkillLaunch: { + adapterName: "linux-do/categories", + args: { + limit: 10, + }, + autoRun: true, + profileKey: "attached-linux-do", + targetId: "tab-linux-do", + requireAttachedSession: true, + saveTitle: "Linux.do 分类扫描", + skillTitle: "Linux.do 分类扫描", + }, + }; + + renderContent(pageParams); + await flushEffects(); + + expect(latestAgentChatProps.value).toMatchObject({ + projectId: "project-1", + contentId: "content-1", + agentEntry: "claw", + theme: "general", + newChatAt: 1234567890, + initialSiteSkillLaunch: { + adapterName: "linux-do/categories", + args: { + limit: 10, + }, + autoRun: true, + profileKey: "attached-linux-do", + targetId: "tab-linux-do", + requireAttachedSession: true, + saveTitle: "Linux.do 分类扫描", + skillTitle: "Linux.do 分类扫描", + }, + }); + }); +}); diff --git a/src/components/AppPageContent.tsx b/src/components/AppPageContent.tsx index c0ae56015..7e6435a9c 100644 --- a/src/components/AppPageContent.tsx +++ b/src/components/AppPageContent.tsx @@ -215,6 +215,7 @@ export function AppPageContent({ entryBannerMessage={agentPageParams.entryBannerMessage} immersiveHome={agentPageParams.immersiveHome} openBrowserAssistOnMount={agentPageParams.openBrowserAssistOnMount} + initialSiteSkillLaunch={agentPageParams.initialSiteSkillLaunch} theme={agentPageParams.theme} lockTheme={agentPageParams.lockTheme} fromResources={agentPageParams.fromResources} diff --git a/src/components/agent/chat/components/AgentThreadTimeline.tsx b/src/components/agent/chat/components/AgentThreadTimeline.tsx index e477d5e07..e865b8baf 100644 --- a/src/components/agent/chat/components/AgentThreadTimeline.tsx +++ b/src/components/agent/chat/components/AgentThreadTimeline.tsx @@ -681,23 +681,7 @@ function extractCompactThinkingParts(item: Extract< }; } - const displayText = resolveThinkingDisplayText(item); - const parsed = parseAIResponse(displayText, false); - if (parsed.hasA2UI || parsed.hasPending) { - return null; - } - - const lines = displayText - .split(/\r?\n/) - .map((line) => line.trim()) - .filter(Boolean); - const [title = item.status === "in_progress" ? "思考中" : "已完成思考", ...rest] = - lines; - - return { - title, - detail: rest.join("\n").trim(), - }; + return null; } function GroupedThinkingRow({ diff --git a/src/components/agent/chat/components/EmptyStateComposerPanel.tsx b/src/components/agent/chat/components/EmptyStateComposerPanel.tsx index 104150d27..334209906 100644 --- a/src/components/agent/chat/components/EmptyStateComposerPanel.tsx +++ b/src/components/agent/chat/components/EmptyStateComposerPanel.tsx @@ -429,7 +429,10 @@ export function EmptyStateComposerPanel({ ) : null} {activeSkill ? ( - + undefined)} + /> ) : null} {shouldShowTeamSuggestion ? ( diff --git a/src/components/agent/chat/service-skills/ServiceSkillLaunchDialog.tsx b/src/components/agent/chat/service-skills/ServiceSkillLaunchDialog.tsx index ffce5c1cf..37d5bc548 100644 --- a/src/components/agent/chat/service-skills/ServiceSkillLaunchDialog.tsx +++ b/src/components/agent/chat/service-skills/ServiceSkillLaunchDialog.tsx @@ -202,11 +202,12 @@ export function ServiceSkillLaunchDialog({ setSiteLaunchReadiness({ phase: "idle" }); return; } + const siteCapabilityBinding = skill.siteCapabilityBinding; setSiteLaunchReadiness({ phase: "checking" }); try { const result = await siteGetAdapterLaunchReadiness({ - adapter_name: skill.siteCapabilityBinding.adapterName, + adapter_name: siteCapabilityBinding.adapterName, }); setSiteLaunchReadiness({ phase: result.status === "ready" ? "ready" : "blocked", @@ -221,17 +222,18 @@ export function ServiceSkillLaunchDialog({ }, [open, skill]); useEffect(() => { - if (!open || !skill || !isSiteSkill) { + if (!open || !skill || !isServiceSkillExecutableAsSiteAdapter(skill)) { setSiteLaunchReadiness({ phase: "idle" }); return; } + const siteCapabilityBinding = skill.siteCapabilityBinding; let cancelled = false; void (async () => { setSiteLaunchReadiness({ phase: "checking" }); try { const result = await siteGetAdapterLaunchReadiness({ - adapter_name: skill.siteCapabilityBinding.adapterName, + adapter_name: siteCapabilityBinding.adapterName, }); if (cancelled) { return; @@ -254,7 +256,7 @@ export function ServiceSkillLaunchDialog({ return () => { cancelled = true; }; - }, [isSiteSkill, open, skill]); + }, [open, skill]); const readinessToneClass = siteLaunchReadiness.phase === "ready" diff --git a/src/components/agent/chat/service-skills/types.ts b/src/components/agent/chat/service-skills/types.ts index 7a3985251..500f0f0a8 100644 --- a/src/components/agent/chat/service-skills/types.ts +++ b/src/components/agent/chat/service-skills/types.ts @@ -42,6 +42,7 @@ export type { ServiceSkillType, }; export type { SkillCatalogExecutionKind, SkillCatalogGroup }; +export type ServiceSkillGroup = SkillCatalogGroup; export type ServiceSkillTone = "slate" | "sky" | "emerald" | "amber"; diff --git a/src/components/agent/chat/workspace/useWorkspaceBrowserAssistRuntime.ts b/src/components/agent/chat/workspace/useWorkspaceBrowserAssistRuntime.ts index 09658cbb9..08445c238 100644 --- a/src/components/agent/chat/workspace/useWorkspaceBrowserAssistRuntime.ts +++ b/src/components/agent/chat/workspace/useWorkspaceBrowserAssistRuntime.ts @@ -105,18 +105,6 @@ function shouldAutoOpenPassiveBrowserAssist( ); } -function shouldAutoOpenBrowserAssistCanvas( - artifact: Artifact | null, - launching: boolean, - sessionState: BrowserAssistSessionState | null, -): boolean { - if (shouldAutoOpenPassiveBrowserAssist(artifact, launching)) { - return true; - } - - return Boolean(sessionState?.sessionId?.trim()); -} - type EnsureBrowserAssistCanvasHandler = ( sourceText: string, options?: { @@ -429,7 +417,6 @@ export function useWorkspaceBrowserAssistRuntime({ void (async () => { try { - setBrowserAssistLaunching(true); setSiteSkillExecutionState({ phase: "running", adapterName: initialSiteSkillLaunch.adapterName, @@ -541,9 +528,7 @@ export function useWorkspaceBrowserAssistRuntime({ { id: toastId }, ); } finally { - if (!cancelled) { - setBrowserAssistLaunching(false); - } + // 站点技能执行不应驱动浏览器画布的“启动中”状态。 } })(); @@ -1030,11 +1015,7 @@ export function useWorkspaceBrowserAssistRuntime({ browserAssistSessionState.profileKey || generalBrowserAssistProfileKey }:${browserAssistSessionState.url || currentUrl || "pending"}`; if ( - shouldAutoOpenBrowserAssistCanvas( - nextArtifact, - browserAssistLaunching, - browserAssistSessionState, - ) && + shouldAutoOpenPassiveBrowserAssist(nextArtifact, browserAssistLaunching) && autoOpenedBrowserAssistSessionIdRef.current !== autoOpenKey ) { autoOpenedBrowserAssistSessionIdRef.current = autoOpenKey; diff --git a/src/components/agent/chat/workspace/useWorkspaceConversationSceneRuntime.tsx b/src/components/agent/chat/workspace/useWorkspaceConversationSceneRuntime.tsx index 2a992828b..10f5c6253 100644 --- a/src/components/agent/chat/workspace/useWorkspaceConversationSceneRuntime.tsx +++ b/src/components/agent/chat/workspace/useWorkspaceConversationSceneRuntime.tsx @@ -291,13 +291,12 @@ export function useWorkspaceConversationSceneRuntime({ .map((line) => `> ${line}`) .join("\n")}\n\n`; - setInput((previous) => { - if (!previous.trim()) { - return quotedBlock; - } + if (!input.trim()) { + setInput(quotedBlock); + return; + } - return `${previous.trimEnd()}\n\n${quotedBlock}`; - }); + setInput(`${input.trimEnd()}\n\n${quotedBlock}`); }; const teamWorkspaceDockLayoutMode = diff --git a/src/lib/dev-bridge/http-client.test.ts b/src/lib/dev-bridge/http-client.test.ts index ec8682cf5..0e9c8090e 100644 --- a/src/lib/dev-bridge/http-client.test.ts +++ b/src/lib/dev-bridge/http-client.test.ts @@ -41,7 +41,7 @@ describe("http-client listenViaHttpEvent", () => { vi.resetModules(); MockEventSource.instances = []; vi.stubEnv("MODE", "development"); - vi.stubEnv("DEV", "true"); + vi.stubEnv("DEV", true); vi.stubEnv("VITEST", ""); vi.stubGlobal("EventSource", MockEventSource as unknown as typeof EventSource); });