217 Commits

Author SHA1 Message Date
程序员阿江(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)
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)
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)
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)
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)
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)
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)
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
程序员阿江(Relakkes)
5c5c5933f0 Preserve composer command popovers after drag-drop support
Drag-drop support made the composer panel clip overflow so the
stateful slash and file popovers still opened but were hidden above
the panel. Restore visible overflow on the drop target while keeping
the drop overlay and attachment flow intact, then lock both active
and empty composer surfaces with regression coverage.

Constraint: Drag-drop attachments must continue using the existing path-only composer pipeline
Rejected: Move slash and file menus outside the composer tree | broader portal refactor not needed for this regression
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce overflow clipping on composer panels without proving bottom-full popovers still render
Tested: cd desktop && bun run test -- --run src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: cd desktop && bun run lint
Tested: Browser smoke at http://127.0.0.1:1421/?serverUrl=http%3A%2F%2F127.0.0.1%3A3456 for / and @src popovers
Tested: bun run check:desktop
2026-05-16 13:49:21 +08:00
程序员阿江(Relakkes)
390c18d82c Let desktop users attach files by dropping them into the composer
A user-requested drag target should reuse the existing attachment pipeline instead
of creating a second upload path. The composer now handles browser DataTransfer
drops and Tauri native drag-drop events, while keeping desktop attachments
path-only so large files are not serialized into chat payloads. The fallback
/goal metadata is also aligned with the existing desktop command surface because
the desktop gate exercises that menu while validating composer behavior.

Constraint: Desktop attachments must remain path-only to avoid inflated websocket payloads
Rejected: Read dropped desktop files with FileReader | would reintroduce large inline data payloads
Confidence: high
Scope-risk: moderate
Directive: Do not replace the Tauri drag-drop path conversion with data URLs without rerunning payload-size regression coverage
Tested: cd desktop && bun run test -- --run src/lib/composerAttachments.test.ts src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: bun run check:desktop
Tested: agent-browser overlay and dropped-chip smoke at http://127.0.0.1:1421/?serverUrl=http%3A%2F%2F127.0.0.1%3A3456
Tested: bun run verify
Not-tested: Finder-to-packaged-.app manual drag smoke
2026-05-16 13:18:59 +08:00
程序员阿江(Relakkes)
818dc7a178 Merge simplified goal command into main
This merge carries the set/clear /goal behavior from the Codex worktree into local main while preserving main's memory-style desktop goal cards and compact active-goal banner.

Constraint: Local main already had unrelated version/Tauri/release-note worktree changes and a newer goal UI style.\nRejected: Overwrite main's goal card UI with the worktree version | user wanted the memory-style visual direction already present on main.\nConfidence: high\nScope-risk: moderate\nTested: 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 src/server/__tests__/conversations.test.ts\nTested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx src/components/chat/composerUtils.test.ts\nNot-tested: Full bun run verify on main was not rerun after merge because unrelated dirty release/version files are present in the main worktree.
2026-05-16 03:35:53 +08:00
程序员阿江(Relakkes)
606f3c0dc2 Simplify goal command into an immediate set-clear loop
The previous /goal surface exposed management subcommands that made the CLI and desktop behavior hard to reason about. This keeps the user-facing command close to Claude Code's set/clear interaction while preserving historical transcript hydration and the existing evaluator loop.

Constraint: Desktop must see a live goal event before the model reaches the next tool call.\nRejected: Keep status/pause/resume/complete slash subcommands | requested UX is only set and clear.\nConfidence: high\nScope-risk: moderate\nDirective: Keep /goal user-facing syntax compact; extend internals without expanding the slash picker unless product explicitly asks.\nTested: 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 src/server/__tests__/conversations.test.ts\nTested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx src/components/chat/composerUtils.test.ts\nTested: bun run check:server\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: ./bin/claude-haha -p '/goal smoke verify immediate goal output' --output-format stream-json --include-partial-messages --max-turns 1 --no-session-persistence --verbose emitted immediate system.local_command_output before model stream\nTested: ./bin/claude-haha -p '/goal clear' --output-format stream-json --include-partial-messages --max-turns 1 --no-session-persistence --verbose exited with duration_api_ms=0 and No active goal\nNot-tested: Browser click smoke was blocked by Chrome DevTools transport closing and Computer Use cgWindowNotFound on this machine.
2026-05-16 03:28:48 +08:00
程序员阿江(Relakkes)
40852a804d Align goal UI with memory activity blocks
Goal status previously used a standalone success-card treatment, which made /goal look visually detached from the transcript. Rework the active goal strip and goal event cards to use the same compact memory/tool-result visual language.

Constraint: Preserve /goal state visibility while reducing alert-like chrome.

Rejected: Continue tuning only goal colors | the layout still read as a separate alert instead of transcript state.

Confidence: high

Scope-risk: narrow

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

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

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run build
2026-05-16 02:28:14 +08:00
程序员阿江(Relakkes)
d76c09c3d6 Restore /goal as a single desktop slash command
The desktop composer had treated /goal lifecycle arguments as standalone slash-command rows. That made the UI look like /goal had second-level commands even though the CLI contract is one /goal command with arguments.

Constraint: Keep /goal status/pause/resume/complete/clear usable as CLI arguments.

Rejected: Keep pseudo subcommands in the picker | it contradicts the slash command model and confuses objective entry.

Confidence: high

Scope-risk: narrow

Tested: cd desktop && bun run test -- --run src/components/chat/composerUtils.test.ts src/__tests__/pages.test.tsx src/pages/EmptySession.test.tsx

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run build
2026-05-16 01:59:06 +08:00
程序员阿江(Relakkes)
7dc0b59784 Refine goal UI around memory theme tokens
Goal status needed to read like persistent agent state, not a success alert. Reuse the Memory surface language and expose goal-specific aliases so the visual treatment follows each supported desktop theme.\n\nConstraint: Must keep /goal visible in both active-session chrome and transcript events.\nRejected: Continue using success tokens | they made the UI look like a warning/success card and clashed with white/dark themes.\nConfidence: high\nScope-risk: narrow\nTested: cd desktop && bun run test -- --run src/pages/ActiveSession.test.tsx src/components/chat/MessageList.test.tsx src/theme/globals.test.ts\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: Chrome screenshot fixture for light, white, and dark goal UI tokens.
2026-05-16 01:49:38 +08:00
程序员阿江(Relakkes)
291dd3e07d Refine goal UI around memory theme tokens
Goal status needed to read like persistent agent state, not a success alert. Reuse the Memory surface language and expose goal-specific aliases so the visual treatment follows each supported desktop theme.\n\nConstraint: Must keep /goal visible in both active-session chrome and transcript events.\nRejected: Continue using success tokens | they made the UI look like a warning/success card and clashed with white/dark themes.\nConfidence: high\nScope-risk: narrow\nTested: cd desktop && bun run test -- --run src/pages/ActiveSession.test.tsx src/components/chat/MessageList.test.tsx src/theme/globals.test.ts\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: Chrome screenshot fixture for light, white, and dark goal UI tokens.
2026-05-16 01:44:42 +08:00
程序员阿江(Relakkes)
929ddd0ebe Refine goal session presentation
Goal sessions need to feel like an ongoing agent-loop state, not a large success alert dropped into the chat. The UI now uses compact status and event treatments, and session titles stay anchored to the original goal objective instead of later /goal status commands.

Constraint: /goal history uses local_command transcript entries and later status checks can append fresh AI titles.
Rejected: Keep the existing green cards | they duplicate the objective, consume too much vertical space, and read as one-off notifications instead of durable loop state.
Confidence: high
Scope-risk: narrow
Directive: Goal status UI should remain compact and stateful; do not reintroduce large centered cards for routine lifecycle events.
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Browser preview at 127.0.0.1:5179 with real session 3e9117d5-b792-43c9-bf57-7aec2b124f7e on desktop and mobile viewport
Not-tested: Packaged macOS app rebuild.
2026-05-16 01:28:09 +08:00
程序员阿江(Relakkes)
e77dd6d3cb Expose goal subcommands in desktop composer
The CLI supports /goal status, pause, resume, complete, clear, and --tokens as arguments, but the desktop composer only showed the top-level /goal entry and closed completion after the space. Users could run the commands if they knew them, yet the UI made them look unavailable.

Constraint: Keep CLI semantics unchanged; this is a desktop discovery and completion fix only

Rejected: Add separate CLI slash commands for every goal subcommand | would diverge from the actual command surface

Confidence: high

Scope-risk: narrow

Directive: Keep /goal subcommands as argument completions unless the CLI command model gains first-class nested slash commands

Tested: cd desktop && bun run test -- --run src/components/chat/composerUtils.test.ts src/components/chat/ChatInput.test.tsx

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

Tested: cd desktop && bun run lint

Not-tested: live desktop manual interaction after restarting the app
2026-05-16 00:21:06 +08:00
程序员阿江(Relakkes)
b4a5c09b11 fix: expose plugin skills in new session slash commands
New desktop sessions populated slash suggestions from the session endpoint before the CLI had emitted init metadata. That endpoint only scanned user and project skill directories, while the plugin settings view and global skills API already saw enabled plugin skills such as superpowers. The session endpoint now reuses the global skill listing and merges it with any cached CLI slash commands, and both composer surfaces rank command-name matches before broad description matches so /su surfaces superpowers first.

Constraint: New sessions need plugin skills before the first real user turn starts the CLI.

Rejected: Start or restart a hidden CLI process on plugin enable | heavier than needed and still misses the REST slash-command fallback path.

Confidence: high

Scope-risk: moderate

Directive: Keep session slash commands and /api/skills on the same skill discovery path when changing plugin skill loading.

Tested: bun test src/server/__tests__/skills.test.ts src/server/__tests__/plugins.test.ts src/server/__tests__/sessions.test.ts

Tested: cd desktop && bun run test --run src/components/chat/composerUtils.test.ts src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx

Tested: bun run check:server

Tested: cd desktop && bun run lint

Not-tested: Manual desktop click-through in the packaged app.
2026-05-16 00:03:44 +08:00
程序员阿江(Relakkes)
62713f30de Merge commit '376fc6de' 2026-05-15 23:27:02 +08:00
程序员阿江(Relakkes)
376fc6de62 Make /goal durable across CLI and desktop sessions
The goal flow needs to drive the agent loop and remain visible after desktop reconnects, so this change treats /goal output as a structured lifecycle signal across CLI, server, and desktop history restoration.

Constraint: The current TypeScript CLI does not share Codex's Rust app-server thread-goal database, so persistence is reconstructed from the existing session transcript.

Rejected: UI-only goal badges | would leave the CLI self-loop unable to recover active or completed goals after process restart.

Rejected: Add a new persisted store | larger migration surface than needed for the existing transcript-backed session model.

Confidence: high

Scope-risk: moderate

Directive: Keep future goal state changes mirrored in transcript-visible local command output or introduce a real migration-backed store.

Tested: bun test src/goals/goalEvaluator.test.ts src/goals/goalState.test.ts

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

Tested: bun run verify

Not-tested: Live provider baseline with a real model-driven goal loop.
2026-05-15 23:26:54 +08:00
程序员阿江(Relakkes)
80291715b4 fix: stop restored chats snapping to latest
Restored transcripts can still present stale running state while their final markdown and tool output continue to reflow. Resize-driven auto-follow now stays enabled only for live resizable output, so readers can scroll up after completion while active streams still remain pinned.

Constraint: Old H5 sessions can reconnect with stale non-idle UI state even when the transcript has an end_turn.
Rejected: Disable resize following entirely | active H5 streaming would regress when rendered content grows after tokens arrive.
Confidence: high
Scope-risk: narrow
Directive: Do not broaden ResizeObserver auto-follow without a restored completed-session regression test.
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Browser smoke on session 85862799-ec6b-43d0-b3b8-6e9fd9130954 kept scrollTop at 320 after content resize.
2026-05-15 21:58:31 +08:00
程序员阿江(Relakkes)
5871af11d4 Always reveal newly sent chat prompts
Sending from history must move the transcript to the new turn even if session state has not yet advanced from idle. The previous guard tied the jump to chatState, so a user_text tail that landed before thinking/streaming would be skipped permanently because the tail id no longer changed.

Constraint: The same MessageList path serves desktop and H5 chat.

Rejected: Wait for chatState to become thinking before scrolling | that misses updates where the message id is already recorded.

Confidence: high

Scope-risk: narrow

Directive: New user_text tail messages are intentional navigation events and must override historical scroll position.

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-15 20:41:32 +08:00
程序员阿江(Relakkes)
76d472376b Keep H5 chat scrolling inside the message pane
iPhone Chrome uses the iOS WebKit viewport model, so using scrollIntoView on the transcript sentinel can ask the browser to scroll ancestor containers, including the page viewport. That is risky with the fixed bottom composer and can expose blank page space when the latest button is clicked. This keeps all latest-message movement scoped to the chat scroll element and follows post-render height changes while streaming.

Constraint: H5 and desktop share MessageList, but the H5 browser runs through iOS WebKit viewport behavior.

Rejected: Keep bottomRef.scrollIntoView with container options | container support is not reliable enough and the default still targets all scrollable ancestors.

Confidence: high

Scope-risk: narrow

Directive: Do not reintroduce transcript scrollIntoView without real iPhone Chrome verification.

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-15 20:38:54 +08:00
程序员阿江(Relakkes)
7bc22e971a Keep chat pinned to the newest turn during active output
H5 browsers can lag behind smooth scroll animations when streaming text grows, and sending a new prompt should always return the reader to the live turn even if they were browsing history. The message list now writes the scroll container directly while preserving the existing opt-out for manual history reading during assistant streaming.

Constraint: H5 and desktop share MessageList behavior
Rejected: Force-scroll on every streaming update | would break reading historical content during long replies
Confidence: high
Scope-risk: narrow
Directive: Preserve manual history reading except when a new user prompt is appended
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Not-tested: Full check:desktop remains blocked by the existing vite-config color-mix assertion in globals.css
2026-05-15 19:37:07 +08:00