# DeepSeek Monitoring Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add production-usable `deepseek` desktop monitoring support with answer capture, citation extraction, runtime routing, and regression coverage. **Architecture:** Reuse the existing generic AI auth flow, add a new Playwright-based desktop monitor adapter for DeepSeek, register it in the desktop runtime, and keep server-side result ingestion unchanged. The adapter should favor structured page observation plus network-response capture so answer capture and citation extraction can degrade independently. **Tech Stack:** TypeScript + Vitest in `apps/desktop-client`, Go + `testing` in `server/internal/tenant/app`, Playwright hidden-page execution, pnpm workspace, existing shared monitoring payload contracts. --- ### Task 1: Lock In Failing Tests First **Files:** - Create: `apps/desktop-client/src/main/adapters/deepseek.test.ts` - Modify: `server/internal/tenant/app/monitoring_callback_service_test.go` - [ ] **Step 1: Write the failing desktop adapter helper tests** ```ts import { describe, expect, it } from "vitest"; import { __deepseekTestUtils } from "./deepseek"; const { extractQuestionText, dedupeSourceItems, classifyDeepseekSources, isObservationComplete, } = __deepseekTestUtils; describe("deepseek adapter helpers", () => { it("extracts question text from monitoring payload", () => { expect(extractQuestionText({ question_text: "DeepSeek 怎么看 GEO?" })).toBe("DeepSeek 怎么看 GEO?"); }); it("dedupes sources by normalized url", () => { const items = dedupeSourceItems([ { url: "https://example.com/a#top", normalized_url: "https://example.com/a#top" }, { url: "https://example.com/a#bottom", normalized_url: "https://example.com/a#bottom" }, ]); expect(items).toHaveLength(1); expect(items[0]?.normalized_url).toBe("https://example.com/a"); }); it("separates citations from search-style sources", () => { const classified = classifyDeepseekSources({ inlineLinks: [{ url: "https://example.com/citation", normalized_url: "https://example.com/citation" }], sourcePanelLinks: [], searchPanelLinks: [{ url: "https://example.com/search", normalized_url: "https://example.com/search" }], }); expect(classified.citations).toHaveLength(1); expect(classified.searchResults).toHaveLength(1); }); it("treats a stable answer as complete even without citations", () => { expect(isObservationComplete({ answer: "这是最终答案", busy: false, loginRequired: false, challengeRequired: false, signature: "sig-1", })).toBe(true); }); }); ``` - [ ] **Step 2: Run the new desktop test to verify it fails** Run: ```bash pnpm --filter @geo/desktop-client test apps/desktop-client/src/main/adapters/deepseek.test.ts ``` Expected: - FAIL because `./deepseek` does not exist yet - [ ] **Step 3: Write the failing server regression test** ```go func TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs(t *testing.T) { searchTitle := "搜索网页结果" req := MonitoringTaskResultRequest{ Status: "succeeded", SearchResults: []MonitoringSourceItem{ { URL: "https://example.com/search", Title: &searchTitle, }, }, } _, sourceInputs := buildMonitoringRawPayload("deepseek", req) if len(sourceInputs) != 1 { t.Fatalf("buildMonitoringRawPayload() sourceInputs len = %d, want 1", len(sourceInputs)) } if sourceInputs[0].URL != "https://example.com/search" { t.Fatalf("buildMonitoringRawPayload() source url = %q, want search url", sourceInputs[0].URL) } } ``` - [ ] **Step 4: Run the targeted Go test to verify the new assertion state** Run: ```bash cd server && go test ./internal/tenant/app -run TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs -count=1 ``` Expected: - PASS or FAIL is acceptable here, but the test must compile and pin the intended DeepSeek ingestion behavior before adapter code lands ### Task 2: Implement the DeepSeek Desktop Adapter **Files:** - Create: `apps/desktop-client/src/main/adapters/deepseek.ts` - Modify: `apps/desktop-client/src/main/adapters/index.ts` - Modify: `apps/desktop-client/src/main/runtime-controller.ts` - [ ] **Step 1: Add the adapter skeleton and helper exports** ```ts export const deepseekAdapter: MonitorAdapter = { provider: "deepseek", executionMode: "playwright", async query(context, payload) { // implement after tests are in place }, }; export const __deepseekTestUtils = { extractQuestionText, dedupeSourceItems, classifyDeepseekSources, isObservationComplete, }; ``` - [ ] **Step 2: Implement page bootstrap and observation helpers** ```ts async function ensurePageOnDeepSeekChat(page: PlaywrightPage, signal: AbortSignal): Promise { if (signal.aborted) { throw new Error("adapter_aborted"); } if (!page.url() || !page.url().startsWith("https://chat.deepseek.com/")) { await page.goto("https://chat.deepseek.com/", { waitUntil: "domcontentloaded", timeout: 20_000, }); } await page.waitForLoadState("domcontentloaded", { timeout: 20_000 }).catch(() => undefined); await page.waitForLoadState("networkidle", { timeout: 3_000 }).catch(() => undefined); } ``` - [ ] **Step 3: Implement answer submission, wait loop, and citation extraction** ```ts const result = await waitForDeepSeekAnswer(page, questionText, context.signal); const classified = classifyDeepseekSources({ inlineLinks: result.inlineLinks, sourcePanelLinks: result.sourcePanelLinks, searchPanelLinks: result.searchPanelLinks, }); return { status: "succeeded", summary: "DeepSeek 监控任务执行成功。", payload: { platform: "deepseek", provider_model: result.providerModel ?? "deepseek-chat", provider_request_id: result.providerRequestID, request_id: result.requestID, answer: result.answer, citations: toJsonSources(classified.citations), search_results: toJsonSources(classified.searchResults), raw_response_json: { platform: "deepseek", page_url: result.url, answer: result.answer, citations: toJsonSources(classified.citations), search_results: toJsonSources(classified.searchResults), }, }, }; ``` - [ ] **Step 4: Register the adapter in exports and runtime routing** ```ts export * from "./deepseek"; ``` ```ts import { deepseekAdapter } from "./adapters"; if (platform === deepseekAdapter.provider) { return deepseekAdapter; } ``` - [ ] **Step 5: Run the desktop DeepSeek tests to verify they now pass** Run: ```bash pnpm --filter @geo/desktop-client test apps/desktop-client/src/main/adapters/deepseek.test.ts ``` Expected: - PASS ### Task 3: Verify Integration Boundaries **Files:** - Modify: `apps/desktop-client/src/main/platform-auth-adapters.ts` only if DeepSeek-specific auth/risk strings are discovered during implementation - Modify: `server/internal/tenant/app/monitoring_callback_service_test.go` - [ ] **Step 1: Keep server ingestion behavior explicit for DeepSeek** ```go func TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs(t *testing.T) { // keep DeepSeek aligned with the default non-Kimi ingestion path } ``` - [ ] **Step 2: Run the combined targeted verification suite** Run: ```bash pnpm --filter @geo/desktop-client test apps/desktop-client/src/main/adapters/deepseek.test.ts apps/desktop-client/src/main/platform-auth-adapters.test.ts cd server && go test ./internal/tenant/app -run 'TestBuildMonitoringRawPayload(DeepSeek|Qwen|Kimi)|TestExtractMonitoringInlineCitationsFromRawResponseKimi' -count=1 ``` Expected: - all targeted tests pass - [ ] **Step 3: Run desktop typecheck** Run: ```bash pnpm typecheck:desktop ``` Expected: - PASS with no new type errors from the DeepSeek adapter wiring - [ ] **Step 4: Commit the implementation** ```bash git add apps/desktop-client/src/main/adapters/deepseek.ts \ apps/desktop-client/src/main/adapters/deepseek.test.ts \ apps/desktop-client/src/main/adapters/index.ts \ apps/desktop-client/src/main/runtime-controller.ts \ server/internal/tenant/app/monitoring_callback_service_test.go \ docs/superpowers/plans/2026-04-23-deepseek-monitoring.md git commit -m "feat(desktop): add DeepSeek monitoring adapter" ```