docs(plan): update planning artifacts for adapter and account-pool work
Desktop Client Build / Resolve Build Metadata (push) Successful in 24s
Frontend CI / Frontend (push) Successful in 2m56s
Backend CI / Backend (push) Failing after 8m6s
Desktop Client Build / Build Desktop Client (push) Successful in 24m4s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 50s

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 14:17:36 +08:00
parent 06dafa88f8
commit a02ed4e79f
3 changed files with 233 additions and 1 deletions
+66
View File
@@ -292,3 +292,69 @@
- The Electron main bundle owns Playwright adapters. Playwright serializes `page.evaluate` callbacks with `Function.prototype.toString()` and runs them in the target web page. When `javascript-obfuscator` rewrites main-process evaluate callbacks, string-array helper identifiers remain in the function body but are not defined in the browser page context. - The Electron main bundle owns Playwright adapters. Playwright serializes `page.evaluate` callbacks with `Function.prototype.toString()` and runs them in the target web page. When `javascript-obfuscator` rewrites main-process evaluate callbacks, string-array helper identifiers remain in the function body but are not defined in the browser page context.
- `electron.vite.config.ts` already disables Vite minification for main for the same reason; the remaining failure path was `scripts/obfuscate.cjs` obfuscating `out/main/*.cjs` after build. - `electron.vite.config.ts` already disables Vite minification for main for the same reason; the remaining failure path was `scripts/obfuscate.cjs` obfuscating `out/main/*.cjs` after build.
- Fix: keep the main process bundle unobfuscated and only obfuscate renderer assets. `pnpm --filter @geo/desktop-client build:obf` now rebuilds main cleanly and obfuscates renderer only. - Fix: keep the main process bundle unobfuscated and only obfuscate renderer assets. `pnpm --filter @geo/desktop-client build:obf` now rebuilds main cleanly and obfuscates renderer only.
## Hunyuan SSE/Search And Markdown - 2026-07-12
- `yuanbao.ts` already captures JSON/event-stream responses and parses current `hunyuan_t1` frames, so the SSE-first change should strengthen the existing path rather than introduce a second protocol implementation.
- The page query currently ensures the deep-think toggle is enabled before submit, but initial inspection found no corresponding pre-submit `ensureToggleEnabledByPattern(webSearchPattern, ...)` call.
- Final answer selection currently compares parsed SSE text and DOM text by a quality score; this violates the requested strict ordering because a longer DOM snapshot may replace a valid API answer.
- The Markdown display issue can originate either in Yuanbao answer normalization or in `TrackingQuestionDetailView` preprocessing; the exact boundary remains to be reproduced before editing.
- A web-search enablement function already exists and is called, including a Tools-menu fallback, but its success is inferred from text/color state and has not been verified against the outgoing chat request.
- The response capture currently accepts every Yuanbao-host `/api/` response and merges all parsed candidates, rather than binding the result to the current POST chat SSE response.
- `selectBestAnswerText` can replace a valid parsed API answer with a higher-scoring DOM answer, so the current implementation is not API-first.
- Both `sanitizeAnswerCandidate` in the desktop adapter and `sanitizeAnswerBody` in admin-web split on repeated newlines and rejoin with single newlines. That destroys Markdown paragraph boundaries before `markdown-it` renders the answer.
- Ranked diagnosis hypotheses: (1) DOM quality scoring overrides valid SSE; (2) broad capture mixes unrelated API responses; (3) search toggle detection reports a false positive; (4) admin sanitization removes Markdown block spacing; (5) stream-fragment merging itself damages Markdown.
- `yuanbaoQueryInPage` already calls `ensureWebSearchEnabled`, so the implementation gap is verification and fail-closed search enablement rather than adding the call from scratch.
- `selectBestAnswerText` is exported through `__yuanbaoTestUtils`, providing a direct deterministic seam for an API-priority regression test.
- The API-priority regression test fails exactly as predicted: current selection returns the longer DOM/tooling text instead of valid SSE Markdown.
- Recent real run `question_monitor_runs.id=1034` contains three concatenated layers: model reasoning, a corrupted partial answer, then the complete Markdown answer. Its raw metadata reports `candidate_count=782`, confirming broad capture/recursive merge contamination rather than a renderer-only defect.
- The same real run reports `web_search_enabled=null` even though search sources were collected, so search execution occurred but UI state verification did not establish a reliable enabled state.
- The stored answer already contains malformed/duplicated Markdown before admin-web renders it; admin newline normalization can worsen layout, but the primary corruption starts in desktop SSE aggregation.
## 2026-07-12 Desktop Hunyuan/Yuanbao CDP findings
- Electron CDP at `127.0.0.1:9339` exposes two authenticated Yuanbao chat pages.
- The current Yuanbao composer is a Quill `.ql-editor`; web search is not a persistent switch. It is an item inside the `工具` dropdown.
- A CDP screenshot showed only the `深度思考` chip active at the composer. `联网搜索` was present only in the open tools menu, so web search was not enabled for that page.
- `yuanbaoQueryInPage.ensureWebSearchEnabled()` currently falls back to `menuState ?? true` after clicking the menu item. The live menu item has no `aria-checked`, `aria-selected`, `aria-pressed`, or equivalent state attributes, so this path can report `true` without verifying that the active `联网搜索` chip actually appeared.
- Next CDP probe: click the live `联网搜索` item, verify the active composer chip, then submit a short query while recording only the request method/URL, safe request-body shape, response content type, and SSE event shape.
- CDP verified successful activation by the appearance of a visible `<span class="application-blot-ai-atom">联网搜索</span>` in the composer.
- The authoritative response is `POST https://yuanbao.tencent.com/api/chat/{conversationId}` with `content-type: text/event-stream`.
- The live search-enabled chat request contains `supportFunctions: ["openAutoSearchSwitch", "autoInternetSearch"]`; its model extension also contains `internetSearch` state.
- One submission also emitted unrelated POSTs such as `updateModel`, `promptSug`, `hintV2`, and red-dot APIs. This confirms the current broad `/api/` capture admits unrelated payloads.
- The captured official SSE contained 124 frames: 13 top-level `text` answer deltas, 105 `deepSearch` process frames, three `step` frames, one `searchGuid`, one `hint_v2_tip`, and a final `meta` frame with `endConv`. Recursively treating nested `deepSearch.contents` text as answer is the direct answer-contamination mechanism.
- Implementation boundary selected: bind capture to the current question's POST chat SSE, compose answer only from top-level `text` frames, keep search/process frames source-only, prefer any usable SSE answer over DOM, and expose `response_source` diagnostics.
- A live invocation of the updated `yuanbaoAdapter.query()` against the Electron CDP page passed in about 10 seconds. It asserted `status=succeeded`, `response_source=api_sse`, `web_search_enabled=true`, `capture_count=1`, and a non-empty API answer.
- The current request-body equality filter was therefore validated against the real Yuanbao payload, not only a fixture: live `prompt` matches the submitted monitoring question and the official stream is retained.
## 2026-07-12 AI platform multi-account request
- Desired behavior: one AI platform can bind multiple model accounts on the same desktop client.
- Monitoring should try another healthy account from the same platform when the selected account triggers risk control/challenge or returns no usable result.
- The current AI-platform screen presents one platform card with a single `当前账号`, so multi-account visibility and add-account controls are missing even if storage supports multiple rows.
- Existing account rows and session partitions must remain intact; failover should happen within the same platform only and must not duplicate final task completion.
- Server storage already supports multiple accounts: the active uniqueness key is `(workspace_id, client_id, platform_id, platform_uid)`, and desktop account list/upsert APIs are client-scoped.
- `buildRuntimeAccountDedupeKey()` currently collapses every AI account to `ai-platform:{platform}`. The next sync then calls `cleanupDuplicateDesktopAccounts()`, tombstoning valid extra accounts and deleting their local sessions.
- `noteRuntimeAccountBound()` also matches any existing AI account with the same platform, replaces it in memory, clears its health/session, and deletes its server row.
- `monitorPlatformBlockedReason()` examines only the first current-client account. A challenged Doubao account therefore blocks the whole Doubao platform even when another account is active.
- Server monitor tasks are completed once after local execution. The desktop can attempt several same-platform sessions inside one leased task and submit only the final result; no server schema change is required for failover.
- Adapter results already feed `reportAccountFailure()`. Doubao challenge/risk-control responses become `challenge_required/risk_control`, enqueue a server health report, create a runtime activity warning, and surface through the existing Home/tray issue indicators.
- Failover policy selected: skip blocked/expired/revoked/cooling accounts; try the server-preferred healthy account first, then least-recently-used same-platform accounts; cool risk/challenge/auth failures for 30 minutes, empty/unknown results briefly, and generic account-specific failures for a shorter interval.
- The redesigned AI page follows the existing media-account management structure: account-level statistics, platform selector cards with counts/add actions, filters, and a per-account table.
- Electron CDP visual verification at 1420x960 found no horizontal overflow or clipped controls. The live page showed three authorized AI accounts as separate rows and six model-platform selector cards.
- Per-account challenge/risk text is rendered inside the affected account identity cell, while the status tag remains account-specific; healthy accounts from the same platform remain visible and usable.
- Server monitor generation remains platform-scoped when multiple accounts are bound: `uniqueMonitorDesktopTaskPlatformIDs()` collapses duplicate platform specs, while `selectMonitorDesktopTaskTargetsWithOptions()` groups every account candidate by platform and returns at most one target for each platform. Extra same-platform accounts therefore form a desktop execution pool without multiplying questions or final task submissions.
## 2026-07-12 AI/media account UI alignment
- The media account page uses one consistent visual hierarchy: hero statistics, large platform authorization cards, and a bordered account-list panel with integrated filters.
- The first AI multi-account implementation diverged by introducing compact selector cards and a separately styled table. Functionality matched, but card size, radius, spacing, status treatment, and table shell did not match Media Account Management.
- Alignment boundary: reuse the media page's card and table composition while retaining AI-specific multi-account counts, add-account behavior, session/probe fields, and account-level risk-control warnings.
- Electron CDP comparison at 1420x960 measured identical first-card geometry on both pages: 327x157, 20px radius, and 24px padding. Both account-list shells measured 1021px wide with 20px radius and hidden overflow.
- At 1024x800, AI platform cards form a stable two-column grid, the account-list toolbar wraps to 116px high, and `body.scrollWidth === body.clientWidth === 1024`; no horizontal page overflow occurs.
- The removed global “添加 AI 账号” action is absent from the live DOM. New accounts remain available through each platform card's “新增账号” or “绑定账号” action, matching Media Account Management.
## 2026-07-12 Admin AI account management extraction
- The embedded “AI 平台状态” section lives in `TrackingView.vue` and queries all tenant desktop accounts, but groups them by platform and exposes only `matchedAccounts[0]`; this hides additional same-platform accounts.
- `/api/tenant/accounts` already returns account-level client, health, runtime authorization, verification, heartbeat, deletion, and session data. No new storage schema or list endpoint is required.
- `tenantAccountsApi.remove(id)` and `requestDelete(id)` already support the same node-scoped unbind lifecycle used by Media Account Management.
- The new management page should list only real bound AI accounts, one card per account. Platform coverage and health totals can preserve the former overview without rendering fake account cards for unbound platforms.
+88
View File
@@ -893,3 +893,91 @@
- `pnpm --filter @geo/desktop-client typecheck` - `pnpm --filter @geo/desktop-client typecheck`
- `pnpm --filter @geo/desktop-client build` - `pnpm --filter @geo/desktop-client build`
- `git diff --check` - `git diff --check`
## 2026-07-12T12:15:00+08:00 - Hunyuan SSE/search and Markdown diagnosis
- Started Phase 50 for the requested Hunyuan collection repair.
- Locked the desired execution order to official API SSE first, page RPA only when SSE is missing or unusable.
- Included explicit web-search enablement and end-to-end Markdown display repair in scope.
- Added an API-priority regression test at the final Yuanbao answer-selection boundary.
- Confirmed the test fails because the current quality score selects a longer DOM/tooling string over valid SSE Markdown.
## 2026-07-12T00:00:00+08:00 - Desktop Hunyuan/Yuanbao CDP diagnosis resumed
- Confirmed `npx` and `playwright-core` are available and connected Playwright to the running Electron CDP endpoint on port 9339.
- Located two authenticated Yuanbao chat pages and inspected the live composer/tool controls.
- Reproduced a web-search verification defect: the page had only `深度思考` active while `联网搜索` remained an unselected tools-menu item, but the adapter can optimistically return enabled when the menu item exposes no ARIA state.
- Captured `apps/desktop-client/output/playwright/yuanbao-tools.png` as local visual evidence.
- Existing API-vs-DOM priority regression test remains red pending the implementation fix.
- Submitted a short search-enabled query through the real desktop Yuanbao page over CDP and captured the safe request/response shape.
- Verified the official chat SSE endpoint, real search request flags, final `meta.endConv`, and the large ratio of `deepSearch` process frames to answer `text` frames.
- Added regression coverage for strict API priority, page fallback, current-chat SSE filtering, Markdown blank-line preservation, and exclusion of nested deep-search text from the final answer.
- Initial live-test attempt selected a stale hard-coded conversation ID and failed before adapter execution. The selector was corrected to any current Yuanbao chat page.
- One CDP probe called `browser.close()` and stopped the attached Electron Chromium. The existing `electron-vite` development process restarted Electron automatically; subsequent probes exit without sending a remote close command.
- Opened the bound Yuanbao account again through the desktop AI-platform card and ran the actual updated adapter through CDP.
- Live adapter verification passed: API SSE source selected, web search verified active, exactly one current chat SSE captured, and answer non-empty.
- Final verification passed:
- `pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/yuanbao.test.ts` (16/16)
- `pnpm --filter @geo/desktop-client typecheck`
- `pnpm --filter admin-web typecheck`
- `pnpm --filter @geo/desktop-client build`
- `pnpm --filter admin-web build` (existing large-chunk warning only)
- targeted Prettier formatting and `git diff --check`
## 2026-07-12T13:30:00+08:00 - AI platform multi-account support started
- Added Phases 53-56 for storage/runtime audit, binding/UI changes, same-platform monitor failover, and verification.
- Initial hypothesis: server uniqueness already permits multiple platform UIDs per desktop client, while desktop projection/binding and monitor task targeting still enforce effective single-account behavior.
- Phase 53 audit completed. No server migration is needed; implementation is scoped to desktop dedupe/binding, platform-blocking, local account-pool failover, diagnostics, and the AI account-management UI.
- Added a tested monitor-account pool with preferred-first/LRU ordering, blocked/cooldown filtering, failover classification, and reason-specific cooldowns.
- Removed desktop AI platform-only deduplication and same-platform replacement cleanup; distinct platform UIDs now retain independent server rows and local session partitions.
- Added same-platform monitor attempts inside one leased task, final-result-only submission diagnostics, account health reporting, runtime switch activities, and per-account risk-control alerts.
- Rebuilt AI Platform Management as a media-account-style multi-account page and verified its desktop layout over Electron CDP.
## 2026-07-12T14:20:00+08:00 - AI platform multi-account server invariant
- Confirmed server monitor targeting remains one target per platform even when several same-platform accounts are bound.
- `uniqueMonitorDesktopTaskPlatformIDs()` de-duplicates requested platforms before account lookup, and `selectMonitorDesktopTaskTargetsWithOptions()` selects a single best target from each platform's account candidates.
- Added focused assertions covering duplicate platform specs and the one-target result for multiple same-platform accounts.
## 2026-07-12T14:35:00+08:00 - AI platform multi-account verification complete
- Focused server targeting tests passed, including duplicate platform de-duplication and one-target selection for multiple same-platform accounts.
- Focused desktop account-pool, account-health, scheduler, and Doubao tests passed: 3 files / 38 tests.
- Full desktop verification passed: 30 files / 206 tests, Vue typecheck, production Electron build, targeted Prettier check, and `git diff --check`.
- Electron CDP UI verification remains valid at 1420x960: six platform selectors, three separately managed AI accounts, add-account choices for all six platforms, working Doubao filtering, and no overflow or overlap.
- Residual live-test limitation: the UI was exercised with real bound accounts, but automatic failover could not be triggered against two simultaneously usable Doubao accounts in this environment. The selection, cooldown, error classification, and final-result-only behavior are covered by deterministic tests.
## 2026-07-12T14:50:00+08:00 - AI/media account UI alignment started
- Added Phases 57-58 after the user clarified that AI Platform Management must visually match Media Account Management, not only expose equivalent account-management functions.
- Audited both live Vue views and identified the mismatch: AI used compact selector cards plus a custom account table, while media uses larger authorization cards and a shared hero-card table shell.
- Locked the change to renderer composition and styles; multi-account runtime behavior and account actions remain unchanged.
- First Electron CDP comparison command failed because `playwright-core` is workspace-local and cannot be resolved from the repository root. The retry runs from `apps/desktop-client`, where the dependency is installed.
## 2026-07-12T15:15:00+08:00 - AI/media account UI alignment implemented
- Removed the global “添加 AI 账号” dropdown; platform cards are now the only binding entry, matching the media account workflow.
- Replaced compact AI selectors with the media page's large card geometry, status dot, bound/unbound treatment, and “新增账号 / 绑定账号” actions.
- Rebuilt the AI authorized-account list with the same hero-card shell, toolbar, filters, transparent table header, row spacing, avatar/platform cells, status tags, and three-action layout used by Media Account Management.
- Retained AI-specific multi-account counts, account health/session data, reauthorization/probe behavior, and row-level risk-control warnings.
- Electron CDP desktop and narrow-width comparisons passed with matching geometry and no page overflow.
## 2026-07-12T15:25:00+08:00 - Missing-citation failover diagnosis
- Confirmed the existing account-pool classifier returned success as soon as any non-empty answer existed, regardless of empty `citations` and `search_results` arrays.
- Added a red regression test proving a source-less successful answer previously returned no failover reason.
- Added `missing_citations` as a current-task failover reason. It has zero account cooldown because missing sources can be question-specific and should not mark the account unhealthy for later tasks.
## 2026-07-12T15:35:00+08:00 - AI account UI and missing-citation failover verified
- Full desktop tests passed: 30 files / 206 tests.
- Desktop Vue typecheck and production Electron build passed.
- Targeted Prettier checks and `git diff --check` passed.
- CDP confirmed AI and media platform cards have matching geometry at 1420x960; the AI page also passed 1024x800 responsive inspection with no page overflow.
- The live DOM contains only the hero actions “一键检测账号” and “刷新状态”; the removed global “添加 AI 账号” button is absent.
- Successful answers now require at least one item in `citations` or `search_results` to stop same-platform account failover. When every account lacks sources, the existing loop preserves and submits the final non-empty answer with attempt diagnostics.
## 2026-07-12T15:50:00+08:00 - Admin AI account management extraction started
- Added Phases 60-62 for a new tracking-side “AI 账号管理” menu after “外部文章标记”.
- Confirmed the existing tracking status panel collapses multiple accounts to the first account per platform even though the API returns every account.
- Confirmed account removal and delete-request APIs can be reused from Media Account Management; implementation needs no server migration.
+79 -1
View File
@@ -4,7 +4,7 @@
Continue the desktop AI monitoring implementation by first adding a client-side local scheduler with durable queueing and adaptive execution policy, then adding a reusable hidden Playwright CDP execution layer that attaches to Electron Chromium and reuses existing account partitions. Continue the desktop AI monitoring implementation by first adding a client-side local scheduler with durable queueing and adaptive execution policy, then adding a reusable hidden Playwright CDP execution layer that attaches to Electron Chromium and reuses existing account partitions.
## Current Phase ## Current Phase
Phase 47 Phase 60
## Phases ## Phases
### Phase 1: Progress Verification ### Phase 1: Progress Verification
@@ -341,6 +341,84 @@ Phase 47
- [ ] Deliver the completed scope to the user - [ ] Deliver the completed scope to the user
- **Status:** pending - **Status:** pending
### Phase 50: Hunyuan Collection And Markdown Diagnosis
- [x] Build deterministic tests for API-SSE-primary and page-RPA-fallback behavior
- [x] Use Electron CDP to inspect the real Hunyuan search toggle and streaming endpoint
- [x] Reproduce the malformed Markdown display at the storage/rendering boundary
- **Status:** complete
### Phase 51: Hunyuan SSE/Search And Markdown Implementation
- [x] Enable web search before submitting Hunyuan monitoring questions
- [x] Parse official API SSE data first and fall back to page RPA only when unavailable
- [x] Normalize collected Markdown without corrupting citations or source extraction
- [x] Fix the admin answer renderer if display corruption is frontend-side
- **Status:** complete
### Phase 52: Hunyuan End-To-End Verification
- [x] Run targeted adapter and Markdown regression tests
- [x] Verify the live Hunyuan flow over Electron CDP
- [x] Run desktop/admin typecheck, tests, build, and diff cleanup
- **Status:** complete
### Phase 53: Multi-Account Model And Runtime Audit
- [x] Confirm whether server account storage already permits multiple accounts per desktop client and AI platform
- [x] Trace desktop binding, account projection, task leasing, and monitor execution account selection
- [x] Define failover-eligible versus terminal task/account errors and cooldown behavior
- **Status:** complete
### Phase 54: Multi-Account Binding And UI
- [x] Preserve multiple bound AI accounts for the same platform instead of replacing the current account
- [x] Render per-platform account lists with add, open, refresh, and unbind controls
- [x] Keep existing single-account installations backward compatible
- **Status:** complete
### Phase 55: Same-Platform Monitor Failover
- [x] Select a healthy local account pool for each monitoring platform
- [x] Retry the same question with another account on risk control, challenge, empty/unknown, or recoverable collection failure
- [x] Cool down failed accounts and record attempted/selected account diagnostics without duplicate server completion
- **Status:** complete
### Phase 56: Multi-Account Verification
- [x] Add focused server/desktop tests for multi-account binding and selection
- [x] Run desktop/server typecheck, tests, build, and diff cleanup
- [x] Verify account-pool UI and failover state through the running desktop client
- **Status:** complete
### Phase 57: AI And Media Account UI Alignment
- [x] Replace the AI-only compact platform selectors with the media-account card structure
- [x] Match media-account spacing, card dimensions, status treatment, buttons, filters, and table shell
- [x] Preserve AI multi-account actions and per-account risk-control warnings
- **Status:** complete
### Phase 58: AI Account UI Visual Verification
- [x] Run desktop typecheck, tests, build, formatting, and diff checks
- [x] Inspect the live AI page over Electron CDP at desktop and narrow viewports
- [x] Remove temporary screenshots and record the final visual result
- **Status:** complete
### Phase 59: Missing-Citation Account Failover
- [x] Add regression coverage for answers with and without structured citation sources
- [x] Rotate to another same-platform account when a successful answer has no citations or search results
- [x] Keep the last non-empty answer when every bound account lacks sources and avoid account-health cooldown
- **Status:** complete
### Phase 60: Admin AI Account Management Extraction
- [ ] Add an AI Account Management route and tracking menu item after External Article Marks
- [ ] Build an account-level multi-account page from tenant desktop account data
- [ ] Match the bound media account card, filter, status, node, and unbind interactions
- **Status:** in_progress
### Phase 61: Tracking Dashboard Cleanup
- [ ] Remove the embedded AI platform status query, projection, template, and styles from Data Details
- [ ] Preserve unrelated platform analytics and collection controls
- **Status:** pending
### Phase 62: Admin AI Account Verification
- [ ] Run admin typecheck, build, formatting, and diff checks
- [ ] Verify navigation order, multi-account cards, filters, and responsive layout in a real browser
- [ ] Remove temporary visual artifacts and record results
- **Status:** pending
## Key Questions ## Key Questions
1. How should the desktop client defer and locally optimize monitor-task execution without violating one-task-per-platform serialism? 1. How should the desktop client defer and locally optimize monitor-task execution without violating one-task-per-platform serialism?
2. What is the smallest durable local cache that allows same-day resume while safely dropping stale next-day monitor tasks? 2. What is the smallest durable local cache that allows same-day resume while safely dropping stale next-day monitor tasks?