305 Commits

Author SHA1 Message Date
程序员阿江(Relakkes)
659c63e5ba perf(desktop): memoize chat row subtree to stop window-slide re-renders
When the virtualization window slid during scroll, every visible row reconciled
from scratch because the heavy chat-row subtree had no memo barriers, and
because renderTranscriptItem rebuilt fresh branchAction / toolResult object
literals each render that broke MessageBlock's existing memo.

Wraps AssistantMessage, UserMessage, ToolCallBlock, ToolCallGroup, ToolResultBlock,
and MarkdownRenderer in React.memo, and hoists the per-message branchAction and
toolResult lookups into useMemo'd Maps keyed by message id. Window slides now
keep referentially-stable props for unchanged rows, so reconciliation skips them
and only newly entering rows pay full render cost.

Tested: 722/722 desktop vitest suites pass

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 23:41:25 +08:00
程序员阿江(Relakkes)
147b744abd perf(desktop): stabilize virtualized transcript scroll and batch height measurements
Long desktop transcripts (797+ messages / 2.6MB) showed 1-2s blank frames while
scrolling. The previous strategy inflated virtualization overscan from 360px to
7200px on every wheel/touch/pointer event and routed scroll-handler state
updates through flushSync, which serialized large React reconciles onto WebKit's
compositor thread and starved paint.

Replaces the dynamic overscan + idle-timer state machine with a single stable
1200px overscan, and removes the flushSync barrier in updateAutoScrollState so
React 18's automatic batching handles the scroll-handler setStates. ResizeObserver
height callbacks now mark a pending bit and flush a single setMeasuredItemsVersion
per animation frame, instead of triggering a list-wide re-render per measured
item. Auto-scroll-on-measure is also removed so measurement feedback no longer
fights an active user scroll.

Tested: 722/722 desktop vitest suites pass

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 23:37:15 +08:00
程序员阿江(Relakkes)
91224cbd20 Merge Writer preview fix into local main
Bring the detached-worktree desktop Writer preview patch into the canonical local main checkout so long pending Write calls show visible progress from main.

Constraint: Local main had advanced beyond the detached worktree base, so this landing keeps an explicit merge boundary.
Confidence: high
Scope-risk: moderate
Directive: Preserve bounded pending Writer rendering; do not replace it with live diff rendering without performance evidence.
Tested: bun run check:desktop in detached worktree before merge.
Not-tested: Post-merge desktop gate before this merge commit was created.
2026-05-23 19:14:32 +08:00
程序员阿江(Relakkes)
6b1561cd1c fix: stream pending Write content as Writer previews
Long Write tool inputs can spend minutes streaming the file body before the tool executes, so the desktop transcript now decodes the pending content field into a lightweight Writer preview. The preview intentionally uses a bounded plain-text window while the tool is pending, then keeps the existing full diff rendering once the Write call completes.

Constraint: Write.content arrives as streaming tool input before the filesystem write executes.
Rejected: Render a live DiffViewer for every input delta | repeated diff and syntax work would reintroduce long-session jank.
Confidence: high
Scope-risk: moderate
Directive: Keep pending Writer rendering lightweight and bounded; reserve full diff rendering for completed Write calls.
Tested: cd desktop && bun run test src/components/chat/chatBlocks.test.tsx -- --runInBand
Tested: cd desktop && bun run test src/stores/chatStore.test.ts -- --runInBand
Tested: bun run check:desktop
Not-tested: Live provider long Write smoke after the UI patch.
2026-05-23 19:14:22 +08:00
程序员阿江(Relakkes)
d327b7fb30 fix: preserve pending user questions across reconnects
Desktop AskUserQuestion waits on the SDK permission bridge, and a transient renderer disconnect could previously let the short no-client cleanup kill the CLI. That turned a live user question into an AbortError and left history restore showing a stale interactive card.

Track live SDK permission requests, replay them to reconnecting clients, and give sessions waiting on user input a longer cleanup grace. Render aborted terminal results as completed history instead of an unanswered prompt.

Constraint: SDK permission requests are live in-memory control messages until answered or aborted.

Rejected: Only de-dupe history cards in MessageList | would not keep the CLI alive or restore the active permission request.

Rejected: Disable disconnect cleanup globally | would leak abandoned CLI processes for ordinary disconnected sessions.

Confidence: high

Scope-risk: moderate

Reversibility: clean

Directive: Do not shorten pending-permission cleanup without testing AskUserQuestion reconnect and abort paths.

Tested: bun run check:desktop

Tested: bun run check:server

Tested: git diff --check

Not-tested: Live Sub2API provider reconnect repro.
2026-05-23 19:06:59 +08:00
程序员阿江(Relakkes)
d116067d9c fix: virtualize long desktop transcripts
Large desktop sessions were still paying too much render and paint cost on tab switch, while the previous content-visibility approach could defer WebView painting enough to show white gaps during scroll. This moves long transcripts to a measured dynamic-height window, keeps small transcripts fully mounted, and caches completed Markdown parses so remounted assistant messages do not pay repeated parse cost.

Constraint: Tauri/WebView must not depend on content-visibility for transcript correctness because deferred offscreen paint can surface as blank regions

Rejected: Keep content-visibility:auto on every chat item | it improves synthetic DOM cost but matches the user's white-gap failure mode

Rejected: Fixed-height transcript virtualization | variable-height chat rows and tool groups can create spacer gaps and broken scroll positions

Confidence: high

Scope-risk: moderate

Directive: Do not reintroduce deferred-paint transcript optimization without testing the real Tauri/WebView scroll path on large sessions

Tested: bun run check:desktop

Tested: Chrome DevTools real-session smoke for 055015e4-6093-418e-9944-19bba25ecb69, switch trace max RunTask about 158ms and scroll blankSamples=0 across 34 sampled positions

Not-tested: Packaged Tauri binary smoke on WebKit/WebView2
2026-05-23 19:03:51 +08:00
程序员阿江(Relakkes)
385435a04d Merge desktop long-session performance fix into local main
Bring the validated detached-worktree desktop performance patch into the canonical local main checkout so long-session tab and scroll fixes are available from the main working copy.

Constraint: Local main had advanced independently beyond the detached worktree base, so fast-forward was not the correct landing shape.
Confidence: high
Scope-risk: moderate
Directive: Verify ancestry for detached-worktree landings because main may advance after the feature worktree is created.
Tested: bun run check:desktop in detached worktree before merge.
Not-tested: Post-merge desktop gate before this merge commit was created.
2026-05-23 17:56:39 +08:00
程序员阿江(Relakkes)
0231e7b61d fix: reduce desktop jank for long sessions
Long transcripts were triggering avoidable layout work during tab switches and auto-scroll because MessageList read scroll geometry while the browser still had a large message tree to lay out. The tab bar also subscribed to full chat session state, so streaming payload churn could invalidate tab chrome even when running status did not change.

This keeps variable-height transcript rows fully mounted, adds browser offscreen rendering hints, scrolls to bottom through clamped large offsets instead of synchronous geometry reads, and narrows TabBar's chat-store subscription to running-state ids.

Constraint: Prior fixed-height virtualization left blank spacer gaps for variable-height chat rows.
Rejected: Reintroduce fixed-height virtualization | would regress existing long transcript rendering behavior.
Confidence: high
Scope-risk: moderate
Directive: Do not replace this with fixed row-height virtualization without measured row heights and scroll-restore coverage.
Tested: bun run check:desktop
Tested: Chrome DevTools trace on real session 055015e4-6093-418e-9944-19bba25ecb69 with INP 84ms to 43ms and ForcedReflow insight removed.
Not-tested: Packaged Tauri WebView runtime beyond the local web UI trace.
2026-05-23 17:56:16 +08:00
程序员阿江(Relakkes)
a6d9adbc38 fix: prevent long tool streams from looking stuck
Desktop already receives tool_use start and input deltas before the final tool_use_complete event, but the chat transcript only rendered a visible tool card after the complete event. This makes long Write payloads look frozen while the model is still generating the tool input. The store now upserts a pending tool_use message on stream start, updates a lightweight input preview from deltas, and resolves that same message when the complete event arrives.

Constraint: Long Write calls spend most perceived time streaming JSON tool input, not executing the filesystem write

Rejected: Show only the global active tool label | it does not anchor progress in the transcript where the user is looking

Confidence: high

Scope-risk: moderate

Directive: Keep pending tool_use messages keyed by toolUseId so the complete event updates in place instead of duplicating cards

Tested: bun run check:desktop

Not-tested: Live provider smoke with a real 10k-word Write stream
2026-05-23 17:08:25 +08:00
程序员阿江(Relakkes)
5fcbdf322a Merge worktree label cleanup into local main
Bring the detached worktree fix into the canonical local checkout so desktop worktree sessions hide internal branch labels while preserving git-info metadata separation.

Constraint: User requested local main integration, not push or PR
Confidence: high
Scope-risk: narrow
Tested: Clean local main worktree before merge; feature commit contains server and desktop verification evidence
Not-tested: Full quality gate after merge
2026-05-23 16:16:50 +08:00
程序员阿江(Relakkes)
89ec7d66c9 fix: hide internal worktree branch labels
Desktop worktree sessions should show the source project and worktree marker without surfacing implementation refs like worktree-desktop-* as user-facing branch state. The git-info response now keeps launch branch metadata separate from worktree identity, and the desktop chip hides branch and slug labels in isolated worktree mode.

Constraint: Git worktrees need an internal branch for isolated execution, but that ref is product plumbing rather than useful UI context
Rejected: Show both launch branch and worktree slug | duplicated noisy identifiers and confused the session location
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/sessions.test.ts -t "git-info"; cd desktop && bun run test src/components/shared/ProjectContextChip.test.tsx; bun run check:server; bun run check:desktop; git diff --check
Not-tested: Live desktop screenshot smoke
2026-05-23 16:16:30 +08:00
程序员阿江(Relakkes)
853e3db6ec fix: keep AskUserQuestion state aligned with CLI answers
The CLI transcript already records each AskUserQuestion once, but the desktop bridge dropped the structured toolUseResult answers from both history replay and live WebSocket tool results. That made answered question cards look unresolved and allowed stale cards to reappear during the gap before the next permission request.

Constraint: Desktop should render the CLI question stream directly without inventing separate question lifecycle state.
Rejected: Hide all previous AskUserQuestion cards after submit | would remove resolved answer history instead of preserving transcript context.
Confidence: high
Scope-risk: moderate
Directive: Preserve toolUseResult metadata when adapting CLI transcript entries; AskUserQuestion answer state depends on the structured answers object, not the prose tool_result string.
Tested: bun test src/server/__tests__/sessions.test.ts --timeout 30000
Tested: bun test src/server/__tests__/ws-memory-events.test.ts --timeout 30000
Tested: cd desktop && bun run test src/stores/chatStore.test.ts
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx
Tested: bun run check:server
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
Not-tested: Full bun run verify and live desktop browser smoke.
2026-05-23 15:58:15 +08:00
程序员阿江(Relakkes)
6b62b6c633 Merge code rendering compatibility fix into local main
Bring the detached worktree fix back to the local main branch so modern WebUI keeps Shiki rendering while legacy WebKit can fall back before loading Shiki.

Constraint: Local main already contains unreleased commits ahead of origin/main.
Confidence: high
Scope-risk: moderate
Tested: Worktree commit passed focused CodeViewer test, desktop lint, desktop build, startup bundle scan, and real WebUI Shiki smoke before merge.
Not-tested: Re-running the full desktop gate after merge.
2026-05-22 17:55:45 +08:00
程序员阿江(Relakkes)
4fce6014a6 fix: preserve Shiki code rendering with legacy fallback
Code blocks should keep the richer Shiki rendering in modern WebUI while avoiding the old WebKit startup crash path by loading Shiki lazily and falling back to Prism only when the runtime cannot safely parse or execute it.

Constraint: Issue #461 is triggered by older Safari/WebKit parsing modern RegExp syntax during startup.
Rejected: Replace Shiki entirely with Prism | visual quality regressed for normal WebUI usage.
Confidence: high
Scope-risk: moderate
Directive: Keep Shiki out of the startup path unless macOS 12 WebView compatibility is re-verified.
Tested: cd desktop && bun run test -- --run src/components/chat/CodeViewer.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Real WebUI smoke on http://127.0.0.1:1420/ verified six code blocks rendered with data-highlight-engine=shiki.
Not-tested: Physical macOS 12 WebView runtime.
Related: https://github.com/NanmiCoder/cc-haha/issues/461
2026-05-22 17:55:33 +08:00
程序员阿江(Relakkes)
861d88225c fix: improve AskUserQuestion desktop rendering
Desktop AskUserQuestion could show duplicate unresolved cards when restored history and a live permission request overlapped. The custom response field was also single-line, making long restored-context answers hard to read or edit.

This keeps the active unresolved AskUserQuestion card aligned with the live permission request, preserves resolved history, and switches custom responses to a multiline editor with Ctrl/Cmd+Enter submission.

Constraint: Desktop issue reports #542 and #543 both hit the AskUserQuestion chat surface.
Rejected: Hide all AskUserQuestion history while pending | resolved answers should remain visible in the transcript.
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test src/components/chat/AskUserQuestion.test.tsx
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: bun run check:desktop
Not-tested: Live Windows desktop manual restore flow from the issue attachment.
2026-05-21 17:20:39 +08:00
程序员阿江(Relakkes)
e437199b81 fix: Surface API retry progress in desktop chat
CLI retry events already include retry attempts, delay, and upstream status, but
the desktop WebSocket translation dropped api_retry system messages. The desktop
chat now keeps the current retry state in session state and renders a compact
active-turn indicator with attempt count, HTTP status, and countdown.

Constraint: Desktop users need visible feedback during provider retry waits after auth or network failures.
Rejected: Add retry rows to the transcript | retry heartbeats would clutter chat history instead of describing the active turn.
Confidence: high
Scope-risk: moderate
Directive: Keep api_retry as active-turn state; do not persist retry heartbeats into transcript history without a product decision.
Tested: bun test src/server/__tests__/ws-memory-events.test.ts
Tested: cd desktop && bun run test src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:server
Tested: Browser smoke with temp CLAUDE_CONFIG_DIR and mock OpenAI-compatible 503 endpoint, screenshot /tmp/cc-haha-retry-ui.png
Not-tested: Live ChatGPT logout retry path against OpenAI production auth.
2026-05-20 18:48:36 +08:00
程序员阿江(Relakkes)
dffe83b83a Merge branch 'feat/chatgpt-official-oauth-provider' 2026-05-19 23:35:09 +08:00
程序员阿江(Relakkes)
a4aba9ec5a Show context compaction as transcript state
Desktop users need to see automatic context compaction as a transient transcript state instead of a stray stdout bubble or large card. The UI now collapses prior visible content into a compact timeline divider, shows the compacting phase, then updates the same divider with the completed summary affordance.

Constraint: CLI compaction emits both status and synthetic transcript artifacts that must be normalized before rendering

Rejected: Keep the existing small pill marker | it did not show the in-progress state and looked like chat content

Confidence: high

Scope-risk: moderate

Directive: Do not render local compact stdout as a user-visible chat message

Tested: cd desktop && bun run test -- src/components/chat/MessageList.test.tsx src/stores/chatStore.test.ts

Tested: bun test src/server/__tests__/ws-memory-events.test.ts

Tested: bun run check:desktop

Not-tested: full root bun run verify after the final UI iteration
2026-05-19 23:13:01 +08:00
程序员阿江(Relakkes)
8bd122c967 Keep message fork controls inline
Desktop transcript messages already expose copy controls in the shared action row. The fork affordance was rendered as its own zero-height hover row, which left the icon below Copy and made the action feel detached from the message toolbar. Moving the fork button into the existing action bar keeps the controls aligned while preserving the original branch-session API path.

Constraint: Desktop message actions should stay visually grouped for both user and assistant messages

Rejected: Keep a separate fork-only hover row | it reproduces the two-line layout regression shown in the desktop UI

Confidence: high

Scope-risk: narrow

Directive: Keep future per-message actions inside MessageActionBar unless they need a different interaction surface

Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx --pool forks --poolOptions.forks.singleFork=true

Tested: cd desktop && bun run test src/i18n/index.test.tsx --pool forks --poolOptions.forks.singleFork=true

Tested: cd desktop && bun run lint

Tested: bun run check:desktop

Not-tested: Full live desktop backend fork-click E2E
2026-05-19 23:11:03 +08:00
程序员阿江(Relakkes)
abd782494e fix: keep desktop composer toolbar out of multiline input
The active-session composer used an absolute toolbar overlay and textarea bottom padding to reserve space. When the input grew and scrolled, the caret could render inside that reserved overlay area, making it appear to pass through the controls.

Move the non-hero toolbar back into normal layout flow and remove the oversized textarea bottom padding so the text area and controls occupy separate boxes.

Constraint: Active-session and hero composer variants share the component, so keep the change scoped to the non-hero toolbar path.
Rejected: Increase textarea bottom padding | preserves the overlapping layout that caused the caret artifact.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce an absolutely positioned toolbar over the active-session textarea without validating multiline caret rendering.
Tested: cd desktop && bun run test src/components/chat/ChatInput.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: Browser DOM check confirmed textarea and toolbar no longer overlap.
2026-05-19 22:26:18 +08:00
程序员阿江(Relakkes)
318bf336b0 Expose active session state across desktop navigation
Multiple desktop sessions can run at the same time, so navigation surfaces need a compact shared signal instead of making users open each conversation to check progress. The sidebar and tab bar now derive running state from both persisted tab status and live chat state, while session rows keep worktree and updated-time metadata aligned on the right.

Constraint: Reuse existing tab/chat state without changing session persistence or server APIs
Rejected: Add another session status field | duplicated state would drift from chatStore and tabStore
Confidence: high
Scope-risk: narrow
Directive: Keep sidebar and tab running indicators sourced from the same state rules
Tested: cd desktop && bun run test src/components/layout/TabBar.test.tsx src/components/layout/Sidebar.test.tsx --pool forks --poolOptions.forks.singleFork=true
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: real desktop session in /private/tmp/cc-haha-real-status-fsip8S using gpt-5.5 Sub2API-ChatGPT; running markers appeared in sidebar and tab, then cleared after CCH_STATUS_REAL_DONE
2026-05-19 22:13:54 +08:00
程序员阿江(Relakkes)
9e45724290 Merge local main into ChatGPT OAuth provider work
Local main added desktop branch-from-chat, attribution headers, sidecar packaging changes, and provider model normalization. The merge keeps those mainline changes while preserving ChatGPT Official provider metadata, OpenAI OAuth runtime env, provider-load guards, and model catalog behavior.

Constraint: Current worktree was detached; created feat/chatgpt-official-oauth-provider before merging to preserve the OAuth commit line.

Rejected: Rebase the OAuth line over main | the branch already contains many reviewed commits and a merge makes the integration point explicit.

Confidence: high

Scope-risk: moderate

Tested: bun test src/server/__tests__/providers.test.ts src/server/__tests__/settings.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/proxy-transform.test.ts src/utils/__tests__/providerManagedEnvCompat.test.ts src/services/api/client.test.ts src/services/openaiAuth/fetch.test.ts src/server/__tests__/haha-openai-oauth-service.test.ts src/server/__tests__/haha-openai-oauth-api.test.ts

Tested: cd desktop && bun run test -- src/__tests__/generalSettings.test.tsx src/components/settings/ChatGPTOfficialLogin.test.tsx src/stores/providerStore.test.ts src/components/controls/ModelSelector.test.tsx --testNamePattern "ChatGPT|OpenAI OAuth|Providers tab|ChatGPTOfficialLogin|ModelSelector|providerStore"

Tested: git diff --cached --check
2026-05-19 18:35:55 +08:00
程序员阿江(Relakkes)
73e915ac6b Enable desktop branching from transcript messages
Desktop users need the same branch-from-here workflow that the CLI already exposed, so the branch creation logic now lives in a shared transcript utility and the desktop app routes completed message actions through the server API. The UI hydrates transcript ids after live completions so newly generated turns can be branched immediately without a refresh.

Constraint: Source sessions must remain unmodified while branch sessions inherit the active transcript chain and persistence metadata.
Rejected: Keep a desktop-only branch implementation | it would drift from CLI /branch semantics and duplicate transcript filtering rules
Confidence: high
Scope-risk: moderate
Directive: Do not remove the post-completion transcript hydration without a real-model desktop E2E for just-finished messages
Tested: bun run verify; Chrome Web UI E2E with real gpt-5.5 provider on ports 45678/45679
Not-tested: Provider-specific behavior beyond the configured Sub2API-ChatGPT route
2026-05-19 18:31:24 +08:00
程序员阿江(Relakkes)
e1c2f54f14 fix: Improve settings extension browsing
Settings needed a faster way to find local skills, and the plugin overview was wasting space by forcing summary cards into a tall right rail. Keep skill discovery client-side over the already-loaded list and flatten plugin metrics into a compact header so release notes can point issue #513 at the exact user-facing fix.

Fixes #513

Constraint: Settings skills data is already loaded locally through /api/skills
Rejected: Add server-side skill search | unnecessary API surface for a small local filter
Confidence: high
Scope-risk: narrow
Directive: Keep Settings extension browsing read-only unless an install/edit flow is explicitly added
Tested: bun run check:desktop
Related: #513
2026-05-19 16:07:07 +08:00
程序员阿江(Relakkes)
c6bc51a9fd fix: Keep agent group label formatting type-safe
The structured Agent fallback formatter used indexed string access to capitalize grouped result labels, which fails under the desktop TypeScript build because the first character may be undefined. Use charAt so the formatter remains equivalent while satisfying noUncheckedIndexedAccess.

Constraint: desktop build runs tsc with strict indexed access checks
Rejected: Non-null assertion on normalized[0] | keeps the fragile indexed access pattern
Confidence: high
Scope-risk: narrow
Directive: Prefer string helpers over indexed access in build-sensitive UI formatting code
Tested: cd desktop && bun run build
Not-tested: Full macOS packaging script after the frontend build failure point
2026-05-19 04:10:44 +08:00
程序员阿江(Relakkes)
3ce7b02fc2 fix: Normalize text-wrapped agent result JSON
Some providers return the final Agent answer as a text block containing structured JSON instead of Markdown. The desktop result fallback now parses that text-wrapped shape and formats plain and grouped result arrays into readable Markdown before display.

Constraint: Historical sessions can store Agent tool_result content as [{ type: 'text', text: '{...}' }] rather than a direct object
Rejected: Treat provider-specific JSON output as a model bug only | existing transcripts still need readable display
Confidence: high
Scope-risk: narrow
Directive: Agent result display must normalize text-wrapped structured payloads before falling back to raw text
Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t "formats structured agent fallback"
Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t agent
Not-tested: Full desktop gate per request to avoid broad gate/test runs
2026-05-19 04:05:08 +08:00
程序员阿江(Relakkes)
3433be18dd fix: Render structured agent fallbacks readably
Agent fallback output can arrive as structured result arrays when no terminal task report is available. The desktop card now formats that shape into a readable Markdown list instead of exposing raw transport JSON in the preview or result modal.

Constraint: Some Agent executions still only provide structured tool_result content without a terminal Markdown report
Rejected: Keep pretty-printed JSON as the fallback | still exposes implementation shape instead of user-facing results
Confidence: high
Scope-risk: narrow
Directive: Structured Agent result arrays are display data; keep them readable before falling back to raw serialization
Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t "formats structured agent fallback"
Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t agent
Not-tested: Full desktop gate per request to avoid broad gate/test runs
2026-05-19 03:39:23 +08:00
程序员阿江(Relakkes)
430edc436e fix: Preserve readable agent result reports
Agent task notifications carry the user-facing Markdown report, while some Agent tool results contain structured intermediate payloads. The UI now keeps the terminal report as the modal/preview source when it exists, and falls back to raw tool output only when there is no report.

Constraint: Background Agent runs can emit structured tool_result payloads before the terminal task notification carries the readable Markdown report
Rejected: Convert every structured Agent payload into display Markdown | risks inventing summaries and still lets raw transport data compete with the real report
Confidence: high
Scope-risk: narrow
Directive: Do not let raw Agent tool_result content override terminal task result when both are present
Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t "prefers the terminal task report"
Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t agent
Not-tested: Full desktop gate per request to avoid broad gate/test runs
2026-05-19 03:32:34 +08:00
程序员阿江(Relakkes)
2323e6ec47 Keep subagent progress readable in desktop sessions
Desktop sessions can receive task tools, background-agent lifecycle events, and child tool stream events in the same turn. The UI now keeps root Agent cards separate from task-management tools, preserves parent links when later stream events omit them, and surfaces completed background-agent result text from task notifications.

Constraint: Existing desktop session history can contain task-notification XML and live WS events with incomplete parent metadata.
Rejected: Render local_agent lifecycle messages as standalone cards | duplicates the Agent tool card and makes ownership unclear.
Confidence: high
Scope-risk: moderate
Directive: Do not collapse Agent and TaskCreate/TaskUpdate root tools into one generic group without checking multi-agent screenshots.
Tested: SKIP_INSTALL=1 PRESERVE_TAURI_TARGET=1 ./desktop/scripts/build-macos-arm64.sh
Tested: Real gpt-5.5 desktop E2E sessions under /tmp/cc-haha-subagent-e2e-20260519-025454 for 3 subagents, child tools, result dialog, and background Bash completion.
Not-tested: Full bun run verify gate.
2026-05-19 03:15:52 +08:00
程序员阿江(Relakkes)
5f97f9ac62 Prevent bulk tab closing from silently abandoning running sessions
Bulk tab actions reused a direct disconnect path instead of the single-tab running-session confirmation path. The tab bar now collects running sessions before closing any selected tab set, prompts when active work would be affected, and applies the user's stop-or-keep choice consistently across close all, close others, close left, and close right.

Constraint: Desktop users can run multiple sessions concurrently and must not lose running work through a bulk tab action without confirmation
Rejected: Patch only Close All | the neighboring bulk actions had the same direct disconnect bypass
Confidence: high
Scope-risk: narrow
Directive: Keep bulk tab closing routed through the same running-session policy as single tab closing
Tested: cd desktop && bun run test src/components/layout/TabBar.test.tsx --pool forks --poolOptions.forks.singleFork=true
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: git diff --check
Not-tested: Manual desktop UI click-through
2026-05-19 00:20:41 +08:00
程序员阿江(Relakkes)
f77ab9b0a0 Stabilize workspace preview coverage timing
The workspace long-file preview test regularly crosses ten seconds under V8 coverage instrumentation even though the behavior passes. Raising its explicit timeout keeps the coverage lane aligned with the runtime it already observes.

Constraint: PR verification runs the full desktop test suite under coverage, which adds substantial highlighting overhead for this fixture.

Rejected: Change the component rendering path | the failure was a coverage-mode test timeout, not a product regression.

Confidence: high

Scope-risk: narrow

Tested: cd desktop && bun run test -- src/components/workspace/WorkspacePanel.test.tsx --run --coverage --coverage.reporter=json-summary --coverage.reporter=lcov --coverage.reportsDirectory=/tmp/cc-haha-workspace-panel-coverage --testTimeout=20000

Tested: bun run check:native

Tested: bun run check:coverage

Tested: git diff --check
2026-05-19 00:17:59 +08:00
程序员阿江(Relakkes)
f497a08453 Clear stale ChatGPT OAuth fallback links
Manual authorization links now behave as transient recovery state: a fresh login clears older URLs, a successful copy clears the exposed URL before polling, and logged-in status defensively drops any stale manual link.

Constraint: OAuth authorization URLs should not survive beyond the active recovery action.

Rejected: Keep the copy button after a successful copy | that leaves an expired authorization URL visible after later auth state changes.

Confidence: high

Scope-risk: narrow

Tested: cd desktop && bun run test -- src/components/settings/ChatGPTOfficialLogin.test.tsx src/stores/hahaOpenAIOAuthStore.test.ts

Tested: cd desktop && bun run test -- src/components/settings/ChatGPTOfficialLogin.test.tsx src/stores/hahaOpenAIOAuthStore.test.ts src/__tests__/generalSettings.test.tsx --testNamePattern "ChatGPT|OpenAI OAuth|Providers tab|ChatGPTOfficialLogin|hahaOpenAIOAuthStore"

Tested: cd desktop && bun run lint

Tested: git diff --check
2026-05-19 00:05:59 +08:00
程序员阿江(Relakkes)
d2ff9d0c00 Preserve ChatGPT OAuth fallback authorization links
When the desktop shell cannot open a browser, the ChatGPT OAuth component keeps the transient authorization URL available for copy and starts polling after that explicit fallback action.

Constraint: OAuth authorization URLs are transient UI state and must not be persisted.

Rejected: Tell users to find the URL in server logs | the desktop flow does not expose those logs as the recovery surface.

Confidence: high

Scope-risk: narrow

Tested: cd desktop && bun run test -- src/components/settings/ChatGPTOfficialLogin.test.tsx src/stores/hahaOpenAIOAuthStore.test.ts

Tested: cd desktop && bun run test -- src/components/settings/ChatGPTOfficialLogin.test.tsx src/stores/hahaOpenAIOAuthStore.test.ts src/__tests__/generalSettings.test.tsx --testNamePattern "ChatGPT|OpenAI OAuth|Providers tab|ChatGPTOfficialLogin|hahaOpenAIOAuthStore"

Tested: cd desktop && bun run lint

Tested: git diff --check
2026-05-19 00:04:11 +08:00
程序员阿江(Relakkes)
83ee4c09ec Add desktop ChatGPT OAuth controls
The desktop app now has a dedicated OpenAI OAuth API client and store mirroring the existing Claude Official flow. The component uses the existing browser-open plus polling pattern and never exposes token material to the UI.

Constraint: OAuth status responses must not return token bodies

Rejected: Reuse Claude OAuth store | the status shape and endpoint differ

Confidence: high

Scope-risk: narrow

Tested: cd desktop && bun run test -- src/stores/hahaOpenAIOAuthStore.test.ts

Tested: git diff --check
2026-05-18 23:53:11 +08:00
程序员阿江(Relakkes)
6e1ba2753e Represent ChatGPT Official as a desktop built-in provider
ChatGPT Official uses a real provider id, so desktop UI consumers cannot keep treating only null as the official lane. The model selector now exposes the GPT catalog for that built-in provider, and Settings renders a separate active card instead of folding it into Claude Official state.

Constraint: Claude Official remains represented by activeId null.

Rejected: Reuse the Claude Official card for ChatGPT Official | it hides the active ChatGPT provider and shows the wrong login surface.

Confidence: high

Scope-risk: narrow

Tested: cd desktop && bun run test -- src/components/controls/ModelSelector.test.tsx

Tested: cd desktop && bun run test -- src/__tests__/generalSettings.test.tsx --testNamePattern "Providers tab"

Tested: git diff --check
2026-05-18 23:45:53 +08:00
程序员阿江(Relakkes)
d7315df18b Keep agent background progress inside agent cards
Desktop receives task lifecycle events for local subagents in addition to the visible Agent tool call. Rendering those events as standalone transcript cards exposed internal task types such as local_agent and duplicated the Agent surface. The store now keeps agent lifecycle state for Agent tool notifications without inserting separate transcript background cards, while non-agent background work remains visible with user-facing labels.

The long workspace preview test now has a wider timeout so coverage mode does not fail a behaviorally passing case.

Constraint: CLI task events use internal task_type values such as local_agent and local_bash.
Rejected: Rename local_agent in the standalone card | still leaves duplicate Agent and task cards for one subagent.
Confidence: high
Scope-risk: moderate
Directive: Do not render local_agent or remote_agent as standalone transcript cards unless the Agent tool card no longer exists.
Tested: bun run verify passed=8 failed=0 skipped=2
Not-tested: Live provider-backed desktop session smoke.
2026-05-18 23:41:57 +08:00
程序员阿江(Relakkes)
71ce980f1c fix: make goal completion durable through stop hooks
The goal command now uses a session Stop hook as the durable completion mechanism and removes the separate evaluator path. Completion output is persisted as local command output so CLI streams, transcripts, and desktop history all see the same completed state.

Desktop keeps active goals in a compact header strip and lets completed goals render in the conversation flow, which avoids the old duplicate pinned card behavior while preserving visibility for running goals.

Constraint: /goal state has to survive transcript replay and desktop session restore.
Rejected: Keep the legacy evaluator alongside Stop hooks | duplicate completion paths made desktop state drift and hid the final card.
Confidence: high
Scope-risk: moderate
Directive: Keep goal completion events on the local_command transcript path unless desktop history restore is changed at the same time.
Tested: bun test src/goals/goalState.test.ts src/query/stopHooks.test.ts src/commands/goal/goal.test.tsx src/commands/headless.test.ts src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/sessions.test.ts
Tested: bun run check:server
Tested: bun run check:desktop
Tested: real /tmp /goal CLI run 98fb4bb2-52da-4850-bc8e-e5e1cc0e4c51 with deepseek-v4-pro and WebUI screenshot verification
Not-tested: Remote release workflow
2026-05-18 22:30:18 +08:00
程序员阿江(Relakkes)
8ee4bf682a fix: restore local main fixes stranded in worktrees
Bring back the narrow, previously validated sidebar recovery and Windows x64 sidecar compatibility fixes that were present on worktree branches but absent from the canonical local main checkout. The broader worktree branch is intentionally not merged because it would replay unrelated history and risk dropping current main work.

Constraint: The local main worktree already contains unrelated Settings and portable storage edits, so only the recovery hunks are staged.

Rejected: Merge 1506086927/main wholesale | its history includes broad rollback/replay commits that would remove current main changes.

Confidence: high

Scope-risk: narrow

Directive: Recover future worktree fixes by cherry-picking or staging narrow hunks; do not merge drifted worktree branches wholesale.

Tested: cd desktop && bun run test -- --run src/components/layout/Sidebar.test.tsx scripts/build-sidecars.test.ts

Not-tested: Full bun run verify due unrelated dirty Settings/portable-storage worktree changes.
2026-05-18 22:28:04 +08:00
程序员阿江(Relakkes)
4b76319781 Merge background task UI state fix into main
This brings the detached worktree fix for desktop background-task visibility into the local main branch while preserving the main branch updates that landed after the worktree base.

Constraint: The source worktree was detached from an older main ancestor
Rejected: Fast-forward merge | local main has newer commits and cannot fast-forward to the detached worktree commit
Confidence: high
Scope-risk: narrow
Tested: bun run verify
Not-tested: No live provider baseline was run because this was a desktop UI state fix and the PR gate used non-live checks
2026-05-18 15:14:20 +08:00
程序员阿江(Relakkes)
3511f30978 fix(desktop): prevent completed turns from hiding active background work
The CLI can emit a completed assistant turn while local background bash or task work continues. The desktop transcript and header now keep that background activity visible without converting the composer into a global stop state.

Constraint: Preserve CLI turn semantics where message_complete means the current assistant turn is done, not every background task is finished
Rejected: Treat background tasks as composer-active generation | this blocks follow-up input even though the CLI keeps the session responsive
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- --run src/components/chat/MessageList.test.tsx src/components/chat/ChatInput.test.tsx src/pages/ActiveSession.test.tsx
Tested: bun run check:desktop
Tested: bun run verify
2026-05-18 15:12:49 +08:00
程序员阿江(Relakkes)
0506452a1b Merge commit '2b5bf56475c5a8afd2ccd40df4e63e58d4d88c87' 2026-05-18 14:41:13 +08:00
程序员阿江(Relakkes)
2b5bf56475 Make selection actions track selected text instead of the cursor
The add-to-chat popover was keyed off the mouse-up location, so broad selections could cover the selected text and stale popovers could remain visible. Shared placement and dismissal logic now anchors the action to the selected range, prefers the space above the selection like Codex App, and clears stale selections on outside clicks.

Constraint: Desktop chat and workspace previews share the same selection affordance and must keep their existing add-reference behavior.

Rejected: Keep cursor-based placement | it can hide selected text and makes multi-line selections unpredictable.

Confidence: high

Scope-risk: moderate

Directive: Keep future selection actions range-anchored and outside-click dismissible across chat and workspace surfaces.

Tested: bun run check:desktop

Tested: Browser verification on http://127.0.0.1:5174 showed the popover above selection, no overlap, and outside click clearing it.

Not-tested: Native packaged Tauri window.

Related: https://github.com/NanmiCoder/cc-haha/issues/484
2026-05-18 14:40:56 +08:00
程序员阿江(Relakkes)
6ff76171af fix: preserve unsent desktop prompts across tab switches
Desktop non-session tabs unmount the active chat composer, so component-local draft refs disappeared before users returned to the session. Move the draft into per-session chat state and save it at session-switch and unmount boundaries, then clear it after submit.

Constraint: Settings and terminal tabs replace ActiveSession in ContentRouter.
Rejected: Persist drafts in localStorage | would add unnecessary persistence migration surface for a runtime-only draft.
Confidence: high
Scope-risk: narrow
Directive: Keep composer draft state scoped per session; do not move it back into component-only refs without covering unmount and remount.
Tested: bun run test src/components/chat/ChatInput.test.tsx --run
Tested: bun run check:desktop
Tested: bun run verify
2026-05-18 14:40:52 +08:00
程序员阿江(Relakkes)
331372e866 Keep multi-question replies isolated in AskUserQuestion
AskUserQuestion used one free-text value for every tab, so typing a custom response on one question leaked into the next and option selection could erase another question's answer. Store free-text answers per question index and submit the per-question answer map so mixed custom and option replies stay intact.

Constraint: Multiple questions share one AskUserQuestion component instance
Rejected: Keep a single free-text fallback | it cannot distinguish which question owns the custom reply
Confidence: high
Scope-risk: narrow
Directive: Keep custom text and option state scoped to a question index when changing this component
Tested: Reproduced in Web UI on 127.0.0.1:1420 with server 127.0.0.1:4678 and deepseek-v4-pro in /private/tmp
Tested: cd desktop && bun run test -- AskUserQuestion.test.tsx --run
Tested: bun run verify
Not-tested: none
2026-05-18 14:23:47 +08:00
程序员阿江(Relakkes)
8ecc757729 Hide unfinished goal command entrypoints
The goal implementation is being kept in the tree for later redesign, but the CLI and desktop slash-command surfaces should not advertise or route new users into it while the long-running goal experience is incomplete. Existing transcript parsing and goal event rendering remain intact so historical sessions still load.

Constraint: Keep the implementation code available for future iteration while removing discoverable entrypoints.
Confidence: high
Scope-risk: narrow
Directive: Do not re-expose /goal until the persistent goal/runtime design is revisited.
Tested: bun test src/commands/headless.test.ts
Tested: cd desktop && bun run test -- composerUtils.test.ts pages.test.tsx
Tested: NODE_ENV=test ANTHROPIC_API_KEY=dummy bun -e getCommands assertion for hidden goal command
2026-05-17 00:05:50 +08:00
程序员阿江(Relakkes)
d2d27b3845 Avoid noisy stopped states for background agents
Goal sessions were showing stopped background-agent cards because the parent model used TaskStop after reading enough partial review output. That writes a killed task notification, which the desktop then rendered like a failure.

TaskStop and async agent launch guidance now make cancellation an exceptional action rather than a normal cleanup step. Stopped background-agent transcript cards also render as neutral interrupted events instead of error-styled failures.

Constraint: Background agent task notifications must remain truthful; killed tasks still surface as stopped instead of being hidden.

Rejected: Hide stopped notifications in /goal sessions | this would mask real user cancellations and runaway-task stops.

Confidence: medium

Scope-risk: narrow

Directive: Do not encourage parent turns to kill background agents only because partial output was read.

Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx -t "renders stopped background agents as neutral transcript events"

Tested: cd desktop && bun run lint

Tested: bun run check:server
2026-05-16 23:04:02 +08:00
程序员阿江(Relakkes)
45b6674726 Place goal background work in the transcript
Goal runs spawn visible background work while the conversation is active, so the desktop should show those events at their actual transcript position instead of pinning a separate panel above the chat. The UI now renders background task records as inline message events, repairs restored task state from transcript notifications, and keeps later task updates on the original event card.

Constraint: Existing transcripts only persist terminal task notifications, so the server now preserves their timestamps for deterministic restore ordering.

Rejected: Keep the page-level background-agent panel | it occluded the session content and duplicated message-flow information.

Confidence: high

Scope-risk: moderate

Directive: Background task message timestamps intentionally stay fixed after insertion; update the task content without moving the card in the transcript.

Tested: cd desktop && bun run test -- src/stores/chatStore.test.ts src/pages/ActiveSession.test.tsx src/components/chat/MessageList.test.tsx

Tested: cd desktop && bun run lint

Tested: bun test src/server/__tests__/sessions.test.ts
2026-05-16 21:38:52 +08:00
程序员阿江(Relakkes)
2d7d11d822 Reduce formula-heavy chat render cost
Formula-heavy conversations were still expensive after removing the fixed-height virtual chat window because every render repeated KaTeX expansion and HTML post-processing. Keep readable math, but reduce the DOM and CPU cost by rendering KaTeX without the hidden MathML copy, caching repeated formula output, and memoizing enhanced HTML parts outside the JSX render path.

Constraint: H5 streaming scroll follow behavior must remain unchanged
Rejected: Restore fixed-height virtualization | it caused blank gaps for variable-height math and code messages
Rejected: Disable LaTeX rendering | formula readability is a user-facing feature
Confidence: medium
Scope-risk: narrow
Directive: Do not reintroduce estimated-height chat virtualization without measured row heights for math-heavy messages
Tested: cd desktop && bun run test -- MarkdownRenderer.test.tsx MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
Not-tested: Manual desktop scroll trace on the user's formula-heavy production conversation
2026-05-16 17:14:15 +08:00
程序员阿江(Relakkes)
5e899bb1a1 Restore reliable long chat scrolling
The fixed-height virtual window introduced after v0.2.6 assumed every rendered chat item was 96px tall. Real desktop messages include markdown, math, code blocks, tool groups, and agent cards with highly variable heights, so scrolling history could replace real content with incorrectly sized blank spacer regions and make tab restoration janky.

Remove that unsafe virtualization layer and keep the existing direct scroll restoration behavior. The regression tests now keep long mixed-height transcripts and tool-call histories mounted so variable-height rows cannot disappear behind fake spacer math again.

Constraint: Desktop chat rows have unbounded variable height from markdown, KaTeX, code, tool, and agent output.
Rejected: Tune the 96px estimate | any constant row height still fails for mixed markdown and tool output.
Rejected: Add measured virtualization now | larger change surface than needed for the production white-gap regression.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce chat virtualization without real row-height measurement and scroll restore coverage.
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
2026-05-16 16:45:59 +08:00
程序员阿江(Relakkes)
7f74f1bde7 Prevent removed goal subcommands from replacing goals
The simplified /goal surface only supports setting a condition and clearing it. Removed subcommand names such as status were still valid free-form objectives, so a user trying the old query flow could overwrite the real goal with a goal named status and make the desktop state look stuck in progress.

Constraint: /goal should stay as /goal <condition> and /goal clear for the prelaunch simplified UX

Rejected: Reintroduce /goal status | it expands the command surface the product direction intentionally removed

Confidence: high

Scope-risk: narrow

Directive: Do not add pseudo subcommands back to the desktop picker unless the CLI command surface is deliberately expanded again

Tested: bun test src/commands/goal/goal.test.tsx src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts src/server/__tests__/ws-memory-events.test.ts

Tested: cd desktop && bun run test -- --run src/components/chat/composerUtils.test.ts src/__tests__/pages.test.tsx src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx
2026-05-16 14:00:48 +08:00