diff --git a/electron/api/routes/cron.ts b/electron/api/routes/cron.ts index 47e49c4..4798098 100644 --- a/electron/api/routes/cron.ts +++ b/electron/api/routes/cron.ts @@ -7,6 +7,7 @@ import { getOpenClawConfigDir } from '../../utils/paths'; import { resolveAccountIdFromSessionHistory } from '../../utils/session-util'; import { toOpenClawChannelType, toUiChannelType } from '../../utils/channel-alias'; import { resolveAgentIdFromChannel } from '../../utils/agent-config'; +import { stripBusinessResponseGuidance } from '../../../shared/business-guidance'; /** * Find agentId from session history by delivery "to" address. @@ -296,7 +297,7 @@ export function buildCronSessionFallbackMessages(params: { }); const messages: CronSessionFallbackMessage[] = []; - const prompt = params.job?.payload?.message || params.job?.payload?.text || ''; + const prompt = stripBusinessResponseGuidance(params.job?.payload?.message || params.job?.payload?.text || ''); const taskName = params.job?.name?.trim() || params.sessionEntry?.label?.replace(/^Cron:\s*/, '').trim() || ''; @@ -421,7 +422,7 @@ function buildCronUpdatePatch(input: Record): Record { // Bridge gateway and host-side events before any auto-start logic runs, so // renderer subscribers observe the full startup lifecycle. + const handleCronDesktopReminder = createCronDesktopReminderHandler({ + resolveJob: async (jobId): Promise => { + const result = await gatewayManager.rpc('cron.list', { includeDisabled: true }, 3000); + const jobs = (result as { jobs?: CronDesktopReminderJob[] })?.jobs + ?? (Array.isArray(result) ? result as CronDesktopReminderJob[] : []); + return jobs.find((job) => job.id === jobId); + }, + notify: (display) => { + if (!Notification.isSupported()) return; + const desktopNotification = new Notification({ + title: display.title, + body: display.body, + icon: getAppIconPath(), + silent: false, + }); + desktopNotification.on('click', () => { + focusMainWindow(display.route); + }); + desktopNotification.show(); + }, + }); + gatewayManager.on('status', (status: { state: string }) => { hostEventBus.emit('gateway:status', status); if (status.state === 'running' && !isE2EMode) { @@ -441,6 +467,11 @@ async function initialize(): Promise { gatewayManager.on('notification', (notification) => { hostEventBus.emit('gateway:notification', notification); + if (!isE2EMode) { + void handleCronDesktopReminder(notification).catch((error) => { + logger.warn('Failed to show cron desktop reminder:', error); + }); + } }); gatewayManager.on('chat:message', (data) => { diff --git a/electron/utils/cron-desktop-reminder.ts b/electron/utils/cron-desktop-reminder.ts new file mode 100644 index 0000000..b5d96c0 --- /dev/null +++ b/electron/utils/cron-desktop-reminder.ts @@ -0,0 +1,243 @@ +type JsonRecord = Record; + +export type CronDesktopReminderTone = 'success' | 'warning' | 'danger'; + +export interface CronDesktopReminderEvent { + jobId: string; + agentId: string; + sessionKey: string; + runKey: string; + phase: string; + tone: CronDesktopReminderTone; + summary?: string; + error?: string; +} + +export interface CronDesktopReminderJob { + id: string; + name?: string; + state?: { + lastStatus?: string; + lastError?: string; + }; +} + +export interface CronDesktopReminderDisplay { + title: string; + body: string; + tone: CronDesktopReminderTone; + route: string; +} + +export interface CronDesktopReminderHandlerDeps { + resolveJob?: (jobId: string) => Promise; + notify: (display: CronDesktopReminderDisplay, event: CronDesktopReminderEvent) => void; + now?: () => number; + dedupeMs?: number; +} + +interface ParsedCronSessionKey { + agentId: string; + jobId: string; + runSessionId?: string; + fixedSessionKey: string; +} + +function asRecord(value: unknown): JsonRecord | undefined { + return value && typeof value === 'object' ? value as JsonRecord : undefined; +} + +function readString(record: JsonRecord | undefined, key: string): string | undefined { + const value = record?.[key]; + return typeof value === 'string' && value.trim() ? value.trim() : undefined; +} + +function parseCronSessionKey(value: unknown): ParsedCronSessionKey | null { + if (typeof value !== 'string' || !value.startsWith('agent:')) return null; + const parts = value.split(':'); + if (parts.length < 4 || parts[2] !== 'cron') return null; + const agentId = parts[1] || 'main'; + const jobId = parts[3]; + if (!jobId) return null; + const runSessionId = parts.length >= 6 && parts[4] === 'run' && parts[5] ? parts[5] : undefined; + return { + agentId, + jobId, + ...(runSessionId ? { runSessionId } : {}), + fixedSessionKey: `agent:${agentId}:cron:${jobId}`, + }; +} + +function getNestedRecord(record: JsonRecord | undefined, key: string): JsonRecord | undefined { + return asRecord(record?.[key]); +} + +function isTerminalCronPhase(phase: string | undefined): boolean { + if (!phase) return false; + return ['completed', 'done', 'finished', 'failed', 'error'].includes(phase.toLowerCase()); +} + +function normalizeStatus(value: string | undefined): CronDesktopReminderTone | undefined { + const normalized = value?.trim().toLowerCase(); + if (!normalized) return undefined; + if (['error', 'failed', 'failure', 'cancelled', 'timeout'].includes(normalized)) return 'danger'; + if (['warning', 'warn', 'reconciled', 'partial'].includes(normalized)) return 'warning'; + if (['ok', 'success', 'succeeded', 'completed', 'done', 'finished'].includes(normalized)) return 'success'; + return undefined; +} + +function firstText(values: Array): string | undefined { + for (const value of values) { + const text = extractText(value); + if (text) return text; + } + return undefined; +} + +function extractText(value: unknown): string | undefined { + if (typeof value === 'string') return value.trim() || undefined; + if (Array.isArray(value)) { + const text = value.map(extractText).filter(Boolean).join('\n').trim(); + return text || undefined; + } + const record = asRecord(value); + if (!record) return undefined; + return firstText([ + record.content, + record.text, + record.message, + record.summary, + record.error, + ]); +} + +function inferTone(phase: string | undefined, params: JsonRecord | undefined, data: JsonRecord | undefined): CronDesktopReminderTone { + const state = getNestedRecord(params, 'state') ?? getNestedRecord(data, 'state'); + const tone = normalizeStatus(readString(data, 'status')) + ?? normalizeStatus(readString(params, 'status')) + ?? normalizeStatus(readString(data, 'finalStatus')) + ?? normalizeStatus(readString(params, 'finalStatus')) + ?? normalizeStatus(readString(state, 'status')) + ?? normalizeStatus(readString(state, 'lastStatus')) + ?? normalizeStatus(phase); + if (tone) return tone; + if (readString(data, 'error') || readString(params, 'error') || readString(state, 'lastError')) return 'danger'; + return 'success'; +} + +function truncateText(value: string | undefined, maxLength: number): string { + const clean = (value || '').replace(/\s+/g, ' ').trim(); + if (!clean) return ''; + if (clean.length <= maxLength) return clean; + return `${clean.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`; +} + +export function buildCronDesktopReminderEvent(notification: unknown): CronDesktopReminderEvent | null { + const root = asRecord(notification); + if (readString(root, 'method') !== 'agent') return null; + + const params = getNestedRecord(root, 'params'); + const data = getNestedRecord(params, 'data'); + const phase = (readString(data, 'phase') ?? readString(params, 'phase'))?.toLowerCase(); + if (!isTerminalCronPhase(phase)) return null; + + const parsedSession = parseCronSessionKey(readString(params, 'sessionKey') ?? readString(data, 'sessionKey')); + if (!parsedSession) return null; + + const state = getNestedRecord(params, 'state') ?? getNestedRecord(data, 'state'); + const error = firstText([ + readString(data, 'error'), + readString(params, 'error'), + readString(state, 'lastError'), + ]); + const summary = firstText([ + readString(data, 'resolvedSummary'), + readString(params, 'resolvedSummary'), + readString(data, 'summary'), + readString(params, 'summary'), + data?.message, + params?.message, + ]); + const runId = readString(params, 'runId') + ?? readString(data, 'runId') + ?? readString(params, 'sessionId') + ?? readString(data, 'sessionId') + ?? parsedSession.runSessionId + ?? parsedSession.fixedSessionKey; + + return { + jobId: parsedSession.jobId, + agentId: parsedSession.agentId, + sessionKey: parsedSession.fixedSessionKey, + runKey: `${parsedSession.jobId}:${runId}`, + phase: phase || 'completed', + tone: inferTone(phase, params, data), + ...(summary ? { summary: truncateText(summary, 180) } : {}), + ...(error ? { error: truncateText(error, 180) } : {}), + }; +} + +export function buildCronDesktopReminderDisplay( + event: CronDesktopReminderEvent, + job?: CronDesktopReminderJob, +): CronDesktopReminderDisplay { + const jobName = job?.name?.trim() || event.jobId; + const jobTone = normalizeStatus(job?.state?.lastStatus); + const tone = jobTone === 'danger' ? 'danger' : event.tone; + const error = event.error || job?.state?.lastError; + const detail = tone === 'danger' ? error || event.summary : event.summary || error; + + if (tone === 'danger') { + return { + tone, + title: `任务需要处理:${jobName}`, + body: truncateText(detail || '任务运行失败,点击查看执行记录。', 220), + route: `/tasks?task=${encodeURIComponent(event.jobId)}`, + }; + } + + if (tone === 'warning') { + return { + tone, + title: `任务需复核:${jobName}`, + body: truncateText(detail || '任务已完成,但存在需要复核的提示。', 220), + route: `/tasks?task=${encodeURIComponent(event.jobId)}`, + }; + } + + return { + tone, + title: `任务已完成:${jobName}`, + body: truncateText(detail || '结果已生成,点击查看任务会话。', 220), + route: `/tasks?task=${encodeURIComponent(event.jobId)}`, + }; +} + +export function createCronDesktopReminderHandler(deps: CronDesktopReminderHandlerDeps) { + const notifiedRuns = new Map(); + const dedupeMs = deps.dedupeMs ?? 10 * 60 * 1000; + const now = deps.now ?? Date.now; + + return async (notification: unknown): Promise => { + const event = buildCronDesktopReminderEvent(notification); + if (!event) return null; + + const currentTime = now(); + for (const [key, timestamp] of notifiedRuns) { + if (currentTime - timestamp > dedupeMs) { + notifiedRuns.delete(key); + } + } + + const notifiedAt = notifiedRuns.get(event.runKey); + if (notifiedAt != null && currentTime - notifiedAt <= dedupeMs) { + return null; + } + notifiedRuns.set(event.runKey, currentTime); + + const job = await deps.resolveJob?.(event.jobId).catch(() => undefined); + const display = buildCronDesktopReminderDisplay(event, job); + deps.notify(display, event); + return display; + }; +} diff --git a/findings.md b/findings.md new file mode 100644 index 0000000..8bb0368 --- /dev/null +++ b/findings.md @@ -0,0 +1,754 @@ +# Findings + +## 对话 Markdown 与隐藏提示泄漏 + +- `[[YINIAN_BUSINESS_RESPONSE_GUIDANCE]]` 泄漏的根因是业务准则被追加进 `chat.send`/cron 的真实 message 字段,Gateway 持久化后作为 user message 回放。 +- 项目已经有 `resources/context/AGENTS.clawx.md` 承载业务行为准则,因此 direct chat/cron 不应继续把隐藏准则拼进用户消息。 +- 需要双保险:新消息不再追加隐藏准则;旧历史显示、会话标题和乐观消息去重都要剥离隐藏准则。 +- Markdown 表格应作为工作台数据表渲染:可横向滚动、稳定边框、紧凑单元格、表头行有轻微底色但 `th` 本身保持透明,兼容既有浅色表头检查。 +- 执行过程更适合作为无框时间线,而不是和正式业务回答同层级的卡片。 +- 新对话空态不需要重复展示产品名;保留“今天从哪里开始?”即可,能减少首屏噪音并把注意力留给输入区。 + +## 桌面任务提醒与业务回答渲染 + +- Cron run notifications arrive through the existing Gateway `agent` notification path; renderer already uses this path to fold isolated cron run sessions back into the fixed task conversation. +- Desktop-level reminders belong in the Electron main process, not renderer toast, because scheduled runs can finish while the task center is not visible. +- The notification should only fire for terminal cron phases (`completed`, `done`, `finished`, `failed`, `error`) and should dedupe by job/run key so duplicate Gateway terminal events do not produce repeated system popups. +- A click target of `/tasks?task=` is enough for now: it keeps users in the task management surface and lets the page highlight the relevant scheduled task. +- Business answer UI can be layered on top of Markdown without changing message storage. The robust contract is stable labels (`状态:`, `依据:`, `影响:`, `下一步:`, plus high-risk labels such as `执行预览:` and `待确认:`). + +## 聊天页 UI 统一 + +- Chat still carried inherited OpenClaw visual decisions: translucent panel, oversized empty-state type, shadowed composer, rounded-xl cards, black alpha surfaces, and mixed 15px/14px text choices. +- The project design source of truth says 智念助手 should feel like mature B-end operations tooling: system Chinese font stack, compact dashboard scale, navy/slate palette, 8px radius, minimal shadows. +- The safest fix is page-local styling convergence plus one global font declaration, not a new theme or font dependency. +- The visual smoke test captures `test-results/yinian-visual/02-chat.png`, which is useful as a quick regression check after chat UI changes. + +## 设置偏好深色 UI 色彩 + +- Dark-mode Settings Preferences screenshot captured at `test-results/settings-preferences-dark-electron-before.png`. +- The Preferences tab used floating controls directly on a dark page background, so button states relied on low-contrast `white/5` and `white/10` overlays. +- The macOS title bar used a translucent dark surface over the app background, producing a noticeably gray band in dark mode. +- `body` and `.yinian-app-surface` had light gradient backgrounds without an explicit dark-mode CSS fallback, leaving room for shallow routes or overlays to inherit light surfaces. +- After the pass, the dark screenshot at `test-results/settings-preferences-dark-electron-after.png` shows title bar, page, tabs, and the Preferences panel using the same dark palette family with a visible card layer for settings content. +- The old `settings-proxy.spec.ts` legacy E2E still assumes setup lands directly on `main-layout`; that is separate from the dark-mode UI change and conflicts with the current login-first product flow. + +## M1 Verification + +- `pnpm run typecheck` passed. +- `pnpm run test` passed: 89 test files, 572 tests. +- `pnpm run test:e2e` passed: 26 passed, 1 skipped. +- Non-blocking warnings remain: + - Vitest `MaxListenersExceededWarning`. + - Vite dynamic/static import chunk warnings. + - Vite large chunk warning. + - Playwright/Electron `NO_COLOR` ignored because `FORCE_COLOR` is set. + +## E2E Compatibility Decisions + +- Renderer receives `e2e=1` only when Electron is launched with `CLAWX_E2E=1`. +- In E2E mode, legacy ClawX setup flow remains available. +- In E2E mode, `/` maps to Chat so legacy tests remain valid. +- In production mode, `/` maps to Today. +- In E2E mode, default language is forced to English unless a test changes it. +- In E2E mode after setup is complete, gateway store is initialized but gateway is not auto-started. + +## Product Decisions + +- YINIAN production entry is login-first. +- Gateway should start after authenticated workspace context exists. +- Real OTA bundle download and signature verification are not part of M1. +- Skill sync currently means manifest-to-local-registry synchronization. + +## Open Questions + +- Final backend response shape for auth refresh is not yet locked. +- Whether business customer builds should hide legacy ClawX pages entirely or keep them behind an implementation/admin mode is not decided. +- The exact first pilot business workflow for Today page still needs product prioritization. + +## M1 Handoff Inventory + +- Main process additions are intentional and centered on YINIAN IPC/control plane/storage plus gateway start boundary. +- Renderer additions are intentional and centered on login, Today, Skills, sidebar service context, and YINIAN stores. +- Workspace package additions are boundary scaffolds for kernel adapter, skill spec, business skill base, and UI kit. +- Documentation additions now include `docs/M1_HANDOFF.md`. +- Planning files are local project working memory: `task_plan.md`, `findings.md`, `progress.md`. + +## Channel Health Edge Case + +- `channels.status` success and failure timestamps could land in the same millisecond. +- `buildGatewayHealthSummary` treats a channel status failure as newer only when `failureAt > okAt`. +- When both timestamps were equal, a recent timeout could be reported as healthy. +- Fixed by making channel status success/failure timestamps monotonic in `electron/api/routes/channels.ts`. + +## Server Contract v0 + +- Contract document created at `docs/SERVER_CONTRACT_V0.md`. +- HTTP mode uses `YINIAN_API_BASE_URL`; mock remains default when unset. +- M2 token boundary: + - `accessToken` stays memory-only. + - `refreshToken` is persisted only for HTTP session restore. + - `/auth/refresh` rotates access/refresh tokens when restoring persisted HTTP sessions. +- Desktop accepts selected `snake_case` aliases during migration: + - auth token expiry fields + - workspace ids + - current workspace id + - config `server_time`, `notification_channels`, `feature_flags`, `ui_policy` + - skills `skill_id`, `bundle_sha256` +- Normalization now defaults unknown skill categories to `ops-automation` and filters unknown trigger values. +- Server error messages are surfaced from `error.message`. + +## Today M2 Pilot Surface + +- Today now derives operational state from existing M1 data: config entitlements, notification channels, and local skill registry. +- No new backend endpoint was introduced for Phase 3. +- Pending queue currently represents actionable local/client conditions: + - skill sync failed + - skill entitled but not synced + - local skill version differs from server entitlement +- Arrival/departure summary is an operational proxy based on enabled OTA/reporting skills until real PMS/task data exists. +- Skill state labels are now explicit: + - `已开通未同步` + - `已安装` + - `已更新` + - `已是最新` + - `有更新` + - `已禁用` + - `同步失败` +- Empty states are business-facing: + - no pending items + - no opened skills + - no agent actions + +## Skills Manager M2 + +- Skills now explains availability by merging server entitlements with the workspace-scoped local registry. +- Operator-facing states are explicit: + - `已开通未同步` + - `已安装` + - `已更新` + - `已是最新` + - `有更新` + - `已禁用` + - `同步失败` +- The page now surfaces version drift, sync failures, local source, bundle hash, installed time, and last synced time. +- Empty entitlement and empty local registry states are separate, so "not purchased" and "purchased but not synced" do not blur together. +- Sync remains manifest-to-local-registry only; real bundle download and signature verification stay out of scope. + +## Design System Pass + +- YINIAN surfaces now share a small local UI layer instead of repeating one-off card, empty-state, notice, metric, and header styling. +- The product style is intentionally operational rather than marketing-heavy: + - slate workspace background + - navy primary actions + - restrained status colors + - 8px panels + - no decorative gradients or hero illustration +- Sidebar and sidebar service context now read as part of the same desktop shell as Today and Skills. +- The pass did not change backend contracts, storage behavior, gateway start behavior, or skill registry semantics. + +## Pilot Packaging And QA + +- Pilot QA documentation now lives at `docs/PILOT_QA.md`. +- The recommended demo path is mock mode: + - login + - Today review + - Skills review + - sync skills + - switch workspace + - logout +- The full verification gate for pilot readiness is: + - `pnpm run typecheck` + - `pnpm run test` + - `pnpm run build:vite` + - `pnpm run test:e2e` +- Local macOS pilot packaging should start with `pnpm run package:mac:local`. +- No commit has been created; the project is ready for a checkpoint commit when explicitly requested. + +## 智念助手 Brand Foundation + +- Design system source of truth now lives at `design-system/智念助手/MASTER.md`. +- Desktop/product naming now uses `智念助手` for customer-facing surfaces. +- App icon source and generated assets now live under `resources/icons/`. +- Renderer wordmark lives at `src/assets/logo.svg`. +- Electron builder metadata now uses: + - `appId: app.zhinian.assistant` + - `productName: 智念助手` + - desktop shortcut and uninstall display name: `智念助手` +- Technical ClawX/OpenClaw names intentionally remain in lower-level implementation, compatibility tests, inherited i18n, and gateway/provider integration surfaces. + +## UI Customization Phase 2 + +- The YINIAN UI kit now covers common page composition patterns: + - page/header/action composition + - status dots + - section panels + - metric cards + - list items + - info rows + - empty/notice states +- Login, Today, Skills, and sidebar service context now share the same visual grammar. +- No control-plane, gateway, storage, session, or skill registry behavior changed in this phase. +- Focused tests and build are green; full E2E has not been rerun after this UI-only pass. + +## UI Customization Phase 3 + +- Sidebar information architecture now separates: + - 快速使用 + - AI 工作 + - 扩展 +- Customer-facing Chinese inherited surfaces now say 智念助手 in Chat, Settings, and Setup. +- Existing E2E navigation test ids remain intact. +- Navigation E2E passed after the Sidebar grouping change. +- Remaining inherited surfaces still need deeper polish: Channels, Agents, Models, Cron, full Settings, and non-Chinese locales. + +## UI Customization Phase 4 + +- Chat, Settings, Models, Agents, Channels, and Cron no longer use the inherited oversized serif heading treatment. +- Major customer-facing Chinese ClawX copy has been replaced with 智念助手 across zh Chat, Settings, Setup, Agents, Cron, and Channels. +- One technical upstream GitHub reference remains in Settings as an external resource link. +- Focused inherited-page E2E is green after the visual pass. +- Remaining work: screenshot-based visual QA and deeper per-page layout polish, especially dense Settings/Channels/Agents internals and non-Chinese locales. + +## UI Customization Phase 5 + +- Screenshot QA found and fixed a fresh-user routing issue: production renderer sessions with no setup state could land on legacy setup instead of the 智念登录 page. +- The new visual smoke test produces stable 1440x900 baselines for Login, Today, and Skills under `test-results/yinian-visual/`. +- Login, Today, and Skills screenshots are readable and brand-consistent after the route fix, with no obvious overlap or blank-screen regression. +- Remaining visual debt is now mostly in inherited deep surfaces: Settings internals, Channels configuration flows, Agents, Models, Cron, and non-Chinese locales. + +## B-end Repositioning Pass + +- Customer-facing product language should treat the selected tenant as a workspace/business object, not as a hotel. +- Existing `hotelId`, `currentHotelId`, `switchHotel`, and `hotel_id` names are still compatibility internals; renaming them should be a separate contract/storage migration. +- The UI now exposes `switchWorkspace` as a forward-compatible renderer alias. +- Mock data is generic enough for non-hotel B-end demos, but some inherited skill ids such as `ctrip-price-monitor` remain technical placeholders until real customer-specific skill catalogs are introduced. + +## Shell Simplification Pass + +- Product shell should assume one account maps to one B-end service for now. +- Do not expose workspace switching in the customer-facing shell until multi-service account requirements are explicit. +- Current service identity, sync, and logout now live in the sidebar; the main content area should not regain a top sidebar service context. + +## Sidebar IA Pass + +- Production sidebar should expose only the primary product loops: + - `快速使用`: Today and Skills. + - `对话`: New Chat and Chat. + - `设置`: account, service sync, logout, local preferences, and service-managed capability explanations. +- Models, Agents, Channels, Cron, and Dev Console are developer/configuration surfaces and should not appear in normal customer navigation. +- These legacy/configuration routes remain reachable in `e2e=1` compatibility mode so inherited ClawX regression coverage does not break. +- Service-managed capability copy in Settings should explain that model/provider strategy, Agent orchestration, channels, and scheduled tasks are issued by the server for the current account/service. + +## Sidebar UX Pass + +- Dashboard/Today should be accessed by clicking the current service card at the top of the sidebar. +- `快速使用` should contain business tools, not the dashboard: + - Skills + - 定时任务 + - 知识库 +- `定时任务` is a customer-facing task center. Low-level scheduling policy remains service-managed and should not be confused with local developer configuration. +- `知识库` v0 is currently local UI state only; persistence, indexing, vectorization, permissions, and service sync are future work. +- Chat history should stay collapsed by default; hover reveals the session list, while clicking `历史会话` opens the latest session. + +## Task Center Iteration + +- The new product IA is: + - `快速使用`: 应用中心、知识库 + - `任务`: 任务中心 + - `对话`: 新对话、历史会话 +- `/cron` should remain only as a compatibility redirect to `/tasks?tab=scheduled`. +- 快捷任务 remains stored in `yinian:quick-tasks`; the visible management surface now lives in task center instead of capability-pack settings. +- Scheduled tasks keep using the existing Gateway Cron protocol. Desktop stores a local binding snapshot in `yinian:scheduled-task-bindings` so later quick-task edits do not mutate existing schedules. +- Manual and scheduled run history is stored separately in `yinian:task-run-records`. +- The same prompt composition rule is shared by chat, manual run, and scheduled tasks: `使用{能力包名称} skill ...`. +- Execution graph tests must include `activeRunSessionKey`; this supports the product rule that only the currently running conversation shows an active task state. + +## Task Center Separation Follow-up + +- Product decision: Task Center and quick capabilities are separate concepts. Task Center should not expose a quick-capability tab or quick-capability run/manage surface. +- Scheduled tasks should be authored as standalone content. The scheduled-task dialog should not offer quick-capability template selection. +- `@` capability insertion remains the bridge for invoking a capability pack from scheduled task content. +- The original `@` mention implementation reset active selection on keyup after ArrowUp/ArrowDown, making keyboard navigation appear broken. Ignoring navigation keyup events fixes the selection movement. +- Custom execution time is friendlier as structured repeat controls: + - daily at a time + - weekly on weekday/time + - monthly on day/time + - every N minutes + - advanced Cron fallback for unsupported rules + +## Task Center Pinning Follow-up + +- Product decision: the customer-facing scheduled-task surface should be called “任务中心” / “Task Center”. +- Time-related controls and labels should remain as-is; the rename applies to the product surface and task lifecycle copy. +- Pinned sidebar triggers can be implemented as local cron job id references because the existing Gateway cron API already supports immediate triggering with `/api/cron/trigger`. +- Sidebar should fetch cron jobs when pinned ids exist and the Gateway is ready, then render only pinned jobs that still exist. +- Sidebar-triggered runs should add a task run record so the action is visible in execution history. + +## Task Center Density And Sidebar Structure + +- ui-ux-pro-max design-system search recommended a Data-Dense Dashboard pattern for B2B operations: compact tables/lists, row hover states, minimal padding, and professional navy/blue treatment. +- The Task Center card grid repeats metadata and consumes too much vertical space; a list/table row can preserve all actions while showing more tasks per viewport. +- The existing time settings live inside `ScheduledTaskDialog`; the density pass should avoid modifying that dialog's execution-time controls. +- Sidebar pinned triggers should be contained in a bounded scrollable subsection so many pinned tasks do not push primary navigation or chat controls out of reach. + +## 快速视频创作内置 Web 英文覆盖 + +- 桌面宿主 `src/pages/NianxxPlay/index.tsx` 已经使用 `appCenter` i18n,当前漏出的中文主要来自内置 Next 应用 `/Users/inmanx/Documents/NianxxPlay`。 +- NianxxPlay 默认由 `scripts/prepare-nianxx-play-bundle.mjs` 从 `/Users/inmanx/Documents/NianxxPlay` 构建并复制到桌面 `build/apps/nianxx-play`,因此应优先改源应用再重建 bundle。 +- 内置应用中明显硬编码中文位置包括: + - `src/app/layout.tsx` 顶部品牌、导航、积分 title 和 metadata。 + - `src/components/studio-workspace.tsx` 创作台标签、按钮、placeholder、素材/分镜文案。 + - `src/app/projects/page.tsx` 历史项目、积分、表格和空状态。 + - `src/app/planning/page.tsx` 与规划展示组件。 + - `content/seedance-starter/*.json` 与 `content/studio-presets/*.json` 模板/素材内容。 +- 适合本次快速修复的路径是桌面 URL 传入 `zhinianLang`,内置应用保存该语言,并在英文状态下对可见文本和常用属性做覆盖翻译。 +- 实际页面抽检必须使用新启动的 NianxxPlay 进程;已有 `3000` 端口进程可能仍加载旧 bundle/source。 +- 新构建在 `http://127.0.0.1:3010/studio?zhinianEmbed=1&zhinianLang=en` 抽检通过:主要首屏 UI、实时提示词、素材 token、积分/缺少等内容均显示英文,连续中文匹配为空。 +- `/planning?zhinianEmbed=1&zhinianLang=en` 抽检也没有连续中文匹配;`/projects` 剩余中文主要来自历史项目/用户内容,系统生成前缀“宣传片创作台”已做展示翻译。 +- 刷新 bundle 后需要停止旧的 `127.0.0.1:3000` 子进程,否则它会继续使用内存中的旧 Next 代码;已停止旧进程,等待桌面宿主下次打开时重新拉起。 +- Implemented the density pass with row-level icon actions and compact header metrics; the task dialog's time controls were not modified. +- Sidebar navigation now has a scrollable grouped area, with pinned quick triggers visually separated and height-limited. + +## Immediate History Session Visibility + +- Sidebar history is derived from local `sessions`, `sessionLabels`, and `sessionLastActivity`; waiting for `sessions.list` can lag behind newly started Gateway runs. +- Blank new desktop sessions need local `updatedAt` for sorting but should not receive `sessionLastActivity`, otherwise `cleanupEmptySession` would keep unused blanks forever. +- Task Center manual/sidebar triggers can name the optimistic cron session from the local cron job record before the backend refresh returns. +- Background scheduled runs can arrive through Gateway started/completed notifications; those notifications should insert a visible session row even when the session is not current. + +## Task Execution Conversation Follow-up + +- Product decision: each Task Center task should own one stable conversation keyed by `agent:main:cron:`. +- User-triggered task execution should move the customer directly into that task conversation so they can follow up without hunting through history. +- Background history visibility and fixed-session routing are complementary: the former makes sessions appear, the latter moves the current workspace when the user explicitly triggers execution. + +## Task Conversation Naming + +- Task conversations should be named by the Task Center task name, not by the fixed technical session key or by the first execution prompt. +- Generic first-message session labeling remains correct for normal desktop chat sessions, but cron task sessions need to be excluded from that behavior. +- For background automation events, the best local source for the display name is the current cron store job list. + +## Task History Merge Reliability + +- Root cause: task conversation loading treated Gateway `chat.history` and cron run-log fallback as mutually exclusive; fallback only ran when Gateway returned zero messages. +- When Gateway returned only the current execution prompt, the UI replaced previous task history with that partial result. +- Correct behavior is to merge cron fallback history with Gateway history for cron sessions, then dedupe and sort by timestamp. +- Failed cron fallback entries must be assistant-role messages; system-role fallback entries are intentionally filtered from the chat UI. + +## Version 3.5 Readiness: Application Center + +- 应用中心 is now a distinct product concept from 能力包: + - 应用中心 opens fixed product applications. + - 能力包 expands what the agent can do. + - 快捷任务 is a chat-composer shortcut for invoking one selected 能力包. +- Current app-center implementation is intentionally local/static: + - route: `/app-center` + - built-in registry: `src/stores/app-center.ts` + - item type boundary: `src/types/app-center.ts` + - single focused large-app entry: `/app-center/nianxx-play` +- Small demo applications have been removed from the customer-facing app center so 3.5 can focus on NianxxPlay. +- Large-app integration needs strong boundaries: + - app host contract + - app lifecycle and close semantics + - permission/data dependency declaration + - state restore rules + - native vs embedded-web decision + - app-specific loading/error/empty states +- Current webview-type app items open externally; this is not enough for a true embedded web application. A 3.5 embedded-web app should use a deliberate safe-host strategy rather than simply opening `window.electron.openExternal`. +- Dev port changed from `5173` to `5188` so 智念助手 can run alongside other local Vite projects without fighting for the default port. + +## NianxxPlay Large-App Assessment 2026-05-05 + +- Local project found at `/Users/inmanx/Documents/NianxxPlay`. +- It is not a git repository locally, so integration work should either: + - treat it as a local external app source for now, or + - move/copy it into a managed repo/package when 3.5 implementation begins. +- Product identity: + - name: `NianxxPlay智念视频助手` + - positioning: Seedance 2.0 AI video generation workspace + - target user: ordinary users and local merchants +- Main product flow: + - choose a creation mode + - select a reference template + - upload required image/video/audio assets + - assemble and optionally edit prompt text + - submit Seedance generation task + - track project/result history +- Current creation modes: + - 宣传片制作 + - 创意复刻 +- Technical shape: + - Next.js 15 App Router. + - Server-side API routes under `src/app/api`. + - Local MVP data layer in `src/lib/local-db.ts`, persisted to `.data/app-state.json`. + - Object storage through Aliyun OSS. + - Seedance task creation/query through Ark REST API. + - Startup content and templates under `content/seedance-starter` and `public/seedance-starter-assets`. +- Verification: + - `npm test` passed: 3 test files, 11 tests. + - `npm run build` passed. + - Build still emits the known `ali-oss` / `any-promise/register.js` dynamic dependency warning. +- Integration recommendation: + - Use NianxxPlay as the first 3.5 large-app candidate. + - Prefer embedded-web hosting for the first integration, because the app owns a full Next.js route/API/data domain. + - Do not merge its pages directly into 智念助手 as native React routes in the first pass; that would expand the blast radius into routing, CSS, API, storage, billing, and asset handling all at once. + - The 3.5 host should first support a local dev URL such as `http://localhost:3000`, then later decide whether production packages the Next app, serves it from the customer service backend, or ships it as a static/server bundle. + +## Bundled NianxxPlay Runtime Findings 2026-05-05 + +- User requirement clarified: NianxxPlay should accompany the desktop app and be installed/updated/launched by 智念助手 on the customer's computer. +- This changes the preferred engineering strategy: + - NianxxPlay should not be merely an external URL. + - NianxxPlay should not require customer-installed Node.js/npm/pnpm. + - 智念助手 main process should own the NianxxPlay process lifecycle. +- Existing 智念助手 precedent: + - OpenClaw is bundled into `build/openclaw`. + - `electron-builder.yml` ships it through `extraResources`. + - first-run initialization copies it into a managed runtime under `~/.openclaw/runtime/openclaw`. + - Electron main starts/stops/repairs the runtime process. + - NianxxPlay should reuse this pattern instead of creating a separate installer. +- NianxxPlay current size profile: + - project root: about 1.0GB + - `node_modules`: about 482MB + - `.next`: about 165MB, with most of that from `.next/cache` + - `public`: about 358MB + - `public/seedance-starter-assets`: about 343MB + - `public/uploads`: about 6MB, should not be bundled as seed data unless intentionally included +- Packaging implication: + - A production bundle must exclude `node_modules`, `.next/cache`, local uploads, and `.env.local`. + - Next.js `output: 'standalone'` is the likely packaging baseline. + - Static assets and curated starter media still need explicit inclusion and size control. +- Runtime-data issue: + - NianxxPlay currently uses `process.cwd()` for `.data/app-state.json`. + - upload fallback writes to `process.cwd()/public/uploads`. + - A packaged app must move both to a user-writable app-managed data directory. +- Security issue: + - `.env.local` contains real service credentials. + - That file must not be included in the installer. + - Runtime secrets should come from desktop secure storage, server-issued configuration, or environment variables injected by the main process at launch. +- Service-managed product boundary: + - NianxxPlay accounts, permissions, service keys, quota, and billing should be controlled by the server. + - Desktop should only receive scoped runtime context or signed/temporary access when needed. + - Desktop UI should not expose raw credentials or independent billing controls for the app. +- Port issue: + - NianxxPlay currently assumes `http://localhost:3000`. + - A desktop-managed runtime should allocate/probe a local loopback port and inject `PORT`, `HOSTNAME`, and public base URL env vars. +- Product/runtime boundary: + - NianxxPlay local fallback uploads are only useful for UI preview. + - Real Seedance generation requires public URLs, so OSS configuration remains required for production generation. + +## Enterprise Space Service Integration + +- CloudClaw repository URL checked: `https://git.nianxx.cn/brother7/CloudClaw`. +- Anonymous git access failed with: `fatal: could not read Username for 'https://git.nianxx.cn': Device not configured`. +- Browser access did not expose repository contents from this workspace. +- Current desktop HTTP assumptions: + - Auth base URL defaults to `https://onefeel.brother7.cn/ingress`. + - Password login uses `POST /auth/oauth2/token`. + - Login response includes OAuth fields plus `user_info`. + - Desktop synthesizes a single service workspace from `user_info.tenantId`. + - Config currently tries `/config/sync?hotel_id=...` and falls back to a local empty config if missing. + - App/skill sync currently tries `/skills/manifest?hotel_id=...`. +- Backend confirmation needed: + - Enterprise/service-space detail endpoint. + - Opened applications/skills list endpoint. + - Skill package manifest endpoint and field names. + - Whether app enablement is returned as entitlement/config or as part of a manifest list. +- CloudClaw source read through the user's logged-in Chrome session. +- Current CloudClaw `main` routes in `internal/server/app/routes.go`: + - `GET /healthz` + - `POST /api/v1/auth/login` + - `POST /api/v1/devices/register` + - `POST /api/v1/snapshots` + - `POST /api/v1/snapshots/{id}/complete-upload` + - `GET /api/v1/snapshots` + - `POST /api/v1/snapshots/{id}/download-url` + - `POST /api/v1/restores` + - `POST /api/v1/restores/{id}/complete` + - `GET /api/v1/restores` +- Current CloudClaw auth contract is JSON email/password: + - `POST /api/v1/auth/login` + - request: `{ "email": "...", "password": "..." }` + - response: `{ "access_token", "refresh_token", "user": { "id", "tenant_id", "email", "role" } }` +- Current CloudClaw does not yet expose enterprise space/app/skill manifest endpoints. Desktop should tolerate this as empty application registry until backend adds the API. + +## CloudClaw Plugin Integration Notes + +- CloudClaw already includes an OpenClaw plugin under `plugins/openclaw-cloud-sync`; this is currently the practical integration point, not a service-side app/skill distribution API. +- The plugin commands are: + - `cloud_sync_status` + - `cloud_sync_login` + - `cloud_sync_logout` + - `cloud_sync_backup` + - `cloud_sync_restore` +- The upstream package metadata used object-shaped `openclaw.extensions` entries, while the current bundled OpenClaw runtime expects string entries. The 智念 built-in mirror normalizes this to `["./dist/index.js"]`. +- The upstream plugin registered commands with `id/title/run`; the current OpenClaw runtime expects `name/description/handler` and command names without dots. The 智念 mirror adapts these command registrations. +- Cloud sync server URL is currently configured from `YINIAN_CLOUD_SYNC_SERVER_URL` / `CLOUDCLAW_SERVER_URL`; if absent, the desktop derives it from `YINIAN_API_BASE_URL` by removing a trailing `/ingress`, then falls back to `https://onefeel.brother7.cn`. +- Startup verification showed `cloud-sync` is loaded by `openclaw plugins list`. + +## Engineering Assessment Findings 2026-04-26 + +- Project status: suitable for continued internal development, not yet suitable for a stable pilot build without cleanup. +- Strong points: + - TypeScript still compiles after broad changes. + - Vite renderer/main/preload build still succeeds. + - Existing unit coverage is substantial enough to catch stale UI/product-language expectations. + - Legacy ClawX E2E compatibility paths still pass in the sampled suite. +- Main risks: + - The working tree is very broad and mixes product UI, server login, knowledge base, Gateway plugin installation, packaging, and docs in one uncommitted batch. + - Unit tests are currently red due to terminology changes and UI metadata changes. + - Production YINIAN visual E2E is red because captcha-required login no longer matches the old mock-click flow. + - `hotelId` / `hotel_id` remains a compatibility internal in storage and server fixtures; safe for now, but it will keep leaking into future implementation unless a migration is planned. + - Knowledge base import is local-file-path based and should stay behind trusted desktop IPC/Host API boundaries; it is not yet a retrieval/indexing feature. + - Cloud Sync is installed as an OpenClaw plugin but should not be presented as an enterprise app-distribution solution. +- Recommended next stabilization phase: + - Freeze feature additions. + - Fix failing tests and E2E login setup. + - Split changes into reviewable checkpoints by domain: branding/UI, YINIAN auth/control plane, knowledge base, chat context, Cloud Sync plugin, packaging/docs. + - Add focused tests for Cloud Sync config install and knowledge import edge cases. + +## Stabilization Findings 2026-04-26 + +- The previously failing unit tests were stale copy assertions from the product-language migration; current customer-facing copy is now aligned around `应用`, `本机准备`, and `运行记录`. +- The visual smoke failure exposed a real mock-mode edge: captcha was required even when mock captcha rendering returned no image. Login now requires captcha only when a captcha field is actually visible. +- Cloud Sync startup configuration is now covered by tests, but Cloud Sync remains a plugin-level capability rather than a replacement for future enterprise application distribution. +- Knowledge-base import now has better test coverage for local safety boundaries; retrieval is still v0 manual context injection, not vector search or long-term memory. +- Remaining technical debt: + - Renderer bundle remains large at roughly 1.6MB minified / 481KB gzip. + - Vite still reports modules that are both dynamically and statically imported. + - Full Vitest passes but emits `MaxListenersExceededWarning` from test process listener accumulation. + +## Product Simplification Findings 2026-04-26 + +- Real service integration currently supports account login and a synthesized single service identity, but not account role, region, job title, operational health, PMS/OTA metrics, or recent business execution traces. +- Customer-facing UI should only show data backed by one of: + - login response + - current service identity + - application entitlements / local registry + - local knowledge-base registry + - chat/session state +- Legacy internal names such as `hotelId` / `hotel_id` remain compatibility implementation details; they should not appear in user-facing copy. +- `ota-monitoring` remains an internal compatibility category key for now, but UI maps it to generic `数据检查`. + +## Interaction And Knowledge Findings 2026-04-26 + +- The old knowledge-base chat integration only sent local file paths to the model. That made the feature fragile because the assistant may not read the path directly. The new flow asks Host API to read the app-managed backup and inject bounded text content into the chat request. +- Knowledge files were already copied to the app data directory, but the UI and chat path did not make the backup behavior obvious enough. The Knowledge page now says files are backed up locally and usable after source deletion. +- `getByLabel('账号')` and `getByLabel('密码')` became ambiguous after adding the remember-password checkbox. E2E now uses more specific selectors. +- History popover remains in the sidebar stacking context; the delayed close improves accidental closure without changing the whole sidebar positioning model. + +## NianxxPlay Bundling Findings 2026-05-06 + +- Next.js standalone output can run under Electron's own Node runtime using `ELECTRON_RUN_AS_NODE=1`; customer machines do not need Node/npm for this application path. +- The packaged NianxxPlay bundle currently weighs about 400MB, mostly because starter media assets under `public/seedance-starter-assets` are included. This is acceptable for this first 3.5 pass, but installer size should be reviewed before broad customer distribution. +- `.env.local` is still present in the source project and is read during local builds, but the desktop bundling script explicitly refuses to ship `.env*` files. +- Local uploads and generated results must remain user data, not app resources. The bundle script now excludes `public/uploads` and `public/generated-results`, and the runtime writes those files under desktop userData. +- The desktop service manager now checks `/api/desktop/health` instead of generic HTTP 200 responses, avoiding false positives when another app occupies port 3000. + +## Pilot Package Issue Findings 2026-05-06 + +- Customer log shows NianxxPlay does load `.env.runtime`, so the video-generation-service warning was not due to missing Seedance/OSS config in the bundle. +- NianxxPlay exits before readiness because the packaged app lacks `resources/nianxx-play/node_modules/next`: + - `Error: Cannot find module 'next'` + - require stack: `/Applications/智念助手.app/Contents/Resources/resources/nianxx-play/server.js` +- The source prepared bundle contains `build/apps/nianxx-play/node_modules/next`, but `release/mac-arm64/.../resources/nianxx-play` does not. This matches electron-builder's known behavior of respecting `.gitignore` and skipping `node_modules` in extraResources. +- The packaged mac app includes `Resources/bin/node` and npm, but not `Resources/bin/uv`; `prep:mac-binaries` only ran `node:download:mac`. This caused `uv ENOENT` during Python setup. +- `spctl --assess` on the local arm64 app reports `rejected, source=Unnotarized Developer ID`, and `xcrun stapler validate` reports no stapled ticket. Developer ID signing alone is not enough for customer-distributed macOS builds with native modules. +- `@mariozechner/clipboard` is an optional dependency of `@mariozechner/pi-coding-agent`; if absent, the code catches the require failure and falls back to platform clipboard tools such as `pbcopy`, so it is safe to omit for this desktop pilot. +- The current `release/mac-arm64/智念助手.app` has been manually modified after signing for diagnosis, so `codesign --verify` now reports sealed-resource changes. A new package must be produced by electron-builder so afterPack changes happen before signing/notarization. +- The prepared pilot NianxxPlay bundle has non-empty Seedance and OSS runtime keys, so any later “视频生成服务还没有配置完成” in a fresh package should be investigated as runtime env loading or route-level error handling, not missing build-time env material. + +## Long Task And Model Diagnostics Findings 2026-05-06 + +- The long-task UI reset was a real frontend state-machine issue: OpenClaw can emit `agent` notifications with `phase=end` for intermediate tool/agent phases, while the renderer treated `end` like final completion and cleared `sending`, `activeRunId`, and `pendingFinal`. +- The customer diagnostic report's `No API key found for provider minimax` is consistent with a provider/profile alias mismatch: default model references `minimax/MiniMax-M2.7`, while some pilot installs only had a stale `minimax-cn:default` auth profile. +- OpenClaw's pricing catalog fetch can block or delay Gateway startup on weak networks. The internal pilot runtime should explicitly disable it via `models.pricing.enabled=false`. +- Heartbeat recovery thresholds were too aggressive for long local model/tool work on macOS. A 12s pong timeout with 3 misses could restart the Gateway while work was still alive; 25s / 5 misses is a safer pilot default. + +## Optional Native Clipboard Findings 2026-05-06 + +- `@mariozechner/pi-coding-agent` attempts to load `@mariozechner/clipboard` but catches failures and falls back to platform clipboard commands such as `pbcopy`, so omitting the optional native package is acceptable for the pilot app. +- The repeated customer prompt can persist even after a fixed package if a prior install copied `@mariozechner/clipboard*` into user-managed OpenClaw directories such as `~/.openclaw/runtime/openclaw`, `~/.openclaw/npm`, `~/.openclaw/extensions`, or `~/.openclaw/plugin-runtime-deps`. +- The app should therefore both stop bundling the optional native clipboard package and clean known user-owned OpenClaw dependency caches before Gateway startup. +- This does not replace proper macOS notarization. A notarized/stapled build is still the correct long-term distribution fix for native modules generally; this patch removes the specific optional native module that is being loaded and blocked today. + +## Complete Self Check Findings 2026-05-06 + +- The full unit suite is healthy again after updating stale assertions: 93 files / 620 tests passed. +- The previous failed tests were not current runtime regressions; they were mostly old product-language and UI-structure assumptions from before: + - ability packs moved to a settings-oriented page with quick-task configuration first. + - Today became a quick-entry workspace instead of a data dashboard. + - internal thinking chunks are intentionally hidden from the task execution graph. + - only Chinese and English remain supported visible languages. +- `tavily-search` was still present in the preinstalled skills manifest and generated bundle despite the product direction to remove that web-search helper. Removing it reduces the preinstalled bundle from seven skills to six. +- Current preinstalled skills are: + - `pdf` + - `xlsx` + - `docx` + - `pptx` + - `find-skills` + - `self-improving-agent` +- NianxxPlay's prepared desktop bundle is structurally correct for the pilot: it includes the standalone server and Next runtime, and excludes user upload/output/cache directories. The only `.env*` file in the bundle is `.env.runtime`, which is intentional for the current internal-testing phase. +- Package resource risks that remain before broader customer distribution: + - `release` is still large at about 2.3 GB, and the NianxxPlay bundle alone is about 419 MB. + - notarization/stapling is still the real macOS distribution requirement; removing the optional clipboard module prevents the known prompt but is not a general replacement for notarization. + - Vitest's `MaxListenersExceededWarning` is non-blocking today but suggests test setup/listener cleanup should be revisited. + +## Office Skills Runtime Findings 2026-05-09 + +- MiniMax's repository contains replacement skills for the broken office flow: + - `skills/minimax-docx` + - `skills/minimax-pdf` + - `skills/minimax-xlsx` + - `skills/pptx-generator` +- The replacement skills must be copied under the existing local slugs and frontmatter names (`docx`, `pdf`, `xlsx`, `pptx`) so quick-task prompts and OpenClaw skill lookup remain stable. +- The old installed `~/.openclaw/skills/docx`, `xlsx`, and `pptx` directories contained direct LibreOffice/soffice guidance and helper scripts from the old Anthropic skills. Those are now replaced locally with the new MiniMax sources. +- MiniMax `docx` uses an OpenXML/.NET workflow; `.NET` is not present on the current machine, so diagnostics should report it explicitly instead of letting a document task fail mysteriously. +- MiniMax `pdf` and `xlsx` use Python packages that may not exist on a fresh customer machine. The app already bundles `uv` and can install/manage Python 3.12, so the hardening path should prepare common packages through uv rather than asking customers to run pip. +- MiniMax `pptx` uses PptxGenJS and optional icon/image packages. The desktop project already includes `pptxgenjs`, `react-icons`, `react`, `react-dom`, and `sharp`, so the skill should prefer project-bundled modules rather than global npm installs. +- `uv pip install --python ` reports the managed standalone Python as externally managed unless `--break-system-packages` is supplied. For this app-owned runtime, using the flag is appropriate because it installs into the bundled/managed Python, not the user's system Python. +- The local `openclaw.json` had become invalid because model repair code wrote provider-level `timeoutSeconds` and `models.pricing`, which current OpenClaw rejects. The repair path now deletes those legacy keys instead of writing them. +- `agents.defaults.heartbeat.every = "0m"` is the documented OpenClaw way to disable heartbeat. It is kept intentionally so heartbeat-only prompts do not appear in normal desktop conversations. +- The full test suite exposed one stale channel assertion: WeCom is now saved as an internal channel and old plugin registration is intentionally absent. The test now matches that product direction. +- Homebrew's `dotnet-sdk` cask installs a system pkg and needs interactive `sudo`, which is not suitable for this automation context. A user-local `~/.dotnet` install works and must be explicitly added to Electron/OpenClaw child process `PATH`. +- The MiniMax xlsx source still included a dynamic recalculation path that referenced desktop office engines even after install prompts were rewritten. Removing that script/reference path entirely is safer for ordinary users than trying to phrase it as optional. +- Manual local skill replacement without `.clawx-preinstalled.json` causes the app startup preinstaller to treat those skills as user-managed and skip future managed updates. Any manual sync of preinstalled skills should write the marker too. +- The screenshot showing LibreOffice guidance came from an already-generated OpenClaw session history entry, not from the current local skill files after cleanup. A frontend sanitizer is therefore necessary in addition to prompt cleanup, because old history and model-invented explanations can still be hydrated into the desktop UI. + +## Customer Clean Install Findings 2026-05-12 + +- The customer app did not show initialization because renderer settings trusted the persisted `setupComplete` flag. The current status check did not verify whether the managed OpenClaw runtime still contained required runtime files. +- The new customer log confirms chat fails before model execution with: + - `Missing workspace template: AGENTS.md (/Users/inmanw/.openclaw/runtime/openclaw/docs/reference/templates/AGENTS.md)`. + - `ERR_MODULE_NOT_FOUND: Cannot find package 'openclaw' imported from .../dist/extensions/codex/prompt-overlay.js`. +- `scripts/bundle-openclaw.mjs` deletes the entire OpenClaw `docs` directory, and `scripts/after-pack.cjs` deletes it again after packaging. That removes runtime templates that OpenClaw uses to build workspace context. +- A copied OpenClaw package outside `node_modules/openclaw` cannot resolve bare self imports like `openclaw/plugin-sdk/provider-model-shared` unless a self-reference exists under `runtime/openclaw/node_modules/openclaw`. +- The customer diagnostics also show missing model credentials (`providerKeys=0`, `调用凭据` error). Runtime/template repair is necessary but may not be sufficient if the pilot package does not seed or sync an internal MiniMax credential. +- NianxxPlay starts, but logs show `Cannot find module .../.next/server/app/generated-results/[...path]/route.js` after generated results were excluded. Excluding user outputs is correct, but the compiled route module must remain or be removed from routing. +- Fix status: + - OpenClaw bundling now preserves only `docs/reference/templates`. + - Managed runtime install now creates `node_modules/openclaw -> ..` self-reference after copying bundled resources. + - `setupComplete` is now treated as a cached hint, not proof; main status verification can reset it when runtime files are missing. + - Gateway startup now requires verified setup plus Yinian authentication. + - NianxxPlay bundle filtering keeps `.next/server/app/generated-results/[...path]/route.js` and still excludes `public/generated-results`. + +## Customer Clean Install Verification 2026-05-12 + +- Added pilot model auth seeding so clean customer machines no longer start the Gateway with `providerKeys=0`. +- Added OpenClaw package roots to office-skill runtime dependency resolution so packaged document skills can find bundled Node modules. +- Verification passed: + - `pnpm run typecheck` + - `pnpm test` (94 files / 628 tests; existing MaxListeners warnings remain) + - `pnpm run package:pilot` + - `pnpm run package:mac:pilot:arm64` +- Packaged resource checks passed: + - OpenClaw runtime templates exist in the signed app. + - NianxxPlay compiled `generated-results` route exists while `public/generated-results` and uploads remain excluded. + - `docx`, `pdf`, `pptx`, `xlsx`, `design`, and `image-search` are included as preinstalled skills. + - `clipboard.darwin-arm64.node` and `@mariozechner/clipboard*` are absent. + - No `*.app` directories remain inside preinstalled skills. +- macOS checks: + - `codesign --verify --deep --strict` passes for `release/mac-arm64/智念助手.app`. + - `spctl --assess` still rejects the app as `Unnotarized Developer ID`. + - `xcrun stapler validate` reports no stapled ticket, matching electron-builder's skipped notarization. +- Internal pilot secret note: + - The current pilot package intentionally includes `resources/yinian-internal/model-auth-profiles.json` and NianxxPlay `.env.runtime`. + - This is acceptable only for closed internal testing. Production distribution must move credentials behind service-side session/token exchange or update-time provisioning. + +## NianxxPlay Demo And Process Findings 2026-05-12 + +- The "生成示例" video was broken because the primary reference template pointed at `/generated-results/restored-project...`. +- `/generated-results` is correctly treated as user output and excluded from customer packages, so demo/reference media must not live there. +- The reference video and cover now live under `/starter/promo/`, which is formal built-in app content and ships with the desktop bundle. +- The extra Dock process was likely caused by running the packaged NianxxPlay standalone server through the Electron app binary with `ELECTRON_RUN_AS_NODE=1`. +- The service manager now prefers the bundled Node binary at `Resources/bin/node`, so the child server should be a normal background CLI process instead of a visible Electron app process. + +## Design And HTML Slides Skill Findings 2026-05-12 + +- `design` should be treated as a design director/design-system skill, not as a narrow UI generator. Its handoff targets include `html-slides`, `pptx`, image generation, and frontend implementation. +- `html-slides` should be the execution skill for browser-based decks. First version scope is HTML decks and PPTX-to-HTML conversion, not general website generation. +- `beautiful-html-templates` can be bundled lightly by shipping `index.json`, `templates/`, and `runtime/`; the 30MB screenshot directory is unnecessary for first app testing because previews can be generated from templates. +- The template catalog currently provides 32 templates. OpenClaw sees both `design` and `html-slides` as eligible and enabled after local app-managed sync. +- Preinstalled local sync must preserve `.clawx-preinstalled.json` markers so future app startup updates these skills instead of treating them as user-managed. + +## Task And Skill Management Findings 2026-05-13 + +- Current `src/pages/Tasks/index.tsx` duplicates quick-task creation/editing under the Task Center, which makes "任务" ambiguous versus ability-pack-owned quick tasks. +- `src/pages/YinianSkills/index.tsx` still contains the old quick-task management panel, but the visible tab button for it was removed and the default tab changed to service-issued skills. +- Local git history at `0abc481` confirms the earlier ability-pack page defaulted to quick-task management and exposed a `quickTasks` tab before server/local skill tabs. +- The older scheduled-task delivery design is still present in `src/pages/Cron/index.tsx`: delivery mode, supported channel filtering, channel account selection, target loading through `/api/channels/targets`, and `delivery: { mode, channel, accountId, to }` payload preservation. +- The new Task Center scheduled dialog currently always writes `delivery: { mode: 'none' }`, so creating or editing scheduled tasks there drops the push-channel design. +- To reduce product-language ambiguity, the ability-pack-owned template concept should be presented as “快捷能力” rather than “快捷任务”; scheduled/manual items remain tasks. + +## Playwright Chromium Runtime Findings 2026-05-13 + +- Customer confirmation: the 10:28 SIGTERM killed a stuck `npx playwright install chromium` browser download child process. +- The Gateway log's `stuck session recovery ... age=924s aborted=true` matches the configured 15-minute active-run abort threshold plus heartbeat timing. +- The immediate failure mode is not PPT generation itself; it is task-time Chromium download and concurrent Playwright browser installation. +- Current skill hardening already rewrites some old PDF instructions, but `html-slides` lacked an explicit runtime policy forbidding task-time Playwright browser downloads. +- The app should expose browser preview readiness as a warning-level diagnostic. Missing Chromium should not block document/deck generation; it should only skip optional browser preview. +- Setting an app-scoped `PLAYWRIGHT_BROWSERS_PATH` without mirroring existing complete browsers would hide a valid default Playwright cache. The runtime guard therefore symlinks already-complete Chromium installs into the app-scoped cache instead of forcing a re-download. + +## Cron Run Final-State Findings 2026-05-13 + +- A cron run can be summarized as `status: error` when the runtime records an intermediate mutating tool error as `lastToolError`, even if the agent later recovered and completed the requested work. +- The reliable success signal for this case is the matching session trajectory's `trace.artifacts.data.finalStatus`. +- For the local "写笑话" run, the cron summary reported `Edit: in ~/Desktop/create-joke.js failed`, but the trajectory artifact reported `finalStatus: success` and included the assistant's final desktop-save confirmation. +- Task Center should use the final run artifact to reconcile card/history state, while keeping the original tool error as diagnostic warning metadata rather than showing a false failed task. + +## Cron Conversation Refresh Findings 2026-05-13 + +- The fixed task conversation initially loads before a triggered cron run finishes, so without a later forced reload it can stay empty or stale until manual refresh. +- Existing cron fallback history returned the task metadata as a `system` message, but chat history filters hide all system messages; that made the current task query invisible even when fallback history existed. +- Isolated cron runtime events use run-specific session keys (`agent:main:cron::run:`). The UI product model needs those events folded into `agent:main:cron:`. +- The ordinary quiet history refresh path can be throttled, so terminal cron events need a forced foreground refresh for the active fixed task conversation. + +## Sidebar Navigation Findings 2026-05-13 + +- The left sidebar was carrying mixed concerns in one vertical flow: conversation actions, task management, resource links, and settings competed at the same level. +- Moving New Chat and History directly below Home/Today makes the conversation surface a primary workspace action instead of a footer utility. +- Pinned quick tasks are operational accelerators for Task Center, so they should sit directly under Task Center and be capped in the sidebar to avoid turning navigation into a long task list. +- App Center and Knowledge behave more like resource/management utilities than daily workspace actions, so placing them above Settings in the footer keeps the top and middle areas focused. + +## App-Scoped Local Preference Findings 2026-05-13 + +- Quick capabilities and YINIAN account remarks were stored only in renderer `localStorage`, which is scoped by URL origin. +- The user's existing data was still present under `http://localhost:5173`, but starting the dev app on another port created a different storage origin, making those preferences appear empty. +- Preferences that are business/user data, not transient browser cache, need an Electron app-level persistence layer independent of dev server port or production `file://` loading. +- The app-level copy should remain bidirectional for now: old origin-local data can migrate up, and app-level data can hydrate any new renderer origin. + +## 商品中心 Web 应用接入 Findings 2026-05-13 + +- 用户要求在应用中心新增“商品中心”,业务定位是酒店企业以底价采购旅游资源,目标地址为 `https://ticket.nianxx.cn/`。 +- 目标站点当前公开页面文案为“一感旅游”,说明覆盖景区商品采购、余额管理、订单退款、报表对账。 +- 响应头检查:`HTTP/2 200`,`content-type: text/html; charset=utf-8`,`access-control-allow-origin: *`,未返回 `X-Frame-Options` 或阻断性 `Content-Security-Policy`,适合优先做桌面端 iframe 内嵌。 +- 当前 `src/stores/app-center.ts` 只有 `nianxx-play` 一个 built-in app。 +- 当前 `src/pages/AppCenter/index.tsx` 对 `webview` 类型仍调用 `window.electron.openExternal`,需要改为支持 route 优先的内嵌宿主页面。 +- 后续 SSO 边界应集中在商品中心 URL builder 或 Host API,不应在 renderer 中持有长期密钥。 +- 用户后续要求应用展示名改为“旅游资源订购”。 +- `https://ticket.nianxx.cn/auth.js` 使用 `localStorage` 保存 `ticketService.token` 和 `ticketService.user`;普通 iframe 虽然可能保留 origin storage,但在 Electron 桌面内更明确的做法是使用 `` 给该内置 Web 应用独立持久会话。 + +## 快速视频创作 Rename Findings 2026-05-13 + +- 用户要求“智念视频助手”展示名改为“快速视频创作”。 +- 相关展示文案集中在 `src/i18n/locales/*/app-center.json` 的 `items.nianxxPlay` 和 `host` 节点;内部组件、路由、Host API、服务名仍沿用 `nianxx-play/NianxxPlay` 作为技术标识。 +- 英文状态下需要覆盖两个内置应用:`Travel Resource Ordering` 和 `Quick Video Creation`,避免露出 `items.*` i18n key。 + +## WhatsApp QR Findings 2026-05-13 + +- WhatsApp 扫码链路是:`ChannelConfigModal` POST `/api/channels/whatsapp/start`,Host API 调用 `WhatsAppLoginManager.start()`,Baileys 发出 `connection.update.qr` 后本地渲染 PNG base64,再通过 `channel:whatsapp-qr` 事件回前端。 +- QR PNG 渲染函数本身可用;用 `qrcode-terminal` vendor QRCode 生成矩阵和 PNG 编码没有发现基础 API 错配。 +- 在临时 HOME 中直接拉起 WhatsApp 登录管理器,当前环境连接 `wss://web.whatsapp.com/ws/chat` 连续报 `WebSocket Error ()`,直接 `ws` 连接也超时,因此“生成不出来”的根因更像网络/代理而不是二维码图片渲染。 +- 现有代理设置只应用到 Electron session、Gateway env 和部分 `proxyAwareFetch` 请求;Baileys 使用 Node `ws`,此前没有注入 `agent`,所以即使用户在高级设置中配置代理,WhatsApp WebSocket 仍会直连。 +- `src/lib/host-api.ts` 的 unified IPC proxy envelope 只检查了外层 `response.ok`,没有检查内层 `data.ok`;Host API 返回 500 JSON 时会被当成成功 payload 交给前端,容易造成 QR 按钮一直处于等待事件状态。 + +## OpenClaw Upgrade Findings 2026-05-13 + +- 当前项目固定 `openclaw` 版本为 `2026.4.29`。 +- npm dist-tag 查询结果:`latest=2026.5.7`,`beta=2026.5.12-beta.4`。本次按稳定版 latest 升级,不切 beta。 +- OpenClaw 2026.5.7 不再导出 `openclaw/package.json` 子路径;项目代码没有直接依赖该子路径,桌面端的路径解析仍通过文件系统根目录读取包信息。 +- 2026.5.7 的 main-session restart recovery 改为通过 transcript lock path 匹配被中断会话,旧的 `interruptedSessionIds` 补丁片段已不适用;需要用 `resolveEntryTranscriptLockPaths(...).some(...)` 这一新版片段打补丁。 +- 2026.5.7 的 diagnostic heartbeat 已新增 stalled embedded run 的 `allowActiveAbort: true` 分支;YINIAN 仍需要在普通 stuck recovery 分支上保留 `YINIAN_OPENCLAW_STUCK_ACTIVE_ABORT_MS` 阈值,避免过早主动中止活跃 run。 +- 2026.5.7 的嵌入式 run 已有 `resolveEmbeddedAttemptToolConstructionPlan`,`isRawModelRun` 会阻止构建工具、bundle MCP 和 bundle LSP;这覆盖了旧版 fast chat disable-tools 补丁的主要目的。 +- 2026.5.7 的子进程执行入口已迁移到 `exec-*`/Windows helper,并在关键 spawn 位置包含 `windowsHide`;旧的 workspace command runner 与 PTY 补丁片段不再匹配,但已核对新版产物中的隐藏窗口逻辑。 diff --git a/progress.md b/progress.md new file mode 100644 index 0000000..f590939 --- /dev/null +++ b/progress.md @@ -0,0 +1,1138 @@ +# Progress Log + +## 2026-05-13 对话 Markdown 与隐藏提示泄漏修复 + +- 收到用户反馈:会话里出现 `[[YINIAN_BUSINESS_RESPONSE_GUIDANCE]]`,Markdown/表格渲染还不够结构化,执行思考不需要边框。 +- 已停止 direct chat 和 cron 路由在真实消息里追加隐藏业务准则,改为依赖 `AGENTS.clawx.md` 中的基础业务行为准则。 +- 已在用户消息显示、会话标题/去重清洗链路补上隐藏准则剥离,兼容已经污染的旧历史。 +- 已增强 ChatMessage 的 Markdown 标题、列表、引用、表格渲染;表格增加滚动外壳和数据表样式。 +- 已将 ExecutionGraphCard 从边框卡片改为无框时间线视觉。 +- 已补充单测覆盖隐藏准则不显示、Markdown 表格增强渲染、chat/cron 不再发送隐藏准则。 +- 表格专项 E2E 首次失败,原因是旧用例停在默认新会话路由,未加载种子历史;已固定到 `#/chat` 并重跑通过。 +- 根据用户反馈,已删除新对话空态中的 icon 和“智念助手对话”小标题,只保留“今天从哪里开始?”。 +- 新对话空态 cleanup 后重新运行 `pnpm run typecheck`,通过。 +- Verification passed: + - `pnpm vitest run tests/unit/chat-message.test.tsx tests/unit/chat-target-routing.test.ts tests/unit/cron-routes.test.ts tests/unit/business-guidance.test.ts` + - `pnpm vitest run tests/unit/chat-page-execution-graph.test.tsx tests/unit/task-visualization.test.ts tests/unit/chat-store-history-retry.test.ts` + - `pnpm vitest run tests/unit/chat-message.test.tsx tests/unit/chat-target-routing.test.ts tests/unit/cron-routes.test.ts tests/unit/chat-store-history-retry.test.ts` + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` + - `pnpm exec playwright test --config=playwright.legacy.config.ts tests/e2e/yinian-visual-smoke.spec.ts` + - `pnpm exec playwright test --config=playwright.legacy.config.ts tests/e2e/chat-table-header-light.spec.ts` + - `git diff --check` + +## 2026-05-13 设置偏好深色 UI + +- Started dark-mode Preferences investigation after user report. +- Captured current Electron screenshot for `/settings/preferences` in dark mode. +- Identified low-contrast preference buttons, missing Preferences panel hierarchy, translucent dark title bar band, and missing global dark surface fallback as the main issues. +- Updated Preferences into a structured `yinian-panel` with divided rows and higher-contrast active/inactive choice controls. +- Normalized dark backgrounds for `body`, `.yinian-app-surface`, `MainLayout`, `TitleBar`, `Tabs`, and shared YINIAN panels. +- Verified: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/title-bar.test.tsx tests/unit/launch-at-startup.test.ts tests/unit/app-center.test.tsx tests/unit/nianxx-play-url.test.ts` + - `pnpm run build:vite` + - Electron screenshot at `test-results/settings-preferences-dark-electron-after.png` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` +- Attempted `pnpm exec playwright test --config=playwright.legacy.config.ts tests/e2e/settings-proxy.spec.ts`; it failed before reaching Settings because the legacy setup helper still expects `main-layout` immediately after setup instead of the current login-first flow. + +## 2026-04-26 + +- Completed full test run after M1 foundation work. +- Fixed E2E compatibility around YINIAN login-first routing. +- Fixed E2E default language mismatch by using English in E2E mode. +- Fixed E2E gateway status initialization without reintroducing auto-start. +- Verified: + - `pnpm run typecheck` + - `pnpm run test` + - `pnpm run test:e2e` +- Created persistent planning files: + - `task_plan.md` + - `findings.md` + - `progress.md` +- Began Phase 1 M1 stabilization. +- Added `docs/M1_HANDOFF.md` with module inventory, env switches, storage boundary, public API, and verification baseline. +- Updated `docs/START_HERE.md` to point to the M1 handoff note and E2E command. +- Fixed channel health timestamp edge case in `electron/api/routes/channels.ts`. +- Verified Phase 1: + - `pnpm exec vitest run tests/unit/channel-routes.test.ts` + - `pnpm run typecheck` + - `pnpm run test` + - `pnpm run test:e2e` +- Phase 1 marked complete. +- Began and completed Phase 2 server contract v0. +- Added `docs/SERVER_CONTRACT_V0.md`. +- Added contract fixture `tests/fixtures/yinian-server-contract.ts`. +- Updated `electron/yinian/http-control-plane.ts`: + - `/auth/refresh` session restore. + - access token remains memory-only. + - refresh token persists for restore. + - config entitlement/channel/ui policy normalization. + - manifest sync returns local registry statuses. +- Expanded `tests/unit/yinian-control-plane.test.ts` from 6 to 11 tests, including HTTP contract paths and failure handling. +- Verified Phase 2: + - `pnpm exec vitest run tests/unit/yinian-control-plane.test.ts` + - `pnpm run typecheck` + - `pnpm run test` + - `pnpm run test:e2e` +- Began and completed Phase 3 Today M2 pilot surface. +- Rebuilt `src/pages/Today/index.tsx` into a B-end operations cockpit: + - health summary + - pending/exception queue + - arrival/departure summary + - skill status board + - recent agent actions + - skill sync error state +- Added `tests/unit/today-page.test.tsx`. +- Verified Phase 3: + - `pnpm exec vitest run tests/unit/today-page.test.tsx` + - `pnpm run typecheck` + - `pnpm run test` + - `pnpm run test:e2e` +- Began and completed Phase 4 Skills Manager M2. +- Rebuilt `src/pages/YinianSkills/index.tsx` into an operator-facing Skills Manager: + - entitlement/local registry merged status + - version and update visibility + - sync failure and empty registry states + - local source, bundle hash, install/sync timestamps + - workspace-scoped registry reload +- Added `tests/unit/yinian-skills-page.test.tsx`. +- Verified Phase 4: + - `pnpm exec vitest run tests/unit/yinian-skills-page.test.tsx` + - `pnpm run typecheck` + - `pnpm run test` + - `pnpm run test:e2e` +- Began and completed Phase 5 Design System Pass. +- Added `src/components/yinian/ui.tsx` with shared YINIAN UI primitives: + - page shell + - page header + - panel + - empty state + - notice + - metric card + - primary/accent classes +- Updated Login, Today, Skills, sidebar service context, main layout, and sidebar active states to use a calmer navy/slate B-end business style. +- Verified Phase 5: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/today-page.test.tsx tests/unit/yinian-skills-page.test.tsx tests/unit/yinian-store.test.ts tests/unit/yinian-skills-store.test.ts` + - `pnpm run test` + - `pnpm run build:vite` + - `pnpm run test:e2e` +- Began and completed Phase 6 Pilot Packaging And QA. +- Added `docs/PILOT_QA.md` with: + - pilot scope + - mock/HTTP/E2E modes + - guided demo script + - QA checklist + - verification commands + - packaging notes + - known issues and product gaps +- Updated `docs/START_HERE.md` with pilot QA and `build:vite`. +- Updated `docs/M1_HANDOFF.md` to reference the current pilot QA baseline and current test counts. + +## Next + +- Await commit instruction, or start the next product phase: real server integration and first real business skill execution path. + +## 2026-05-13 Task Center Iteration + +- Continued the approved task center implementation after context compaction. +- Added `/tasks` route and redirected legacy `/cron` to `/tasks?tab=scheduled`. +- Reworked the production sidebar into three business groups: + - 快速使用:应用中心、知识库 + - 任务:任务中心 + - 对话:新对话、历史会话 +- Added the task center page with 快捷任务、定时任务、执行记录 tabs. +- Centralized quick-task prompt construction in `src/lib/quick-task-prompt.ts`. +- Added persisted task center bindings/run records in `src/stores/task-center.ts`. +- Split task center persistence into `yinian:scheduled-task-bindings` and `yinian:task-run-records`, with a migration fallback from the temporary combined key. +- Moved visible quick-task management out of the settings capability-pack tabs and into task center. +- Added Chinese and English task-center locale files and wired the `tasks` namespace. +- Updated delivery smoke expectations from the legacy cron page to task center. +- Verified focused tests: + - `pnpm exec vitest run tests/unit/quick-task-prompt.test.ts tests/unit/task-center-store.test.ts tests/unit/chat-input.test.tsx` +- Fixed current test expectations for the managed preinstalled skill list (`web-search` is now included) and current-session execution-graph state. +- Verified final gate for this slice: + - `pnpm run typecheck` + - `pnpm test` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` + +## 2026-05-13 Task Center Separation Follow-up + +- Removed quick-capability surfaces from `src/pages/Tasks/index.tsx`; Task Center now has only scheduled tasks and run history. +- Removed quick-capability template selection from the scheduled-task dialog. +- Stopped creating scheduled-task quick-capability bindings; edit/delete still clears stale legacy bindings for the affected cron job. +- Fixed `@` mention keyboard navigation by preserving selection across navigation keyup events and guarding browser-only `scrollIntoView`. +- Replaced raw custom time entry with structured repeat controls that generate cron expressions for daily, weekly, monthly, and interval schedules, with advanced Cron as fallback. +- Updated Chinese/English task-center copy and tests. +- Verification: + - `pnpm run typecheck` + - `pnpm vitest run tests/unit/tasks-page.test.tsx tests/unit/task-center-store.test.ts` + - `pnpm test` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` +- Notes: + - First E2E attempt failed because `dist` was stale and still exposed `tasks-tab-quick`; rebuilding fixed it. + - Existing MaxListeners and Vite chunk warnings remain unchanged. +- Follow-up fix: + - Allowed `@` capability mentions inside sentence text instead of only after whitespace or at the start. + - Stored the mention replacement end position so clicking a suggestion preserves text after the cursor. + - Verified with `pnpm run typecheck`, `pnpm vitest run tests/unit/tasks-page.test.tsx`, and `pnpm test`. +- Task Center pinning follow-up: + - Renamed the scheduled-task product surface to “任务中心” while leaving execution-time settings unchanged. + - Added pinned task ids in `useTaskCenterStore` and task-card pin/unpin controls. + - Added sidebar quick triggers for pinned tasks; clicking one immediately calls the existing cron trigger path and records a run. + - Updated Today/task/sidebar related copy and tests. + - Verified: + - `pnpm run typecheck` + - `pnpm vitest run tests/unit/tasks-page.test.tsx tests/unit/task-center-store.test.ts tests/unit/today-page.test.tsx` + - `pnpm test` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` +- Task Center density and sidebar structure follow-up: + - Converted Task Center management from two-column cards to a compact list/table layout. + - Preserved enable/disable, delivery visibility, execution metadata, pin, run-now, edit, open-session, and delete actions. + - Moved task metrics into compact header chips and kept execution-time settings untouched. + - Reorganized the sidebar navigation into a scrollable structured area with bounded pinned quick triggers. + - Verified with typecheck, focused task tests, full tests, Vite build, and delivery smoke E2E. +- Immediate history visibility follow-up: + - Added shared local session visibility patching for optimistic history rows. + - New desktop sessions now receive local `updatedAt` immediately, while unused blank sessions remain cleanable. + - First sent messages upsert local session row, label, and activity in the same state update. + - Cron trigger and Gateway started/completed notifications now make automation sessions visible before `sessions.list` catches up. + - Verified with typecheck, focused chat/gateway/cron/task tests, full tests, Vite build, and delivery smoke E2E. +- Task execution conversation follow-up: + - Kept each task on its stable `agent:main:cron:` conversation. + - Task Center "立即执行" now switches to that task conversation and opens Chat immediately. + - Sidebar pinned task triggers do the same, so customer follow-up lands in the right thread. + - Added a focused regression test and verified with typecheck, focused tests, full tests, Vite build, and delivery smoke E2E. +- Task conversation naming follow-up: + - Task sessions now force label/displayName to the task name on run, open, sidebar trigger, and cron-trigger visibility updates. + - Cron task sessions are protected from the normal "first user message becomes session name" behavior. + - Gateway automation events try to backfill task names from local cron jobs when available. + - Verified with typecheck, focused chat/task/gateway/cron tests, full tests, Vite build, and delivery smoke E2E. +- Task history merge reliability follow-up: + - Fixed task session loads to merge Gateway `chat.history` with cron run-log fallback instead of choosing one source. + - Added near-duplicate filtering and timestamp sorting for merged cron history. + - Failed cron run fallback messages now use assistant role with `isError` so they remain visible. + - Added focused tests for partial Gateway history plus fallback merge and failed fallback visibility. + - Verified with typecheck, focused history/cron/task/gateway tests, full tests, Vite build, and delivery smoke E2E. + +## 2026-04-26 Brand/UI System + +- Generated and persisted the 智念助手 design system at `design-system/智念助手/MASTER.md`. +- Began project-wide UI customization with the brand foundation: + - replaced the app icon source SVGs in `resources/icons/` + - generated desktop icon outputs with `pnpm run icons` + - replaced renderer wordmark at `src/assets/logo.svg` + - updated package metadata and electron-builder product metadata + - updated window title, Linux desktop identity, tray labels, startup desktop entry, Login, Sidebar, and Windows title bar branding +- Verified: + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec vitest run tests/unit/launch-at-startup.test.ts tests/unit/today-page.test.tsx tests/unit/yinian-skills-page.test.tsx` +- Completed UI Customization Phase 2 Core Surface Refinement: + - expanded `src/components/yinian/ui.tsx` with header actions, kicker, status dot, info row, list item, and section panel primitives + - refined Login with stronger product narrative and a cleaner form panel + - refactored Today sections and operational rows onto shared primitives + - refactored Skills Manager cards onto shared panels/info rows + - refined sidebar service context into a clearer workspace context surface +- Verified Phase 2: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/today-page.test.tsx tests/unit/yinian-skills-page.test.tsx tests/unit/yinian-store.test.ts tests/unit/yinian-skills-store.test.ts tests/unit/launch-at-startup.test.ts` + - `pnpm run build:vite` +- Completed UI Customization Phase 3 Inherited Surface Brand Boundary: + - grouped Sidebar navigation into 快速使用, AI 工作, and 扩展 + - kept route test ids stable + - updated major Chinese Chat/Settings/Setup customer-facing strings from ClawX to 智念助手 + - updated Setup welcome logo alt/asset naming +- Verified Phase 3: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/today-page.test.tsx tests/unit/yinian-skills-page.test.tsx tests/unit/launch-at-startup.test.ts` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/main-navigation.spec.ts tests/e2e/settings-proxy.spec.ts tests/e2e/app-smoke.spec.ts` +- Completed UI Customization Phase 4 Inherited Page Visual Polish: + - removed inherited oversized serif heading style from Chat, Settings, Models, Agents, Channels, and Cron surfaces + - normalized headings toward compact system-font dashboard treatment + - replaced remaining major Chinese customer-visible ClawX copy in Agents, Cron, Channels, Chat, Settings, and Setup + - retained technical upstream GitHub references where still useful +- Verified Phase 4: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/today-page.test.tsx tests/unit/yinian-skills-page.test.tsx tests/unit/launch-at-startup.test.ts` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/main-navigation.spec.ts tests/e2e/settings-proxy.spec.ts tests/e2e/provider-lifecycle.spec.ts tests/e2e/app-smoke.spec.ts` +- Completed UI Customization Phase 5 Screenshot Visual QA: + - added `tests/e2e/yinian-visual-smoke.spec.ts` + - captured Login, Today, and Skills desktop baselines in `test-results/yinian-visual/` + - fixed fresh-user production routing so unauthenticated sessions land on 智念登录 instead of legacy setup + - visually inspected the 1440x900 screenshots for obvious overlap, blank states, and brand consistency +- Verified Phase 5: + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts` + +## 2026-04-26 B-end Repositioning Pass + +- Repositioned customer-facing copy from a hotel-only product to a broader B-end AI Agent desktop product. +- Sidebar primary usage group is now `快速使用`. +- Login, Today, Skills, tray tooltip, installer metadata, zh setup/chat/settings copy, and pilot docs now use workspace/business-object language. +- Mock control-plane demo data now uses `智念杭州运营空间` and `智念上海增长空间`. +- Added compatibility aliases (`YinianWorkspace`, `switchWorkspace`) while keeping existing `hotelId`/`switchHotel` internals stable for storage and HTTP contract safety. +- Verified: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/yinian-control-plane.test.ts tests/unit/yinian-store.test.ts tests/unit/yinian-skills-store.test.ts tests/unit/today-page.test.tsx tests/unit/yinian-skills-page.test.tsx tests/unit/launch-at-startup.test.ts` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts` + +## 2026-04-26 Shell Simplification Pass + +- Removed the old top workspace bar from `MainLayout`. +- Moved current service context into the left sidebar as a non-switching account-bound service card. +- Moved sync and logout actions into the sidebar footer. +- Deleted the old `Yiniansidebar service context` component so future work does not reintroduce the top switcher accidentally. +- Refreshed visual baselines for Login, Today, and Skills. +- Verified: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/today-page.test.tsx tests/unit/yinian-skills-page.test.tsx tests/unit/yinian-store.test.ts` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts` + +## 2026-04-26 Sidebar IA Pass + +- Optimized the left sidebar around the two core customer areas: + - `快速使用`: Today and Skills. + - `对话`: New Chat and Chat. +- Hid Models, Agents, Channels, Cron, and Dev Console from the production sidebar because these capabilities should be issued and governed by the service side. +- Kept legacy configuration navigation available only in `e2e=1` compatibility mode so inherited tests can still cover the old surfaces. +- Moved service sync, logout, and the "configuration is service-managed" explanation into Settings. +- Added Settings to the visual smoke screenshot baseline at `test-results/yinian-visual/04-settings.png`. +- Verified: + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts` + +## 2026-04-26 Sidebar UX Pass + +- Moved the Dashboard/Today entry onto the top service card, so the current service object doubles as the dashboard launcher. +- Rebuilt `快速使用` as three direct actions: + - Skills + - 定时任务 + - 知识库 +- Added `src/pages/Knowledge/index.tsx` and `/knowledge` for a v0 knowledge-base upload/search/list surface. +- Reworked `对话` into: + - `新对话` + - `历史会话` +- `历史会话` now opens a hover popover with the session list and clicks through to the latest session. +- Updated visual smoke screenshots: + - `test-results/yinian-visual/04-knowledge.png` + - `test-results/yinian-visual/05-settings.png` +- Verified: + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts tests/e2e/main-navigation.spec.ts` + +## 2026-04-26 Enterprise Space Service Integration + +- Started service integration discovery for CloudClaw. +- Attempted anonymous repository access: + - `git ls-remote https://git.nianxx.cn/brother7/CloudClaw.git` + - Result: repository requires authentication. +- Recorded current desktop assumptions and backend questions in `findings.md`. +- Connected to the user's logged-in Chrome session via CDP and read CloudClaw source pages. +- Confirmed current CloudClaw backend is the OpenClaw cloud sync MVP and does not yet include enterprise app/skill distribution routes. +- Added desktop-side configurable config/app manifest endpoint support and app-shaped manifest normalization. +- Added missing manifest fallback so `/skills/manifest` 404 stores an empty local registry instead of surfacing an operator-facing crash. +- Added the CloudClaw `openclaw-cloud-sync` plugin as a 智念内置 OpenClaw plugin mirror: + - source mirror: `resources/openclaw-plugins/cloud-sync` + - dev mirror: `build/openclaw-plugins/cloud-sync` + - startup install target: `~/.openclaw/extensions/cloud-sync` +- Added startup config sync for `plugins.entries.cloud-sync.config.serverUrl`. +- Added static plugin copying to `scripts/bundle-openclaw-plugins.mjs` and `scripts/after-pack.cjs`. +- Adapted the plugin metadata/commands for the current OpenClaw runtime: + - `openclaw.extensions` is emitted as a string entry list. + - command registrations use `name` / `description` / `handler`. +- Verified: + - `pnpm run typecheck` + - `pnpm vitest run tests/unit/plugin-install.test.ts tests/unit/yinian-control-plane.test.ts` + - `pnpm run bundle:openclaw-plugins` + - `node node_modules/openclaw/openclaw.mjs plugins list | rg -n "cloud-sync|cloud_sync|Cloud Sync|failed|error" -C 2 || true` + +## 2026-04-26 Engineering Assessment Snapshot + +- Ran a testing-stage engineering assessment after the CloudClaw direct plugin integration. +- Current tracked diff is broad: 69 modified tracked files plus new YINIAN/product modules, docs, design system, Cloud Sync plugin mirror, and tests. +- Verification results: + - `pnpm run typecheck`: passed + - `pnpm run build:vite`: passed, with large renderer chunk warning (~1.6MB minified / ~481KB gzip) + - `pnpm run test`: failed; 88 test files passed, 2 failed; 584 tests passed, 5 failed + - focused E2E (`yinian-visual-smoke`, `main-navigation`, `app-smoke`): 4 passed, 1 failed +- Unit failures are mostly stale expectations after customer-facing terminology changed from Skills/registry to 应用/本机准备. +- Visual E2E failure is a real test-flow gap: production login now requires captcha input, but the visual smoke test still clicks login without filling account/password/captcha. +- Compatibility E2E for legacy setup/main navigation still passed. +- Cloud Sync plugin is loaded by `openclaw plugins list`, but remains infrastructure-only until its server URL/account contract is finalized. + +## 2026-04-26 Stabilization Pass + +- Fixed stale customer-facing terminology expectations in Today and Applications unit tests. +- Fixed the 智念 visual smoke login flow so it fills account/password, and relaxed captcha submit gating when mock mode has no captcha image. +- Added Cloud Sync startup config tests: + - server URL derivation from explicit env, API base URL, and default. + - plugin entry writing before Gateway launch. + - disabled mode skips install/config writes. +- Added knowledge-base import boundary tests: + - missing files are rejected without registry writes. + - newer imports are listed first. + - invalid `.docx` files are rejected before entering the registry. +- Verification: + - `pnpm run typecheck`: passed + - `pnpm run test`: passed, 90 files / 595 tests + - `pnpm run build:vite`: passed, with existing chunk-size and dynamic/static import warnings + - `pnpm run test:e2e`: passed, 27 passed / 1 skipped + +## 2026-04-26 Product Simplification Pass + +- Removed unsupported account/service profile signals from customer-facing UI: + - service city / region + - account role / position + - role labels in sidebar and Settings +- Removed unsupported dashboard signals: + - `运营健康度` + - fake business pending metrics + - recent application run traces derived from mock placeholders + - notification-method count from the dashboard +- Reworked Today into a lighter application readiness dashboard: + - opened apps + - locally ready apps + - pending sync + - sync failures + - service/account summary +- Reworked login side panel from numeric marketing claims into stable product areas: + - account login + - application delivery + - knowledge base +- Simplified `YinianHotel` in `shared/yinian.ts` to service identity only: `id`, `name`, optional `brand`. +- Updated mock control-plane, contract fixtures, tests, and pilot QA docs from hotel/OTA samples to generic enterprise service/application samples. +- Verification: + - `pnpm run typecheck`: passed + - focused YINIAN unit tests: passed, 5 files / 37 tests + - `pnpm run test`: passed, 90 files / 595 tests + - `pnpm run build:vite`: passed, with existing Vite warnings + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts`: passed + +## 2026-04-26 Interaction And Knowledge Fixes + +- Stabilized the sidebar history popover with delayed close handling so it is less likely to close while moving across the New Chat / History area. +- Added saved account/password support on the login page: + - renderer loads saved credentials on login page mount. + - main process stores them in the YINIAN namespace. + - password is encrypted with Electron `safeStorage` when available, with a dev/test fallback. +- Changed knowledge-base chat context from file-path references to actual local-backup content injection through `/api/knowledge/context`. +- Added tests proving a knowledge file still works after the original source file is deleted. +- Updated user-facing `服务空间` language to `组织空间`. +- Removed the preset quick-question buttons from the empty chat screen. +- Fixed the `gateway-ready-fallback` unit test so it no longer tries to start a real Gateway while fake timers are active. +- Verification: + - `pnpm run typecheck`: passed + - focused unit tests: passed, 5 files / 42 tests + - `pnpm run test`: passed, 90 files / 598 tests + - `pnpm run build:vite`: passed, with existing Vite warnings + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts`: passed + +## 2026-04-26 Chat Layout Responsiveness Fix + +- Fixed chat page sizing after sidebar collapse: + - main content now allows flex children to shrink/grow with `min-w-0`. + - chat page no longer relies on negative margins and viewport-height calculation. + - chat messages and composer now use a wider responsive max width. + - knowledge context popover is capped for narrow windows. +- Electron layout verification at 1120x760: + - expanded sidebar: main 864px, composer 816px. + - collapsed sidebar: main 1056px, composer 1008px. +- Verification: + - `pnpm run typecheck`: passed + - focused chat unit tests: passed, 2 files / 4 tests + - `pnpm run build:vite`: passed, with existing Vite warnings + +## 2026-04-26 Settings Tabs Cleanup + +- Reworked Settings into tabbed sections: + - 账号与组织 + - 使用偏好 + - 运行维护 + - 高级工具 + - 关于 +- Kept server-managed capability messaging under account/organization settings. +- Moved advanced diagnostics behind the 高级工具 tab while keeping the unlock switch in 运行维护. +- Removed unrelated external jump buttons from About, including website, GitHub, and FAQ links. +- Verification: + - `pnpm run typecheck`: passed + - `pnpm run build:vite`: passed, with existing Vite warnings + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts`: passed + +## 2026-04-27 Settings Information Architecture Update + +- Reduced Settings top-level tabs to: + - 账号与组织 + - 使用偏好 + - 运行维护 + - 更新与关于 +- Compact account/organization layout: + - tightened the organization card spacing. + - moved account name into a compact inline badge. + - changed service capabilities to show only 渠道管理. + - hid AI service, assistant capability, and automatic-task strategy cards. +- Limited language options to 中文 and English in 使用偏好. +- Folded advanced tools into 运行维护. +- Merged About details into 更新与关于. +- Verification: + - `pnpm run typecheck`: passed + - `pnpm run build:vite`: passed, with existing Vite warnings + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts`: passed + +## 2026-04-27 Settings Channel Management Expansion + +- Expanded 渠道管理 in Settings from a single service-capability row into a full management summary: + - loads `/api/channels/accounts`. + - displays configured channel count, channel-account count, and connected-account count. + - lists each configured channel, group status, default account, per-account status, binding target, and last error. + - adds refresh and 完整管理 actions; 完整管理 navigates to `/channels`. +- Kept the compact account/organization layout while making channel status visible without leaving Settings. +- Verification: + - `pnpm run typecheck`: passed + - `pnpm exec vitest run tests/unit/channel-routes.test.ts`: passed, 23 tests + - `pnpm run build:vite`: passed, with existing Vite warnings + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts`: passed + +## 2026-04-27 Settings Channel Module Integration + +- Promoted 渠道管理 to its own Settings tab instead of showing it as a secondary service capability. +- Embedded the existing full Channels management surface in Settings: + - configured channels + - supported channels + - add/edit/delete account flows + - agent binding controls + - gateway health diagnostics +- Removed the 服务能力 module from 账号与组织. +- Merged account and update settings into 账号与更新. +- Reduced About to a bottom footnote inside 账号与更新 without a large section title. +- Verification: + - `pnpm run typecheck`: passed + - `pnpm exec vitest run tests/unit/channel-routes.test.ts`: passed, 23 tests + - `pnpm run build:vite`: passed, with existing Vite warnings + - `pnpm exec playwright test tests/e2e/yinian-visual-smoke.spec.ts`: passed + +## 2026-05-05 Project Progress Update For Version 3.5 + +- Updated planning status for the next phase: 3.5 will focus on integrating one large application inside 应用中心. +- Current product baseline before 3.5: + - 登录 and server auth flow are connected. + - 组织空间/service context is simplified to one account mapping to one B-end service. + - 侧边栏 is focused around Dashboard, 快速使用, 对话, and 设置. + - 应用中心 exists as a compact launcher with tag filtering and built-in app entries. + - 能力包 is separated from 应用中心 and owns service-issued/local skills plus 快捷任务配置. + - 快捷任务 now injects `使用{能力包名称} skill` into the actual user message, is single-select, and is single-use per send. + - Knowledge Base v0 locally backs up imported files and can inject selected file text into chat context. + - Dev server port is now `5188`, avoiding collisions with other projects using `5173`. +- Added a new `Version 3.5: Application Center Large-App Integration` section to `task_plan.md`. +- Added application-center readiness notes to `findings.md`. +- No code was changed for the large application in this update; this was a planning/progress synchronization step. + +## 2026-05-05 NianxxPlay Large-App Review + +- Located the candidate large app at `/Users/inmanx/Documents/NianxxPlay`. +- Current NianxxPlay scope: + - `NianxxPlay智念视频助手` + - Seedance 2.0 video generation workspace + - creation modes: 宣传片制作 and 创意复刻 + - template-first studio, asset upload, prompt assembly, project history, billing/credits, Seedance task APIs +- Engineering shape: + - Next.js 15 App Router project. + - Not a git repository locally. + - Uses local MVP persistence through `.data/app-state.json`. + - Uses Aliyun OSS and Seedance/Ark API environment configuration for real generation. +- Verification: + - `npm test`: passed, 3 files / 11 tests. + - `npm run build`: passed. + - Known build warning remains in the `ali-oss` dependency chain and does not currently block build. +- 3.5 integration direction: + - Treat NianxxPlay as the first large application for 应用中心. + - Prefer embedded-web hosting in 智念助手 first, because NianxxPlay owns full routes, API routes, storage, upload, and generation workflow. + - Next implementation should build the 智念 large-app host route and then connect a local NianxxPlay URL for internal testing. + +## 2026-05-05 Bundled Runtime Assessment For NianxxPlay + +- Evaluated the clarified target: NianxxPlay should be installed, updated, launched, and maintained together with the 智念助手 desktop app. +- Recommended engineering direction: + - Build NianxxPlay as a Next.js standalone runtime. + - Bundle it into 智念助手 `extraResources`. + - Install/copy it into a desktop-managed user runtime directory. + - Launch it from Electron main process on a local loopback port. + - Render it inside 应用中心 through an embedded app host route. +- Existing desktop precedent: + - bundled OpenClaw already follows the same build -> extraResources -> managed runtime -> process lifecycle model. + - NianxxPlay should reuse this lifecycle style rather than asking customers to install development dependencies. +- Key risks logged: + - NianxxPlay project root is about 1GB in development form. + - starter media assets alone are about hundreds of MB. + - `.env.local` contains real credentials and must never be shipped. + - local data/upload paths currently rely on `process.cwd()` and must move to user-writable app-managed directories. + - the current `localhost:3000` assumption must become dynamic-port aware. +- Updated `task_plan.md` with a new `Version 3.5: Bundled NianxxPlay Runtime` section. +- Updated `findings.md` with size, runtime, security, and packaging findings. + +## 2026-05-05 App Center Focus Pass + +- Removed the small demo applications from 应用中心: + - calculator + - quick note + - file converter + - web assistant + - help center +- Deleted calculator-specific route/code/test files: + - `src/pages/Calculator/index.tsx` + - `src/lib/calculator.ts` + - `tests/unit/calculator.test.ts` +- Added the single NianxxPlay application entry: + - id: `nianxx-play` + - route: `/app-center/nianxx-play` + - icon: `Clapperboard` + - category: 视频创作 +- Added `src/pages/NianxxPlay/index.tsx` as the first large-app host shell: + - back, reload, and browser-open controls + - embedded local app frame + - service-managed account/permission/secret/billing boundary copy + - dev URL defaults to `http://127.0.0.1:3000` and can be overridden with `VITE_NIANXX_PLAY_URL` +- Updated app-center Chinese/English copy around the single-app focus and server-managed boundaries. +- Verification: + - `pnpm run typecheck`: passed + - `pnpm run build:vite`: passed, with existing Vite warnings + - focused vitest command: passed, 1 file / 3 tests + +## 2026-05-06 NianxxPlay Bundled Runtime Implementation + +- Implemented the first packaging pass for 应用中心 large-app distribution. +- NianxxPlay changes: + - enabled Next.js standalone output in `next.config.ts`. + - added `/api/desktop/health` for desktop runtime identity checks. + - moved local state, uploads, and generated-result fallback paths behind env-driven runtime directories. + - added local file routes for `/uploads/*` and `/generated-results/*` so packaged runtime can serve user-writable files outside app resources. +- Desktop changes: + - added `scripts/prepare-nianxx-play-bundle.mjs`. + - added `pnpm run prepare:nianxx-play`. + - wired NianxxPlay bundling into `package` and `build`. + - prepared `build/apps/nianxx-play` as a Next.js standalone bundle for electron-builder. + - excluded `.env*`, `.data`, local uploads, and local generated results from the shipped bundle. + - updated Electron NianxxPlay service manager to detect source vs standalone runtime. + - packaged standalone runtime now launches via Electron's own Node runtime with `ELECTRON_RUN_AS_NODE`, avoiding customer Node/npm dependency. + - runtime data is injected under the desktop app userData directory. + - existing local NianxxPlay `.data`, uploads, and generated results are migrated once into the desktop-managed runtime directory when empty. + - startup health check now validates the NianxxPlay marker endpoint, so a random service on port 3000 is not mistaken for the app. + - Vite dev watch now ignores built-in app bundles to avoid reload spam while preparing large app resources. +- Verification: + - NianxxPlay `npm test`: passed, 3 files / 11 tests. + - NianxxPlay `npm run build`: passed, with the existing ali-oss dynamic dependency warning. + - `pnpm run prepare:nianxx-play`: passed. + - standalone smoke test with Electron Node mode on port 3317: passed for health endpoint and homepage. + - confirmed no `.env*`, `.data`, `public/uploads`, or `public/generated-results` entries remain in the prepared bundle. + - removed local absolute source paths from the shipped bundle manifest. + - added a secret-value guard to the NianxxPlay bundle script: it scans sensitive-looking values from source env files and fails packaging if any are compiled into the shipped text bundle. + - added an internal-testing packaging path: + - `pnpm run prepare:nianxx-play:pilot` + - `pnpm run package:pilot` + - `pnpm run package:mac:pilot` + - pilot packaging writes `.env.runtime` into the bundled NianxxPlay runtime and Electron injects those values when launching the companion app. + - desktop `pnpm run typecheck`: passed. + - desktop `pnpm run build:vite`: passed, with existing Vite chunk warnings. + - desktop dev server restarted on `http://localhost:5188/`. + +## 2026-05-06 Pilot Package Issue Triage + +- Reviewed customer screenshots and `/Users/inmanx/Desktop/clawx-2026-05-06.log`. +- Found three packaging/runtime blockers: + - macOS reported `clipboard.darwin-arm64.node` as unverifiable because the shipped app is Developer ID signed but not notarized/stapled. + - NianxxPlay failed immediately with `Cannot find module 'next'` because electron-builder skipped `build/apps/nianxx-play/node_modules` when copying extraResources. + - Packaged mac build lacked bundled `uv`, causing managed Python setup to fail with `spawn uv ENOENT`. +- Applied fixes: + - `prep:mac-binaries` now downloads both uv and Node/npm. + - afterPack now manually copies NianxxPlay `node_modules` into the packaged resources. + - afterPack removes optional `@mariozechner/clipboard*` native packages from OpenClaw packaging so clipboard fallback uses `pbcopy` instead of loading a native `.node`. + - CLI symlink refresh now removes broken symlinks with `rmSync(..., { force: true })` before recreating. +- Follow-up validation: + - `pnpm run typecheck`: passed. + - `pnpm run build:vite`: passed, with existing Vite chunk warnings. + - `pnpm run prep:mac-binaries:arm64`: passed; bundled arm64 `node` and `uv` are present. + - NianxxPlay pilot runtime env contains the required Seedance/OSS keys without printing secret values. + - Manual afterPack mutation of the already-built release app was used only for diagnosis; it invalidates the local app seal and must not be shipped without a fresh electron-builder packaging/signing pass. + +## 2026-05-06 Mac arm64 Pilot Package Rebuild + +- Rebuilt the internal-testing mac arm64 package with: + - bundled arm64 `node` + - bundled arm64 `uv` + - NianxxPlay standalone runtime env + - afterPack NianxxPlay `node_modules` copy + - optional native clipboard package removal +- Generated arm64 artifacts: + - `release/智念助手-0.1.0-mac-arm64.dmg` + - `release/智念助手-0.1.0-mac-arm64.zip` +- Copied the customer-facing dmg to the desktop: + - `/Users/inmanx/Desktop/智念助手-0.1.0-mac-arm64-内测修复版.dmg` +- Verification: + - packaged app contains NianxxPlay `node_modules/next`. + - packaged app contains `Contents/Resources/bin/node` and `Contents/Resources/bin/uv`. + - optional `@mariozechner/clipboard*` native package is absent. + - `codesign --verify --deep --strict` passes for the arm64 app. + - `hdiutil verify` passes for the arm64 dmg. + - packaged NianxxPlay server starts from the app bundle and `/api/desktop/health` returns ok with Seedance and OSS services configured when `.env.runtime` is loaded. +- Remaining distribution caveat: + - electron-builder skipped notarization because notarize options could not be generated in the current shell; `spctl` still reports `Unnotarized Developer ID`. + - The script still starts redundant x64 packaging from the shared mac target config; this run was interrupted after complete arm64 artifacts were produced. + +## 2026-05-06 Long Task + Model Diagnostics Fix + +- Fixed the chat running-state regression where Gateway `phase=end` notifications were treated as terminal completion. + - `phase=end` now refreshes history only and keeps `sending` / `activeRunId` intact. + - terminal phases remain `completed`, `done`, and `finished`. +- Added Yinian model runtime diagnostics and repair helpers: + - enforces the internal model timeout at 300s. + - disables OpenClaw startup pricing catalog fetches with `models.pricing.enabled=false`. + - normalizes MiniMax auth profile aliases so stale `minimax-cn:default` credentials also satisfy the `minimax/...` model reference. +- Added a Host API endpoint: + - `GET /api/diagnostics/model-config` + - `GET /api/diagnostics/model-config?repair=1` +- Added an admin-only Settings > 运行维护 card for AI service configuration checks. It shows default model, provider state, credential presence, runtime flags, and config paths without exposing secrets. +- Loosened mac/Linux Gateway heartbeat recovery thresholds to match the more forgiving Windows thresholds: 60s interval, 25s timeout, 5 misses. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm run build:vite`: passed with existing Vite chunk warnings. + - targeted tests passed: + - `tests/unit/gateway-events.test.ts` + - `tests/unit/model-diagnostics.test.ts` + - `tests/unit/diagnostics-routes.test.ts` + - `tests/unit/gateway-manager-heartbeat.test.ts` + - Full `pnpm test` still has unrelated existing failures in stale page/test expectations (`yinian-skills-page`, `today-page`, `chat-session-actions`, `task-visualization`, `language-detection`). + +## 2026-05-06 Optional Native Clipboard Gatekeeper Fix + +- Investigated repeated customer macOS security prompt for `clipboard.darwin-arm64.node`. +- Found the current `release/mac-arm64` app no longer contained the clipboard native package, but `build/openclaw` and user-managed OpenClaw directories could still retain `@mariozechner/clipboard*` packages. +- Applied a three-layer fix: + - `scripts/bundle-openclaw.mjs` now skips `@mariozechner/clipboard*` during dependency collection and defensively removes any residual package before writing the runtime marker. + - `electron/utils/paths.ts` now bumps the managed runtime patch marker to `2026-05-06-npm-runner-clipboard-v2`, forcing stale managed runtimes to reinstall from the cleaned bundled runtime. + - `electron/gateway/config-sync.ts` now cleans optional clipboard packages from user OpenClaw runtime/cache/extension dependency directories before Gateway startup. +- Added `electron/utils/optional-native-cleanup.ts` and `tests/unit/optional-native-cleanup.test.ts`. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/optional-native-cleanup.test.ts`: passed. + - `pnpm run build:vite`: passed with existing Vite chunk warnings. + - `pnpm exec zx scripts/bundle-openclaw.mjs`: passed. + - `find build/openclaw -name 'clipboard.darwin-arm64.node' -o -path '*@mariozechner/clipboard*'`: no output. + - `find release -name 'clipboard.darwin-arm64.node' -o -path '*@mariozechner/clipboard*'`: no output. + +## 2026-05-06 Complete Project Self Check Follow-up + +- Fixed stale unit-test expectations for the current product state: + - language detection now expects unsupported `ja_JP` to fall back to English because the visible product language set is Chinese/English. + - new chat session tests now expect desktop-first `agent:main:*` session keys. + - task visualization tests no longer expose internal thinking-only chunks as execution steps. + - Today page tests now cover `开始工作`, `常用应用`, `智念视频助手`, `定时任务`, `知识库`, and recent conversations. + - Ability-pack page tests now select top-level tabs precisely so quick-task skill-source labels do not collide with `服务下发` / `本地安装`. +- Verification: + - targeted failed files: passed, 5 files / 29 tests. + - `pnpm test`: passed, 93 files / 620 tests. + - `pnpm run typecheck`: passed. + - `pnpm run package:pilot`: passed. +- Removed `tavily-search` from `resources/skills/preinstalled-manifest.json` and regenerated `build/preinstalled-skills`. +- Resource checks: + - `tavily` absent from preinstalled manifest and generated preinstalled bundle. + - optional native clipboard package absent from `build/openclaw` and `release`. + - NianxxPlay bundle contains the standalone server, Next runtime, and internal `.env.runtime`. + - NianxxPlay bundle scan only reports `.env.runtime`; no uploads, generated results, or Next cache directories. +- Remaining warnings: + - Vitest still emits `MaxListenersExceededWarning`. + - Vite still warns about large chunks and mixed static/dynamic imports. + - OpenClaw bundling still skips several patch snippets that are not present in the current upstream package. + - NianxxPlay bundle remains heavy at about 419 MB. + +## 2026-05-09 Office Skills Runtime Hardening + +- Started consolidation after replacing old office skills with MiniMax versions. +- Added an explicit task plan and findings entry so this does not remain a one-off local copy change. +- Added `electron/utils/office-skill-runtime.ts` to check and repair the common office runtime layer: + - managed Python 3.12 plus `pypdf`, `reportlab`, `pandas`, `openpyxl`, `matplotlib`, `beautifulsoup4`. + - project/bundled Node modules for PPT generation. + - `.NET` presence as a visible warning for advanced docx flows. +- Wired office runtime preparation into first-run initialization and Gateway warmup. +- Added admin diagnostics endpoint and Settings > 运行维护 UI for office runtime checks/repair. +- Hardened the MiniMax skill bundler so `docx`, `pdf`, `pptx`, and `xlsx` use stable local slugs and avoid global install guidance. +- Fixed the model runtime repair path so it no longer writes OpenClaw-invalid `models.pricing` or provider `timeoutSeconds` fields. +- Cleaned the local `~/.openclaw/openclaw.json` and kept `agents.defaults.heartbeat.every = "0m"` to disable heartbeat prompts. +- Verification: + - `pnpm run bundle:preinstalled-skills`: passed. + - managed Python imports for office packages: passed. + - `openclaw config validate`: passed. + - `openclaw skills list --json`: `docx`, `pdf`, `pptx`, `xlsx`, and `design` enabled. + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/model-diagnostics.test.ts tests/unit/diagnostics-routes.test.ts`: passed. + - `pnpm test`: passed, 93 files / 622 tests. + - `pnpm run build:vite`: passed with existing Vite warnings. + +## 2026-05-09 Local .NET SDK Install + +- Installed .NET SDK 10.0.203 into the user-local runtime path `~/.dotnet` using Microsoft's install script because Homebrew cask installation requires interactive `sudo`. +- Added `DOTNET_ROOT="$HOME/.dotnet"` and `~/.dotnet` to `~/.zprofile` so new zsh sessions can resolve `dotnet`. +- Added desktop runtime path support so Electron/OpenClaw child processes also see user-local `.NET`: + - `electron/utils/dotnet-runtime.ts` + - office runtime diagnostics use the resolved `.NET` executable. + - Gateway launch and doctor-repair environments include the `.NET` path. +- Verification: + - `zsh -lc 'dotnet --version'`: `10.0.203`. + - `pnpm run typecheck`: passed. + - `pnpm run build:vite`: passed with existing Vite warnings. + +## 2026-05-09 Office Skill Prompt Cleanup + +- Removed remaining desktop office-suite conversion/recalculation branches from generated office skills so the agent does not drift into asking for external office tools. +- `xlsx` now exposes static formula validation only through `formula_check.py`; the dynamic recalculation script is removed from the preinstalled bundle. +- `docx` legacy `.doc` conversion helper is removed from the preinstalled bundle; `.docx` creation/editing stays on the .NET OpenXML path. +- Regenerated `build/preinstalled-skills` and synced `docx`, `pdf`, `pptx`, and `xlsx` into `~/.openclaw/skills`. +- Added `.clawx-preinstalled.json` markers back to the local synced skills so future app startup treats them as app-managed preinstalled skills. +- Verification: + - `node --check scripts/bundle-preinstalled-skills.mjs`: passed. + - `pnpm run bundle:preinstalled-skills`: passed. + - scan of generated and local office skills found no `LibreOffice`, `libreoffice`, `soffice`, `Libra`, `libra`, `libreoffice_recalc`, or `doc_to_docx`. + - `openclaw skills list --json`: `docx`, `pdf`, `pptx`, `xlsx`, and `design` enabled. + - `pnpm run typecheck`: passed. + +## 2026-05-09 Assistant Output Sanitizer + +- Added a renderer-side assistant output sanitizer for external office-suite guidance that may appear in old history or model-invented final replies. +- Wired the sanitizer into both history hydration and live streaming/final message handling. +- It removes user-visible `LibreOffice` / `soffice` / `Libra` / technical conversion-engine advice while preserving generated file paths, attachments, and tool blocks. +- Verification: + - `pnpm vitest run tests/unit/assistant-output-sanitizer.test.ts`: passed. + - `pnpm run typecheck`: passed. + - `pnpm run build:vite`: passed with existing Vite warnings. + +## 2026-05-12 Customer Clean Install Stabilization + +- Read the customer logs from both WeChat paths. +- Confirmed the install skipped setup because app state said `setupComplete=true`, even though runtime files were incomplete. +- Confirmed two hard runtime blockers: + - OpenClaw templates were removed from the customer package. + - Managed runtime lacks a self-reference for bare `openclaw/...` package imports. +- Started a focused stabilization plan in `task_plan.md`. +- Fixed OpenClaw packaging cleanup so `docs/reference/templates` survives while the rest of docs can still be pruned. +- Bumped the Yinian managed runtime patch marker to `2026-05-12-runtime-templates-selfref-v1`. +- Added managed runtime self-reference creation at install time so ESM imports like `openclaw/plugin-sdk/...` resolve under `~/.openclaw/runtime/openclaw`. +- Hardened setup status: + - validates runtime files, workspace, config, and initialization marker. + - resets stale `setupComplete=true` if those files are missing. + - renderer settings now reconciles `setupComplete` with `yinian:setup:status`. + - Gateway start is gated by verified setup plus authenticated Yinian session. +- Fixed NianxxPlay bundle filtering so compiled `generated-results` route code ships while `public/generated-results` user output remains excluded. +- Verification: + - `pnpm run typecheck`: passed. + - script syntax checks for after-pack / Nianxx bundle / OpenClaw bundle: passed. + - `pnpm exec zx scripts/bundle-openclaw.mjs`: passed, runtime templates verified. + - managed-runtime simulation with self-reference: `dist/extensions/codex/prompt-overlay.js` imports successfully. + - `pnpm run build:vite`: passed with existing Vite warnings. + - targeted tests passed: `model-diagnostics`, `diagnostics-routes`, `optional-native-cleanup`. + - `pnpm test`: passed, 94 files / 628 tests, with the existing MaxListeners warning. + - `pnpm run package:pilot`: passed. + - resource checks passed: OpenClaw templates present, NianxxPlay compiled generated-results route present, `public/generated-results` absent. + +## 2026-05-12 Clean Install Follow-up And Arm64 Pilot Package + +- Added internal-pilot model auth packaging and initialization seeding: + - `scripts/prepare-internal-model-auth.mjs` + - `resources/yinian-internal/model-auth-profiles.json` in pilot builds + - setup status now treats missing model auth as not initialized. +- Added OpenClaw package roots to office skill runtime dependency resolution. +- Re-ran verification: + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/model-diagnostics.test.ts tests/unit/diagnostics-routes.test.ts tests/unit/optional-native-cleanup.test.ts tests/unit/yinian-store.test.ts tests/unit/openclaw-auth.test.ts tests/unit/gateway-events.test.ts`: passed, 40 tests. + - `pnpm test`: passed, 94 files / 628 tests; existing MaxListeners warnings remain. + - `pnpm run package:pilot`: passed. + - managed runtime simulation with `node_modules/openclaw -> ..`: passed. +- Generated new Apple Silicon pilot installer: + - `release/智念助手-0.1.0-mac-arm64.dmg` (about 1.5 GB) + - copied to `/Users/inmanx/Desktop/智念助手-0.1.0-mac-arm64-20260512.dmg` +- Release resource checks passed: + - OpenClaw `AGENTS.md`, `TOOLS.md`, `HEARTBEAT.md` templates present. + - NianxxPlay generated-results route present. + - six preinstalled skills present. + - optional clipboard native module absent. + - no `*.app` directories in preinstalled skills. +- macOS signing status: + - Developer ID code signature verifies. + - Notarization was skipped by electron-builder due missing notarize options, so Gatekeeper still reports `Unnotarized Developer ID`. + +## 2026-05-12 NianxxPlay Demo Asset And Runner Fix + +- Fixed the NianxxPlay "生成示例" sample video path: + - moved the Wujiang reference video/cover into formal built-in starter assets under `public/starter/promo/`. + - updated `starter-content.ts` and `content/seedance-starter/catalog.json` to reference `/starter/promo/wujiang-reference.mp4` and `.jpg`. + - kept `public/generated-results` excluded from desktop bundles, so user generated history remains out of customer packages. +- Fixed the visible extra Dock process risk: + - NianxxPlay standalone now prefers the bundled Node binary in `Resources/bin/node`. + - it only falls back to `process.execPath + ELECTRON_RUN_AS_NODE` if bundled Node is missing. +- Verification: + - NianxxPlay `npm test`: passed, 3 files / 12 tests. + - NianxxPlay `npm run build`: passed with existing `any-promise/register` warning. + - desktop `pnpm run typecheck`: passed. + - desktop `pnpm run build:vite`: passed with existing Vite warnings. + - `NIANXX_PLAY_BUNDLE_ENV=1 node scripts/prepare-nianxx-play-bundle.mjs`: passed. + - bundle/release resource scans confirm the starter mp4/jpg are included and `public/generated-results` / `public/uploads` remain excluded. + - `pnpm run package:mac:pilot:arm64`: passed. + - `codesign --verify --deep --strict`: passed; notarization still not configured. +- Deliverable: + - `/Users/inmanx/Desktop/智念助手-0.1.0-mac-arm64-20260512-video-node.dmg` + +## 2026-05-12 Design And HTML Slides Skill Split + +- Upgraded the app-bundled `design` skill into a general design director/design-system capability. +- Added a new app-bundled `html-slides` skill for browser-based presentation decks and PPTX-to-HTML conversion. +- Copied `beautiful-html-templates` `index.json`, `templates/`, and `runtime/` into `html-slides` references; screenshots remain excluded. +- Copied lightweight `frontend-slides` references: viewport base CSS, animation patterns, PPTX extraction script, and license. +- Updated `resources/skills/preinstalled-manifest.json` with new versions and `html-slides` auto-enable. +- Ran `pnpm run bundle:preinstalled-skills`; build output now includes `design` and `html-slides` with updated lock entries. +- Synced local app-managed test skills into `~/.openclaw/skills/design` and `~/.openclaw/skills/html-slides`. +- Verification: + - design database smoke command passed. + - `extract-pptx.py` py_compile passed for build and local copies. + - OpenClaw lists `design` and `html-slides` as eligible and enabled. + - resource scans found 32 bundled templates, no screenshots, no `.app` directories, and license files present. +- a temporary 8-slide HTML deck passed 1280x720, 1440x900, and 390x844 no-overflow checks. + +## 2026-05-13 Task And Skill Management Clarification + +- Started a new implementation pass for the user's request: + - move ability-pack quick-task management back into the ability-pack area. + - preserve the older scheduled-task creation design, including push channel selection. + - add `@` skill insertion support in task editing. +- Restored planning context from existing `task_plan.md`, `findings.md`, and `progress.md`; session catchup reported no additional unsynced output. +- Confirmed local git history at `0abc481` had the ability-pack quick-task tab as the default skills page surface. +- Confirmed the older Cron page still had the push-channel design: delivery mode, channel/account/target selection, and `delivery` payload fields. +- Implemented the product clarification: + - ability-pack page exposes quick-task management again and supports editing existing quick tasks. + - user-facing copy now calls this concept “快捷能力” / “Quick Capability” instead of “快捷任务” to avoid collision with Task Center tasks. + - Task Center quick tab is now a run-now surface and points users to ability packs for quick-capability management. + - Task Center scheduled-task dialog preserves push delivery configuration. + - manual and scheduled task editors support `@` skill insertion and insert the existing skill trigger phrase. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/yinian-skills-page.test.tsx tests/unit/tasks-page.test.tsx`: passed, 9 tests. + - `pnpm test`: passed, 97 files / 638 tests; existing MaxListeners warnings remain. +- Browser visual verification was attempted through the in-app browser twice, but the browser connection timed out before a page snapshot could be captured. The running Vite/Electron dev server continued HMR updates without compile errors. + +## 2026-05-13 Playwright Chromium Runtime Hardening + +- Started hardening after customer confirmed a stuck `npx playwright install chromium` process was killed by stuck recovery. +- Added `electron/utils/playwright-runtime.ts` for app-scoped Playwright browser cache and install-lock paths. +- Gateway launch env now sets `PLAYWRIGHT_BROWSERS_PATH`, `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD`, and `YINIAN_PLAYWRIGHT_INSTALL_LOCK_PATH` for embedded tasks. +- The app-scoped Playwright cache now best-effort symlinks already-complete Chromium installs from the default Playwright cache, so existing browsers remain usable without re-downloading. +- Office runtime diagnostics now include a warning-level `playwright-chromium` check showing whether Playwright and the Chromium executable are available. +- Updated `html-slides` runtime policy and preinstalled skill bundling sanitation to forbid task-time `npx playwright install chromium`. +- Bumped preinstalled `pdf` and `html-slides` versions so app-managed skill sync can pick up the new guardrails. +- Regenerated `build/preinstalled-skills`. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm run bundle:preinstalled-skills`: passed. + - `pnpm vitest run tests/unit/playwright-runtime.test.ts`: passed, 2 tests. + - Real Playwright probe with `PLAYWRIGHT_BROWSERS_PATH=~/.openclaw/runtime/ms-playwright`: Chromium executable exists after symlink mirroring. + +## 2026-05-13 Cron Run Final-State Reconciliation + +- Investigated the reported case where a task showed failure while the desktop artifact existed. +- Confirmed the local "写笑话" cron run produced `/Users/inmanx/Desktop/joke.docx`, while the cron run summary still reported `status: error` from an intermediate edit-tool failure. +- Confirmed the same run's trajectory artifact recorded `finalStatus: success` and the final assistant success text. +- Updated Task Center cron job transformation to reconcile failed cron summaries against the corresponding run trajectory: + - if the trajectory final status is successful, the last run is exposed as successful. + - the stale intermediate tool error is preserved only as a warning/reconciliation note. + - cron session fallback messages use the recovered final assistant summary instead of a false failed message. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/chat-history-actions.test.ts tests/unit/cron-routes.test.ts tests/unit/tasks-page.test.tsx tests/unit/cron-store-fetch-dedupe.test.ts`: passed, 32 tests. + - `pnpm test`: passed, 98 files / 652 tests; existing MaxListeners warnings remain. + +## 2026-05-13 Cron Conversation Live Refresh + +- Fixed the case where a Task Center run did not show this run's query or response hint until manual refresh. +- Backend cron fallback history now emits the task query as a visible user message instead of a hidden system message. +- Backend running/empty fallback states now emit visible assistant messages. +- Manual Task Center triggers now optimistically append the task query and a running assistant placeholder to the fixed task conversation, keep the conversation in active-running state, then refresh the conversation after the trigger completes. +- Gateway agent events for isolated cron run sessions like `agent:main:cron::run:` are folded back into the fixed task conversation `agent:main:cron:`. +- Cron completion events force a foreground history refresh for the fixed task conversation so the current run appears without manual refresh. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/gateway-events.test.ts tests/unit/cron-store-fetch-dedupe.test.ts tests/unit/cron-routes.test.ts tests/unit/tasks-page.test.tsx`: passed, 25 tests. + - `pnpm vitest run tests/unit/chat-history-actions.test.ts tests/unit/chat-session-actions.test.ts tests/unit/chat-runtime-event-handlers.test.ts tests/unit/gateway-events.test.ts tests/unit/cron-store-fetch-dedupe.test.ts tests/unit/cron-routes.test.ts tests/unit/tasks-page.test.tsx`: passed, 61 tests. + - `pnpm test`: passed, 98 files / 655 tests; existing MaxListeners warnings remain. + +## 2026-05-13 Sidebar Navigation Restructure + +- Reworked the left sidebar into clearer zones: + - Home/Today remains the first entry. + - Conversation now sits directly under Home/Today in its own container, with New Chat and History. + - Task Center is followed by pinned quick task triggers. + - Only five pinned quick tasks are shown in the sidebar. + - App Center and Knowledge now live in the footer utility group above Settings. +- Added `tests/unit/sidebar-layout.test.tsx` to lock the requested order and pinned-task cap. +- Started a Vite dev server for review at `http://127.0.0.1:5199/` after the existing `5188` endpoint returned `502`. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/sidebar-layout.test.tsx`: passed, 1 test. + - `pnpm vitest run tests/unit/sidebar-layout.test.tsx tests/unit/tasks-page.test.tsx tests/unit/task-center-store.test.ts`: passed, 7 tests. + - `pnpm run build:vite`: passed with existing Vite chunk/dynamic-import warnings. + - `pnpm test`: passed, 99 files / 656 tests; existing MaxListeners warnings remain. + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts`: passed, 1 test. + +## 2026-05-13 App-Scoped Local Preference Recovery + +- Investigated why quick capabilities and account remarks appeared to fail. +- Found the existing data was still present under the old renderer origin `http://localhost:5173`, while the app had been started on a new dev origin. The localStorage origin shift made the data invisible rather than deleting it. +- Restarted the dev app on `http://localhost:5173` for immediate recovery. +- Added an Electron app-level `local-preferences` store and `/api/local-preferences` Host API route. +- Quick capabilities now: + - hydrate from app-level preferences when the current origin has no local data. + - push existing origin-local data into app-level preferences on startup. + - save future add/edit/delete/toggle changes to the app-level copy. +- YINIAN local preferences now sync desktop user name, workspace display name, and channel account remarks through the app-level preference store. +- Confirmed migrated data includes: + - quick capabilities: `写word文章`, `做网页`. + - account remarks: `徐明微信`, `小石的微信`, `徐明飞书`, `宗琦A2A`. +- Verification: + - `pnpm run typecheck`: passed. + - `pnpm vitest run tests/unit/quick-tasks-store.test.ts tests/unit/yinian-local-prefs.test.ts`: passed, 4 tests. + - `pnpm vitest run tests/unit/yinian-skills-page.test.tsx tests/unit/tasks-page.test.tsx tests/unit/sidebar-layout.test.tsx tests/unit/quick-tasks-store.test.ts tests/unit/yinian-local-prefs.test.ts`: passed, 16 tests. + - `pnpm run build:vite`: passed with existing Vite chunk/dynamic-import warnings. + - `pnpm test`: passed, 101 files / 660 tests; existing MaxListeners warnings remain. + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts`: passed, 1 test. + +## 2026-05-13 商品中心 Web 应用接入 + +- Started implementation for adding “商品中心” to App Center. +- Confirmed target URL `https://ticket.nianxx.cn/` is reachable and appears embeddable based on response headers. +- Found current App Center only lists `nianxx-play`; `webview` item handling still opens the browser externally, so this task needs a real internal web host route. +- Added built-in Product Center app metadata, `/app-center/product-center` route, internal iframe host page, reload/external actions, and zh/en copy. +- Added Product Center launch URL builder with embed context, workspace/user context, and a future short-lived SSO ticket slot. +- Added focused unit tests for App Center routing and Product Center launch URL behavior. +- Browser plugin preview timed out twice while connecting to the in-app browser; switched to the existing Electron E2E smoke path and added Product Center assertions there. +- First E2E run failed because `dist` had not been rebuilt, so the launched Electron app did not include the new Product Center card. +- Second E2E run reached `product-center-page` but failed on locale-specific iframe title lookup; added `data-testid="product-center-frame"` and updated the assertion. +- Verification passed: + - `pnpm vitest run tests/unit/app-center.test.tsx tests/unit/product-center-url.test.ts` + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` + +## 2026-05-13 桌面任务提醒与业务回答渲染 + +- 将 cron 任务的 Gateway 终态通知接入 Electron 系统通知:完成、需复核、失败分别显示“任务已完成 / 任务需复核 / 任务需要处理”。 +- 通知点击会聚焦主窗口并导航到 `/tasks?task=`,任务中心会高亮目标任务。 +- 新增 `electron/utils/cron-desktop-reminder.ts`,覆盖 cron session 解析、运行去重、通知文案和路由生成。 +- 新增 `src/pages/Chat/business-answer.ts`,聊天消息会从“状态/依据/影响/下一步”等标签提取业务摘要,并在 assistant markdown 上方渲染摘要面板。 +- 更新隐藏业务回答准则,要求业务回复优先输出稳定段落标签,便于 UI 结构化渲染。 +- Verification passed: + - `pnpm vitest run tests/unit/cron-desktop-reminder.test.ts tests/unit/chat-message.test.tsx tests/unit/tasks-page.test.tsx tests/unit/business-guidance.test.ts` + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` +- Note: an initial E2E attempt timed out because it launched Electron while `build:vite` was rewriting `dist`; a clean rerun passed. + +## 2026-05-13 聊天页字体与 UI 统一 + +- 用户反馈对话字体和整体 UI 与其它页面不一致。 +- 使用 `ui-ux-pro-max` 取了一次设计建议,但最终以项目现有 `design-system/智念助手/MASTER.md` 为准:成熟 B 端工作台、系统中文字体、slate/navy、8px 圆角、少阴影。 +- 更新全局字体:`body` 使用 `-apple-system / BlinkMacSystemFont / PingFang SC / Microsoft YaHei / Segoe UI / sans-serif`,输入控件继承字体。 +- 聊天页外层改为智念标准面板;消息区域为浅 slate 背景;空态改成无框轻量状态,避免卡片套卡片。 +- 消息气泡、业务摘要面板、工具状态条、执行过程卡、文件/图片卡、输入框、快捷任务 chip、知识库选择器都收敛到同一套边框/底色/字号。 +- Visual QA: `test-results/yinian-visual/02-chat.png` 已重新生成并查看,聊天首屏字体和 UI 气质与工作台更一致。 +- Verification passed: + - `pnpm vitest run tests/unit/chat-message.test.tsx` + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` + - `pnpm exec playwright test --config=playwright.legacy.config.ts tests/e2e/yinian-visual-smoke.spec.ts` + +## 2026-05-13 旅游资源订购 Login-State Follow-Up + +- Renamed the Product Center surface to “旅游资源订购” in zh copy and “Travel Resource Ordering” in en copy. +- Checked `ticket.nianxx.cn` scripts and confirmed the embedded site stores its session token/user in localStorage keys `ticketService.token` and `ticketService.user`. +- Switched the embedded container from iframe to Electron `` with `partition="persist:yinian-travel-resource-ordering"` so the embedded web app keeps its own persistent localStorage/cookie state across route changes and app restarts. +- Added unit and E2E assertions for the persistent webview partition. +- Verification passed: + - `pnpm vitest run tests/unit/app-center.test.tsx tests/unit/product-center-url.test.ts tests/unit/product-center-page.test.tsx` + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` + +## 2026-05-13 快速视频创作 Rename And English Coverage + +- Renamed the former “智念视频助手” display surface to “快速视频创作” in Chinese copy. +- Updated English copy to “Quick Video Creation”. +- Added an English App Center unit test covering both built-in apps and checking that raw translation keys are not visible. +- Verification passed: + - `pnpm vitest run tests/unit/app-center.test.tsx tests/unit/product-center-page.test.tsx tests/unit/product-center-url.test.ts` + - `pnpm run typecheck` + - `pnpm run build:vite` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` + +## 2026-05-13 快速视频创作内置 Web 英文覆盖 + +- Investigated the remaining English coverage gap reported by the user. +- Confirmed the desktop wrapper copy is covered by `appCenter` i18n, while the embedded NianxxPlay Next app still contains hard-coded Chinese UI/content. +- Added a new planning section for passing desktop language into the embedded app and covering visible English text inside the embedded Web surface. +- Desktop NianxxPlay host now adds `zhinianLang=zh|en` to embedded URLs. +- NianxxPlay source now has an embedded English bridge that persists `zhinianLang`, translates visible text/attributes/default form examples, and keeps working after client-side page changes. +- Added focused tests for the desktop URL language parameter and the NianxxPlay translation helper. +- Extended the bridge with phrase-level coverage for the default live prompt so the English embedded studio first screen no longer exposes continuous Chinese copy. +- Spot-checked the English embedded studio and planning pages with Playwright; the studio first screen and planning page had no continuous Chinese matches after translation. Projects may still show user/history titles, but the system-generated “宣传片创作台” prefix is covered. +- Rebuilt the NianxxPlay standalone output and refreshed the desktop bundled runtime under `build/apps/nianxx-play`. +- Stopped the stale bundled NianxxPlay child process that was still listening on `127.0.0.1:3000`; the desktop host will start the refreshed bundle the next time Quick Video Creation is opened. +- Verification passed: + - `pnpm vitest run tests/unit/nianxx-play-url.test.ts tests/unit/app-center.test.tsx` + - `/Users/inmanx/Documents/NianxxPlay`: `npm test` + - `/Users/inmanx/Documents/NianxxPlay`: `npm run build` + - `NIANXX_PLAY_SKIP_BUILD=1 pnpm run prepare:nianxx-play` + - `pnpm run typecheck` + - `pnpm run build:vite` + - Playwright page probe against `http://127.0.0.1:3010/studio?zhinianEmbed=1&zhinianLang=en` + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` + +## 2026-05-13 WhatsApp 二维码生成检查 + +- 调查 WhatsApp 二维码链路:前端扫码 Modal、Host API `/api/channels/whatsapp/start`、主进程 `WhatsAppLoginManager`、QR PNG 渲染和 `channel:whatsapp-qr` 事件。 +- 用临时 HOME 拉起登录管理器,避免触碰真实 `~/.openclaw` 凭据;当前网络下 Baileys 对 `wss://web.whatsapp.com/ws/chat` 连续 `WebSocket Error ()`,直接 `ws` 连接也超时。 +- 新增 `electron/utils/whatsapp-proxy.ts`,让 WhatsApp WebSocket 使用应用代理设置,优先 `ALL_PROXY/SOCKS`,再用 HTTPS/基础代理,并支持 `web.whatsapp.com` 绕过规则。 +- 更新 `electron/utils/whatsapp-login.ts`:为 Baileys 注入 HTTP/SOCKS proxy agent,连接失败时保留最后错误并提示检查网络或高级设置代理;QR 渲染异常也会发出 error 事件。 +- 更新 `src/lib/host-api.ts` 和 `src/components/channels/ChannelConfigModal.tsx`:Host API unified 非 2xx 不再被吞掉,扫码启动失败时前端会恢复状态并显示错误。 +- 补充 WhatsApp 网络/代理失败的 zh/en/ja/ru 本地化提示。 +- 将 `socks-proxy-agent` 加入 OpenClaw runtime bundle 依赖列表。 +- 新增/更新测试: + - `tests/unit/whatsapp-proxy.test.ts` + - `tests/unit/host-api.test.ts` +- Verification passed: + - `pnpm run typecheck` + - `pnpm exec vitest run tests/unit/host-api.test.ts tests/unit/whatsapp-proxy.test.ts tests/unit/channel-routes.test.ts` + - `pnpm run build:vite` + +## 2026-05-13 OpenClaw 内核升级 + +- 用户要求更新 OpenClaw 内核。 +- 当前项目依赖为 `openclaw@2026.4.29`。 +- 查询 npm dist-tags:stable latest 为 `2026.5.7`,beta 为 `2026.5.12-beta.4`;本次按稳定版 latest 处理。 +- 已将根项目 `devDependencies.openclaw` 升级到 `2026.5.7`,并刷新 `pnpm-lock.yaml`。 +- 重新运行 `pnpm exec zx scripts/bundle-openclaw.mjs`,`build/openclaw/package.json` 和 `node build/openclaw/openclaw.mjs --version` 均确认内置版本为 `2026.5.7`。 +- 同步更新 `scripts/bundle-openclaw.mjs` 与 `electron/gateway/runtime-deps.ts` 中的新版生成代码匹配片段,覆盖 main-session restart recovery 和 stuck-session active abort 边界。 +- 检查新版 bundle:关键自定义边界仍在产物中,包括 `OPENCLAW_DISABLE_AGENTS_SKILLS`、`OPENCLAW_DISABLE_MAIN_SESSION_RESTART_RECOVERY`、`YINIAN_OPENCLAW_STUCK_ACTIVE_ABORT_MS` 和桌面 webchat fast raw model path。 +- 部分旧补丁片段在 2026.5.7 中已由上游结构变更/等价逻辑替代,bundle 日志仍会提示旧片段不存在;已逐项核对关键行为没有缺失。 +- Verification passed: + - `pnpm run typecheck` + - `node scripts/assert-electron-runtime-deps.mjs` + - `pnpm exec vitest run tests/unit/openclaw-cli.test.ts tests/unit/openclaw-proxy.test.ts tests/unit/channel-config.test.ts tests/unit/channel-routes.test.ts tests/unit/whatsapp-proxy.test.ts tests/unit/host-api.test.ts` + - `pnpm run build:vite` + - `pnpm test` (106 files / 675 tests; existing MaxListeners warnings remain) + - `pnpm exec playwright test tests/e2e/yinian-delivery-smoke.spec.ts` diff --git a/resources/context/AGENTS.clawx.md b/resources/context/AGENTS.clawx.md index 0018f13..c3b5ca8 100644 --- a/resources/context/AGENTS.clawx.md +++ b/resources/context/AGENTS.clawx.md @@ -1,9 +1,148 @@ -## 智念助手环境 +## 智念助手业务行为准则 -你是“智念助手”,面向企业用户的桌面端 AI 工作助手。请始终以“智念助手”的身份与用户沟通,不要自称 ClawX、OpenClaw、agent、main agent,也不要主动解释底层技术栈。 +你是“智念助手”,面向企业用户的桌面端 AI 员工工作台。你的核心任务不是闲聊,也不是替代客户已有的业务系统,而是用自然语言理解用户的问题,调度可用的行业能力包、工具、知识库和业务应用,帮助用户更快完成真实工作。 -当用户询问产品身份时,可以简洁回答:我是智念助手,一个帮助你处理工作、文档、知识和业务应用的桌面 AI 助手。 +请始终以“智念助手”的身份与用户沟通,不要自称 ClawX、OpenClaw、agent、main agent,也不要主动解释底层技术栈。如果必须解释运行机制,请使用普通用户能理解的说法,例如“后台服务”“能力包”“应用中心”“浏览器能力”“定时任务”等;不要把底层运行框架描述成产品本体。 -如果必须解释运行机制,请使用普通用户能理解的说法,例如“后台服务”“能力包”“应用中心”“浏览器能力”等;不要把底层运行框架描述成产品本体。 +当用户询问产品身份时,可以简洁回答:我是智念助手,一个通过通用 AI 交互语言调度行业专业能力包,帮助企业处理业务问题的桌面 AI 助手。 + +## 产品边界 + +- 智念助手不是酒店 PMS、渠道管理系统、收益管理系统、采购系统或财务系统的替代品。 +- 不要求用户迁移原有业务流程,不抢占原系统的数据主权。 +- 智念助手应作为现有系统之上的 AI 工作层:发现问题、整理信息、生成建议、执行重复任务、推送结果,并在需要时引导用户回到原业务系统确认或处理。 +- 对涉及房态、价格、库存、订单、退款、财务等关键业务数据的操作,必须保持谨慎、可解释、可确认。 + +## 解决问题优先 + +用户提出业务问题时,优先判断“用户真正想解决什么”,而不是先解释产品功能。回答应尽量走向可执行结果: + +1. 识别业务场景,例如房态、价格、渠道、订单、退款、报表、客诉、采购、对账。 +2. 判断是否有合适的能力包、工具、知识库或业务应用可以使用。 +3. 如果信息不足,先补齐最少必要参数,例如日期、房型、渠道、门店/酒店、执行范围、目标价格、报表周期。 +4. 能执行就执行;不能直接执行时,给出明确的下一步、所需授权或需要用户提供的数据。 +5. 输出结论时要包含证据、影响范围和建议动作,避免只给泛泛建议。 + +## 行业能力包优先 + +智念助手的关键价值是“通用 AI 员工交互语言 + 行业专业能力包”。当用户描述酒店或企业运营问题时,应优先考虑是否应该调用或建议使用行业能力包,而不是只用通用回答。 + +常见意图与能力方向: + +- 房态不一致、库存异常、渠道不可售:优先考虑房态管理、渠道数据诊断类能力。 +- 改价、调价、价格同步、节假日价格策略:优先考虑全渠道改价、价格策略诊断类能力。 +- 昨日经营、日报、周报、老板汇报:优先考虑自动数据报表与处理类能力。 +- 渠道订单少、转化异常、价格异常:优先考虑渠道数据诊断、经营分析类能力。 +- 退款、余额、采购、对账:优先考虑采购/订单/退款/财务对账类能力或打开对应业务应用。 +- SOP、合同、底价表、房型映射、回复规范:优先检索知识库,再结合能力包执行。 + +如果当前没有可用能力包,也要按业务专家的方式帮助用户拆解问题,并说明需要什么数据或能力才能继续。 + +## 参数补齐方式 + +不要一次性问太多问题。优先问能推进执行的关键参数。 + +示例: + +- 用户说“帮我看今天房态有没有问题”,可追问“要检查哪些渠道?如果不指定,我先按已配置渠道检查今天和未来 7 天。” +- 用户说“把周末价格调高”,应追问“调哪些日期、房型、渠道,以及按百分比还是固定金额?” +- 用户说“生成日报”,如果没有更多要求,可默认生成昨日经营日报,并说明默认口径。 + +当可以采用安全默认值时,先声明默认值并继续推进。 + +## 高风险业务动作 + +以下操作属于高风险动作:改价、改房态、改库存、同步渠道、取消/退款、批量更新订单、修改财务或对账结果、向外部渠道发送正式消息。 + +处理高风险动作时必须遵守: + +1. 未经用户明确确认,不执行写入或批量变更。 +2. 先生成预览或 dry-run 结果,列出日期、房型、渠道、原值、新值、影响范围和风险提示。 +3. 明确区分“建议”“预览”“已执行”。 +4. 用户确认后再执行,并在执行完成后输出成功项、失败项、失败原因和下一步建议。 +5. 如果能力包不支持预览或回滚,要在执行前明确提醒。 + +低风险动作如查询、诊断、报表、摘要、知识库检索,可以更主动地执行。 + +## 输出格式 + +根据问题类型选择稳定格式: + +- 诊断类:结论、发现的问题、证据、影响范围、建议动作。 +- 报表类:核心结论、关键指标、异常事项、原因判断、下一步建议、明细。 +- 改价类:调整范围、原价/新价、渠道、日期、房型、风险提示、确认动作。 +- 房态类:PMS/主系统房态、渠道房态、差异、建议修复动作。 +- 对账类:金额差异、订单/退款/余额证据、可能原因、需要人工确认的项目。 + +涉及表格时,优先用清晰的表格或分组列表。不要只输出长段文字。 + +## 回答样式 + +回答要像一名可靠的业务员工在汇报工作,而不是像通用聊天机器人展示思考过程。优先让用户一眼看懂“发生了什么、严重不严重、接下来做什么”。 + +一般回答结构: + +1. 先给结论或当前状态。 +2. 再给关键依据、影响范围或已检查内容。 +3. 最后给建议动作、需要用户确认的事项或下一步。 + +不同场景的回答方式: + +- 简单问题:直接回答,不铺背景。 +- 诊断问题:用“结论 / 发现 / 影响 / 建议”。 +- 执行动作:用“计划 / 参数 / 预览 / 确认”。 +- 执行完成:用“已完成 / 成功项 / 失败项 / 后续建议”。 +- 数据报表:先写 3 条以内核心结论,再给异常和明细。 +- 高风险动作:必须突出“尚未执行”或“已执行”,避免用户误解。 + +尽量使用短句、分组列表和表格。不要在业务结果前输出长篇解释。不要展示内部推理过程;只展示可验证的业务依据和行动建议。 + +如果用户只是要快速处理问题,回答应短、准、可执行。如果用户要求分析或复盘,再展开原因、风险和方案。 + +## 任务与提醒显示 + +当创建、执行、展示或推送任务时,必须让用户快速判断:这是什么任务、为什么提醒我、是否需要处理、谁来处理、什么时候处理。 + +任务说明应尽量包含: + +- 任务名称:使用业务语言,例如“每日渠道房态诊断”,不要只写技术名称。 +- 业务目标:说明这个任务要解决什么问题。 +- 执行范围:日期、房型、渠道、门店/酒店、数据源。 +- 触发方式:手动、定时、异常触发或用户确认后执行。 +- 当前状态:未开始、执行中、发现异常、待确认、已完成、失败。 +- 下一步:无需处理、建议处理、需要确认、需要补充授权、需要打开原系统。 +- 通知对象:如果有推送,说明发给哪个群或角色。 + +提醒内容应优先显示“异常和待处理”,不要把正常流水写成噪音。普通日报可以汇总,异常提醒必须明确。 + +提醒建议格式: + +- 标题:一句话说明问题,例如“美团豪华大床房库存与主系统不一致”。 +- 严重程度:高 / 中 / 低,或“需立即处理 / 今日内处理 / 仅供查看”。 +- 证据:列出关键差异、时间、渠道、房型、金额或订单号。 +- 建议动作:给出最短处理路径,例如“打开渠道后台核对库存”或“确认后执行同步”。 +- 操作状态:说明当前只是提醒、预览,还是已经执行。 + +对高风险任务提醒,例如改价、改房态、改库存,应默认展示为“待确认”,并强调未确认前不会写入业务系统。 + +任务失败时,提醒必须包含失败阶段和可操作建议,例如“登录态失效,请重新登录渠道后台后重试”,而不是只显示“执行失败”。 + +## 失败处理 + +能力包、工具或业务应用调用失败时,不要只说失败。应说明: + +- 失败发生在哪一步。 +- 可能原因,例如登录态失效、权限不足、网络异常、渠道接口异常、数据为空、参数不完整。 +- 用户可以怎么继续,例如重新登录、补充文件、缩小范围、打开原系统确认、联系实施人员。 + +如果可以重试,可建议重试一次;如果重复失败,应转为排查建议。 + +## 语言和态度 + +- 面向普通企业人员表达,少用技术术语。 +- 直接、清楚、可执行。 +- 不夸大能力,不编造不存在的数据。 +- 不把用户推回“自己去系统里看”,除非确实需要人工确认或原系统授权。 +- 对酒店行业问题,要表现出理解业务流程,但不要假装已经接入了未接入的数据源。 **Tool Usage Rule**: You have access to real, working tools (browser, shell, file operations, etc.). Before telling the user "I can't do that" or "I don't have access to that tool", **always check your available tools and attempt the action first**. Only report inability after receiving an actual error from the tool. Do not refuse based on assumptions from your training data. diff --git a/shared/business-guidance.ts b/shared/business-guidance.ts new file mode 100644 index 0000000..4e58352 --- /dev/null +++ b/shared/business-guidance.ts @@ -0,0 +1,40 @@ +const GUIDANCE_START = '[[YINIAN_BUSINESS_RESPONSE_GUIDANCE]]'; +const GUIDANCE_END = '[[/YINIAN_BUSINESS_RESPONSE_GUIDANCE]]'; + +export const BUSINESS_RESPONSE_GUIDANCE = [ + '请按智念业务员工的回答样式输出:', + '1. 先给结论或当前状态,再给关键依据/影响范围,最后给下一步动作。', + '2. 业务回复优先使用稳定段落标签“状态:”“依据:”“影响:”“下一步:”,方便桌面端渲染成可扫读摘要。', + '3. 诊断类输出使用“结论 / 发现 / 影响 / 建议”;报表类先给 3 条以内核心结论,再给异常和明细。', + '4. 改价、房态、库存、订单、退款、财务等高风险动作必须明确“执行预览:”“待确认:”“已执行:”的状态,未经确认不要写入业务系统。', + '5. 任务结果和提醒要说明异常是否需要处理、证据是什么、建议谁在什么时候处理。', + '6. 不展示内部推理过程,不编造未接入的数据;工具失败时说明失败阶段、可能原因和可操作下一步。', +].join('\n'); + +export function stripBusinessResponseGuidance(message: string | undefined | null): string { + let value = String(message ?? ''); + + while (true) { + const start = value.indexOf(GUIDANCE_START); + if (start < 0) break; + const end = value.indexOf(GUIDANCE_END, start + GUIDANCE_START.length); + if (end < 0) { + value = value.slice(0, start); + break; + } + value = `${value.slice(0, start)}${value.slice(end + GUIDANCE_END.length)}`; + } + + return value.replace(/\n{3,}/g, '\n\n').trim(); +} + +export function appendBusinessResponseGuidance(message: string | undefined | null): string { + const cleanMessage = stripBusinessResponseGuidance(message); + const guidanceBlock = `${GUIDANCE_START}\n${BUSINESS_RESPONSE_GUIDANCE}\n${GUIDANCE_END}`; + return [cleanMessage, guidanceBlock].filter(Boolean).join('\n\n'); +} + +export function hasBusinessResponseGuidance(message: string | undefined | null): boolean { + const value = String(message ?? ''); + return value.includes(GUIDANCE_START) && value.includes(GUIDANCE_END); +} diff --git a/src/i18n/locales/en/tasks.json b/src/i18n/locales/en/tasks.json index c196eb2..eb2139c 100644 --- a/src/i18n/locales/en/tasks.json +++ b/src/i18n/locales/en/tasks.json @@ -61,6 +61,23 @@ "success": "Success", "failed": "Failed" }, + "reminder": { + "unknownError": "check the run history", + "paused": "This task is paused and will not run or notify automatically.", + "failed": "Last run failed: {{error}}. Handle it before running again.", + "warning": "The last run recovered, but it still has a warning. Review the result.", + "delivery": "Results will be sent to {{channel}}. Watch the recipient when anomalies appear.", + "nextRun": "Next run: {{time}}. Anomalies will appear in the task conversation.", + "desktopOnly": "Results stay in the desktop task conversation. No external delivery is configured.", + "badges": { + "paused": "Paused", + "needsAction": "Needs action", + "review": "Review", + "willNotify": "Will notify", + "scheduled": "Scheduled", + "desktopOnly": "Desktop" + } + }, "dialog": { "createTitle": "New task", "editTitle": "Edit task", diff --git a/src/i18n/locales/zh/tasks.json b/src/i18n/locales/zh/tasks.json index 99486f6..d4f9171 100644 --- a/src/i18n/locales/zh/tasks.json +++ b/src/i18n/locales/zh/tasks.json @@ -61,6 +61,23 @@ "success": "成功", "failed": "失败" }, + "reminder": { + "unknownError": "请查看执行记录", + "paused": "任务已暂停,不会自动执行或提醒。", + "failed": "上次执行失败:{{error}}。建议先处理后再运行。", + "warning": "上次执行已恢复,但仍有警告信息,建议复核结果。", + "delivery": "执行结果会推送到 {{channel}},异常时请关注接收群。", + "nextRun": "下次将在 {{time}} 执行;发现异常时会在任务会话中显示。", + "desktopOnly": "结果仅保留在桌面任务会话中,未配置外部推送。", + "badges": { + "paused": "已暂停", + "needsAction": "需处理", + "review": "需复核", + "willNotify": "会提醒", + "scheduled": "已安排", + "desktopOnly": "桌面内" + } + }, "dialog": { "createTitle": "新建任务", "editTitle": "编辑任务", diff --git a/src/pages/Chat/ChatInput.tsx b/src/pages/Chat/ChatInput.tsx index f0ff94e..716427e 100644 --- a/src/pages/Chat/ChatInput.tsx +++ b/src/pages/Chat/ChatInput.tsx @@ -460,7 +460,7 @@ export function ChatInput({
{task.name} -
+
{task.name}
{task.description || t('composer.quickTaskHelp')}
{skillNames && ( -
+
{skillNames}
)} @@ -519,7 +519,7 @@ export function ChatInput({ )} {/* Input Container */} -
+
{selectedKnowledgeDocuments.length > 0 && (
{selectedKnowledgeDocuments.map((doc) => ( @@ -527,7 +527,7 @@ export function ChatInput({ key={doc.id} type="button" onClick={() => toggleKnowledgeDocument(doc.id)} - className="inline-flex max-w-[220px] items-center gap-1.5 rounded-lg border border-[#7DBADB]/70 bg-[#EAF5FA] px-2.5 py-1 text-[13px] font-medium text-foreground transition-colors hover:bg-[#DDF0F8]" + className="inline-flex max-w-[220px] items-center gap-1.5 rounded-lg border border-[#7DBADB]/70 bg-[#F4FAFD] px-2.5 py-1 text-[13px] font-medium text-foreground transition-colors hover:bg-[#EAF5FA]" title={doc.name} > @@ -539,11 +539,11 @@ export function ChatInput({ )} {/* Text Row — flush-left */} -
+
{selectedQuickTaskPrompt && ( textareaRef.current?.focus()} - className="mt-0 select-none whitespace-nowrap pr-1.5 font-sans text-[15px] font-normal leading-6 text-muted-foreground/60 md:text-[15px]" + className="mt-0 select-none whitespace-nowrap pr-1.5 font-sans text-[14px] font-normal leading-6 text-muted-foreground/60" > {selectedQuickTaskPrompt} @@ -563,7 +563,7 @@ export function ChatInput({ placeholder={disabled ? t('composer.gatewayDisconnectedPlaceholder') : ''} disabled={disabled} data-testid="chat-composer-input" - className="block min-h-[48px] max-h-[240px] min-w-0 flex-1 resize-none border-0 focus-visible:ring-0 focus-visible:ring-offset-0 shadow-none bg-transparent p-0 font-sans text-[15px] font-normal leading-6 placeholder:text-muted-foreground/60 md:text-[15px]" + className="block min-h-[48px] max-h-[240px] min-w-0 flex-1 resize-none border-0 bg-transparent p-0 font-sans text-[14px] font-normal leading-6 shadow-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0" rows={1} />
@@ -587,7 +587,7 @@ export function ChatInput({ variant="ghost" className={cn( 'h-8 rounded-lg px-2 text-[12px] text-muted-foreground transition-colors hover:bg-[#EAF5FA] hover:text-[#075985] dark:hover:bg-white/10', - selectedKnowledgeIds.length > 0 && 'bg-[#EAF5FA] text-[#075985] hover:bg-[#DDF0F8]', + selectedKnowledgeIds.length > 0 && 'bg-[#F4FAFD] text-[#075985] hover:bg-[#EAF5FA]', )} onClick={() => setKnowledgePickerOpen((open) => !open)} disabled={!knowledgeBaseAvailable || disabled || sending} @@ -629,7 +629,7 @@ export function ChatInput({ onClick={() => toggleKnowledgeDocument(doc.id)} className={cn( 'flex w-full items-center gap-2 rounded-lg px-2 py-2 text-left transition-colors', - selected ? 'bg-[#EAF5FA] text-foreground' : 'hover:bg-black/5 dark:hover:bg-white/5', + selected ? 'bg-[#EAF5FA] text-foreground' : 'hover:bg-[#F8FBFE] dark:hover:bg-white/5', )} >
-
+
@@ -718,7 +718,7 @@ function AttachmentPreview({ const isImage = attachment.mimeType.startsWith('image/') && attachment.preview; return ( -
+
{isImage ? ( // Image thumbnail
@@ -730,7 +730,7 @@ function AttachmentPreview({
) : ( // Generic file card -
+

{attachment.fileName}

diff --git a/src/pages/Chat/ChatMessage.tsx b/src/pages/Chat/ChatMessage.tsx index dbd924f..50794af 100644 --- a/src/pages/Chat/ChatMessage.tsx +++ b/src/pages/Chat/ChatMessage.tsx @@ -16,6 +16,7 @@ import { cn } from '@/lib/utils'; import { invokeIpc } from '@/lib/api-client'; import type { RawMessage, AttachedFileMeta } from '@/stores/chat'; import { extractText, extractImages, extractToolUse, formatTimestamp } from './message-utils'; +import { buildBusinessAnswerView, type BusinessAnswerView, type BusinessAnswerTone } from './business-answer'; import assistantLogo from '@/assets/logo.svg'; interface ChatMessageProps { @@ -120,13 +121,13 @@ export const ChatMessage = memo(function ChatMessage({ return (
{/* Avatar */} {!isUser && ( -
+
智念助手
)} @@ -134,7 +135,7 @@ export const ChatMessage = memo(function ChatMessage({ {/* Content */}
@@ -192,7 +193,7 @@ export const ChatMessage = memo(function ChatMessage({ ) : (
@@ -253,7 +254,7 @@ export const ChatMessage = memo(function ChatMessage({ } if (isImage && !file.preview) { return ( -
+
); @@ -321,7 +322,7 @@ function ToolStatusBar({ className={cn( 'flex items-center gap-2 rounded-lg border px-3 py-2 text-xs transition-colors', isRunning && 'border-primary/30 bg-primary/5 text-foreground', - !isRunning && !isError && 'border-border/50 bg-muted/20 text-muted-foreground', + !isRunning && !isError && 'border-slate-200/80 bg-white text-muted-foreground dark:border-white/10 dark:bg-white/5', isError && 'border-destructive/30 bg-destructive/5 text-destructive', )} > @@ -353,7 +354,7 @@ function AssistantHoverBar({ text, timestamp }: { text: string; timestamp?: numb }, [text]); return ( -
+
{timestamp ? formatTimestamp(timestamp) : ''} @@ -380,56 +381,164 @@ function MessageBubble({ isUser: boolean; isStreaming: boolean; }) { + const businessAnswer = isUser ? null : buildBusinessAnswerView(text); + return (
{isUser ? ( -

{text}

+

{text}

) : ( -
- - {children} - - ); - } - return ( -
-                    
-                      {children}
-                    
-                  
- ); - }, - a({ href, children }) { - return ( - - {children} - - ); - }, - }} - > - {normalizeLatexDelimiters(text)} -
- {isStreaming && ( - +
+ {businessAnswer && } +
+ + {children} + + ); + }, + h2({ children, ...props }) { + return ( +

+ {children} +

+ ); + }, + h3({ children, ...props }) { + return ( +

+ {children} +

+ ); + }, + p({ children, ...props }) { + return ( +

+ {children} +

+ ); + }, + ul({ children, ...props }) { + return ( +
    + {children} +
+ ); + }, + ol({ children, ...props }) { + return ( +
    + {children} +
+ ); + }, + li({ children, ...props }) { + return ( +
  • + {children} +
  • + ); + }, + blockquote({ children, ...props }) { + return ( +
    + {children} +
    + ); + }, + hr({ ...props }) { + return
    ; + }, + table({ children, ...props }) { + return ( +
    + + {children} +
    +
    + ); + }, + thead({ children, ...props }) { + return ( + + {children} + + ); + }, + tr({ children, ...props }) { + return ( + + {children} + + ); + }, + th({ children, ...props }) { + return ( + + {children} + + ); + }, + td({ children, ...props }) { + return ( + + {children} + + ); + }, + code({ className, children, ...props }) { + const match = /language-(\w+)/.exec(className || ''); + const isInline = !match && !className; + if (isInline) { + return ( + + {children} + + ); + } + return ( +
    +                      
    +                        {children}
    +                      
    +                    
    + ); + }, + a({ href, children }) { + return ( + + {children} + + ); + }, + }} + > + {normalizeLatexDelimiters(text)} +
    + {isStreaming && ( + + )} +
    )} @@ -437,6 +546,88 @@ function MessageBubble({ ); } +function getBusinessAnswerToneClasses(tone: BusinessAnswerTone): { panel: string; icon: string; label: string } { + if (tone === 'danger') { + return { + panel: 'border-rose-200 bg-rose-50 text-rose-950 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-100', + icon: 'bg-rose-100 text-rose-700 dark:bg-rose-500/20 dark:text-rose-100', + label: 'text-rose-700 dark:text-rose-200', + }; + } + if (tone === 'warning') { + return { + panel: 'border-amber-200 bg-amber-50 text-amber-950 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100', + icon: 'bg-amber-100 text-amber-700 dark:bg-amber-500/20 dark:text-amber-100', + label: 'text-amber-700 dark:text-amber-200', + }; + } + if (tone === 'success') { + return { + panel: 'border-emerald-200 bg-emerald-50 text-emerald-950 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-100', + icon: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-100', + label: 'text-emerald-700 dark:text-emerald-200', + }; + } + return { + panel: 'border-sky-200 bg-sky-50 text-sky-950 dark:border-sky-500/30 dark:bg-sky-500/10 dark:text-sky-100', + icon: 'bg-sky-100 text-sky-700 dark:bg-sky-500/20 dark:text-sky-100', + label: 'text-sky-700 dark:text-sky-200', + }; +} + +function BusinessAnswerPanel({ view }: { view: BusinessAnswerView }) { + const classes = getBusinessAnswerToneClasses(view.tone); + const Icon = view.tone === 'success' ? CheckCircle2 : AlertCircle; + const hasEvidence = view.evidence.length > 0; + const hasActions = view.actions.length > 0; + + return ( +
    +
    + + + +
    +
    + 业务摘要 +
    +
    + {view.status} +
    +
    +
    + {(hasEvidence || hasActions) && ( +
    + {hasEvidence && ( + + )} + {hasActions && ( + + )} +
    + )} +
    + ); +} + +function BusinessAnswerList({ title, items }: { title: string; items: string[] }) { + return ( +
    +
    {title}
    +
      + {items.map((item) => ( +
    • + {item} +
    • + ))} +
    +
    + ); +} + // ── File Card (for user-uploaded non-image files) ─────────────── function formatFileSize(bytes: number): string { @@ -463,10 +654,10 @@ function FileCard({ file }: { file: AttachedFileMeta }) { }, [file.filePath]); return ( -
    {fileName} @@ -533,7 +724,7 @@ function ImagePreviewCard({ void filePath; void base64; void mimeType; return (
    {fileName} @@ -628,7 +819,7 @@ function ToolCard({ name, input }: { name: string; input: unknown }) { const [expanded, setExpanded] = useState(false); return ( -
    +