150 Commits

Author SHA1 Message Date
程序员阿江(Relakkes)
664c6ebd9f Prevent desktop turns from hanging after CLI exits
A desktop session can pass the startup grace window and then lose its CLI subprocess before a result arrives. Previously that path only deleted the active process record, leaving the WebSocket client in an active turn with no terminal message. The process-exit handler now emits a synthetic CLI error result through the existing translation path so the UI receives both an error and message_complete. The mock SDK fixture can reproduce post-startup exits, and the integration test locks the terminal event behavior.

Constraint: The frontend already relies on result is_error translation to emit error plus message_complete.

Rejected: Add a separate WebSocket process-exit message type | it would duplicate existing result-error handling and require another frontend state path.

Confidence: high

Scope-risk: narrow

Directive: Keep process-exit failures on the same result-error path unless the client protocol gains a dedicated terminal error event.

Tested: bun test src/server/__tests__/conversations.test.ts

Tested: cd desktop && bun run lint

Tested: agent-browser against http://127.0.0.1:1421 with mock CLI late exit; page showed CLI process exited unexpectedly and composer became editable again
2026-04-28 16:38:16 +08:00
程序员阿江(Relakkes)
1631c449f6 fix: tighten desktop slash command edge cases
Desktop slash command handling needed a few follow-up guards after adding
CLI parity. Bare /clear remains a local desktop reset, but /clear with
arguments is rejected instead of silently discarding user text. Clear also
resets cached slash commands, compact boundaries preserve CLI-provided text,
and the help panel now discloses when the More section is truncated.

Constraint: Preserve the desktop /clear fast path without accepting accidental arguments
Rejected: Let /clear arguments fall through to the CLI | the CLI command still clears context and would keep the footgun
Confidence: high
Scope-risk: narrow
Directive: Keep desktop-local slash command guards in sync with CLI command argument semantics
Tested: cd desktop && bun run test src/stores/chatStore.test.ts src/__tests__/pages.test.tsx --run -t "clears local desktop chat state|ActiveSession routes /help"
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run lint
Tested: git diff --check
Not-tested: Full pages.test.tsx because unrelated locale-sensitive assertions are already present in that file
2026-04-28 16:38:16 +08:00
程序员阿江(Relakkes)
caa164b62c fix: make desktop slash commands behave like the CLI
Desktop had no reliable handling for common local slash commands, so
commands like /clear, /help, /context, /cost, and /compact either looked
unresponsive or lost their CLI-specific side effects. This routes desktop
commands by behavior: local panels stay local, stateful clear resets the
session transcript, and CLI-local outputs are rendered in chat.

Constraint: Preserve existing CLI semantics for prompt, local, local-jsx, and compact slash commands
Rejected: Send every slash command through the normal chat path | local commands need desktop UI or session state side effects
Confidence: high
Scope-risk: moderate
Directive: Keep hidden aliases such as /plugins out of the visible command list unless they become canonical CLI commands
Tested: cd desktop && bun run test src/components/chat/composerUtils.test.ts src/stores/chatStore.test.ts --run
Tested: cd desktop && bun run test src/__tests__/pages.test.tsx --run -t "ActiveSession routes /plugin|ActiveSession routes /help|EmptySession slash picker includes dynamic skills"
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run lint
Tested: agent-browser E2E for /help, /plugin, /plugins, /cost, /clear, /context, /compact, /mcp, and normal text input
Not-tested: Root tsc project because it currently includes generated Tauri target assets and extracted native files
2026-04-28 16:38:16 +08:00
Relakkes Yang
869aeb0e77 fix: honor session model for provider runtime 2026-04-27 10:57:25 +08:00
Relakkes Yang
55c11b481b fix(server): preload macros for Windows dev CLI 2026-04-25 13:58:02 +08:00
程序员阿江(Relakkes)
761031c036 fix: Make desktop MCP tools ready before first turn
Desktop sessions could show MCP configuration in the UI while the CLI SDK child process still started cold on the first user message. Start eligible desktop chat sessions as soon as their websocket is connected, keep init metadata cached but muted, and wait briefly for MCP startup in SDK print mode so turn one sees the tools.

Constraint: Desktop wraps the existing CLI SDK bridge rather than owning MCP startup directly.
Rejected: Prewarm every restored tab | synthetic tabs such as settings would start unnecessary CLI subprocesses.
Confidence: high
Scope-risk: moderate
Directive: Do not remove the synthetic-tab guard without validating restored settings and scheduled-task tabs in the browser.
Tested: bun test src/server/__tests__/conversations.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/mcp.test.ts
Tested: cd desktop && bun run test -- chatStore.test.ts mcpSettings.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: agent-browser E2E new session prewarm plus first message reuse with mock CLI
Not-tested: Full desktop Vitest suite still has pre-existing English-vs-Chinese copy assertion failures.
2026-04-24 17:11:03 +08:00
程序员阿江(Relakkes)
6d40b2befa fix: Keep desktop chats on selected provider runtimes
Desktop sessions can switch provider and model while a CLI subprocess is already alive, so the server now serializes runtime restarts and marks provider-managed launches to prevent stale settings env from overriding the selected provider. Provider settings also write API key env consistently and clear stale managed keys before syncing.

This includes the related desktop/docs brand asset refresh and keeps the desktop locale default in Chinese, with tests updated to match the current provider semantics.

Constraint: Session-scoped model selection must win over cc-haha/settings.json and inherited ANTHROPIC_* values.
Rejected: Store the selected model as a global provider activeModel | chat runtime selection is per session.
Confidence: high
Scope-risk: moderate
Directive: Do not remove CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST without validating Desktop provider switching against stale settings env.
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/conversations.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/providers-real.test.ts
Tested: cd desktop && bun run test src/stores/settingsStore.test.ts
Tested: cd desktop && bun run lint
Tested: git diff --check
Not-tested: Full desktop production package/signing.
2026-04-24 13:24:31 +08:00
程序员阿江(Relakkes)
773a8a703f Keep desktop chats on the selected provider/model
The desktop model picker now stores a session-scoped provider/model selection instead of relying on the global active provider. That selection is replayed on connect, passed into the CLI startup path, and preserved across turns until the user changes it again.

To make that true end-to-end, the server now restarts the session process when runtime selection changes, injects provider-scoped env for third-party providers, and routes proxy traffic by provider id. The selector UI was also tightened so provider grouping stays visible while the actual model choice remains readable.

Constraint: Different providers can expose the same model id, so chat runtime selection cannot be derived from model id alone
Constraint: A desktop session reuses one CLI subprocess across turns, so runtime changes must restart that process to take effect
Rejected: Keep using Settings active provider as the chat selector | conflates defaults with live session state and breaks overlapping models
Rejected: UI-only runtime switching without server restart | later turns would continue using the old CLI subprocess configuration
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep provider defaults and session runtime overrides separate, and preserve provider-scoped proxy routing when extending model selection surfaces
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test src/stores/chatStore.test.ts
Tested: cd desktop && bun run test src/__tests__/generalSettings.test.tsx
Tested: bun test src/server/__tests__/conversation-service.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: bun -e "await import('./src/server/services/titleService.ts'); await import('./src/server/ws/handler.ts')"
Not-tested: Real third-party provider round-trip from the desktop UI against a live upstream account
2026-04-23 13:41:27 +08:00
程序员阿江(Relakkes)
5fea7033e3 Expose the bundled desktop CLI outside the app
The desktop app already shipped a bundled sidecar, but only desktop-managed
sessions could see it. This change installs a `claude-haha` launcher into the
user bin directory, wires PATH setup so new terminals can resolve it, and keeps
desktop installer sessions aligned on the same bundled sidecar resolution path.
The desktop install surface now also reports whether the launcher is ready or
still waiting on a terminal restart.

Constraint: The worktree already contains unrelated icon, docs, and UI changes, so this commit stages only the bundled CLI launcher slice
Rejected: Tell users to install the official Claude CLI separately | it breaks the desktop out-of-box install story
Rejected: Keep the bundled CLI reachable only inside desktop-managed shells | system terminals would still be unable to call the packaged runtime
Rejected: Symlink directly into the app bundle instead of copying to user bin | moving or replacing the app bundle would leave a stale launcher behind
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Proxy-backed non-Anthropic providers still depend on the desktop server; do not assume this launcher makes every provider fully standalone
Tested: bun test src/server/__tests__/desktop-cli-launcher.test.ts src/server/__tests__/settings.test.ts; bun test src/server/__tests__/conversation-service.test.ts; bun test src/utils/shell/bashProvider.test.ts; cd desktop && bun x vitest run sidecars/launcherRouting.test.ts src/components/settings/InstallCenter.test.tsx; cd desktop && bun run lint; cd desktop && bun run build; cargo check --manifest-path desktop/src-tauri/Cargo.toml
Not-tested: Manual packaged desktop app install plus real Terminal/iTerm/PowerShell invocation on fresh macOS and Windows machines
2026-04-23 12:12:49 +08:00
程序员阿江(Relakkes)
467debcd8b feat: add a desktop setup terminal without blocking shell restarts
The desktop Settings flow needed a real shell for bundled CLI setup, but the
restart path could hang behind old PTY teardown. Wire an xterm.js panel to a
portable-pty backend, inject the bundled CLI into the shell bootstrap, and
switch to the new session before cleaning up the previous one so restart work
stays off the frontend critical path.

Constraint: The desktop app must ship its own CLI entrypoint instead of depending on a global Claude install
Constraint: PTY teardown must not block the Tauri invoke path
Rejected: Reuse the install chat for arbitrary shell commands | it does not provide a real interactive PTY
Rejected: Close the old PTY before adopting the new session | it keeps restart vulnerable to hung child shutdown
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep PTY teardown off the invoke path and preserve session handoff ordering when changing terminal lifecycle code
Tested: bunx vitest run src/__tests__/terminalPanel.test.tsx src/components/settings/TerminalPanel.restart.test.tsx; bun run lint; cargo check
Not-tested: End-to-end command echo inside the packaged desktop terminal still needs follow-up runtime verification
2026-04-22 18:06:29 +08:00
程序员阿江(Relakkes)
d8a44c0a54 Stop provider model selection from inheriting global Claude settings
The desktop provider flow was still mixing provider-managed state with
`~/.claude/settings.json`, which let unrelated tools leak fields like
`ANTHROPIC_REASONING_MODEL` and `model` back into the active provider path.
This change moves the provider JSON editor onto `~/.claude/cc-haha/settings.json`,
routes provider settings through dedicated `/api/providers/settings` endpoints,
and makes model reads/writes under an active provider use the managed cc-haha
settings instead of the global user settings file.

Constraint: Active provider model selection must be isolated from legacy ~/.claude/settings.json
Rejected: Keep merging provider JSON with settingsApi.getUser() | external tools can reintroduce unrelated model fields into the provider flow
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any future provider-model UI or runtime change should read/write cc-haha managed settings first, not the global user settings file
Tested: bun test src/server/__tests__/provider-presets.test.ts src/server/__tests__/settings.test.ts; cd desktop && bun run lint
Not-tested: Manual desktop provider modal interaction after the cc-haha settings API switch
2026-04-22 17:19:12 +08:00
程序员阿江(Relakkes)
e0142b286b Keep built-in provider presets aligned with vendor-supported model IDs
The provider picker had two separate preset definitions and some defaults had
started to drift from the model IDs vendors document for Claude Code /
Anthropic-compatible usage. This change moves built-in presets to one
server-side JSON source, serves that source through the existing presets API,
and makes the desktop settings page consume the API instead of a duplicated
frontend constant. The preset defaults were then corrected against vendor docs
so the built-in values match documented model IDs and casing.

Constraint: Built-in provider defaults must match official vendor Claude Code or Anthropic docs
Rejected: Keep duplicated frontend and backend preset lists | values and casing drifted independently
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Update src/server/config/providerPresets.json and rerun provider-presets tests before changing built-in provider defaults again
Tested: bun test src/server/__tests__/provider-presets.test.ts; cd desktop && bun run lint
Not-tested: Manual desktop settings UI interaction after fetching presets from the API
2026-04-22 17:06:35 +08:00
程序员阿江(Relakkes)
49e1367659 fix: avoid blocking MCP list on live health checks
Desktop MCP settings was synchronously probing every configured server from /api/mcp, which made the list page scale with connection latency and surface brittle behavior as installs accumulated more MCPs. Return lightweight snapshot rows from the list API, keep explicit status checks separate, and let the desktop UI refresh status in a constrained background lane while preserving project-aware server identity.

Constraint: MCP list must stay responsive even with many configured servers
Rejected: Probe all servers from the list view without limits | still fans out with server count and can overload slow installs
Rejected: Keep servers permanently unchecked until detail view | misses the desired loading feedback when the MCP page opens
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep /api/mcp as a configuration snapshot endpoint; do not reintroduce per-row live connect work on list load
Tested: bun test src/server/__tests__/mcp.test.ts; cd desktop && bun run test -- mcpSettings.test.tsx; cd desktop && bun x tsc --noEmit --ignoreDeprecations 5.0
Not-tested: Real desktop interaction against an environment with dozens of live MCP servers
2026-04-22 15:34:36 +08:00
Relakkes Yang
4bc6e135dd fix: restore computer use setup for python 3.8 2026-04-22 14:28:36 +08:00
程序员阿江(Relakkes)
6c02904e58 Keep slash-command entry surfaces aligned with MCP desktop management
Merged the desktop MCP management work into local main and retained
existing plugin settings affordances while resolving router and settings-tab
conflicts. The resulting flow keeps MCP settings global-only for speed and
uses slash-command cards to route users into concrete MCP or skill targets.

Constraint: local main already had plugin settings and API routes that had to remain available
Rejected: Favor the worktree version wholesale during merge | would have dropped local plugin tab and router support on main
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep slash-command entry flows and settings tabs additive during future merges; do not collapse MCP and plugin navigation into one another
Tested: Conflict resolution review during merge; source commit 66edded validated with bun test src/server/__tests__/mcp.test.ts; cd desktop && bun run test -- pages.test.tsx mcpSettings.test.tsx; cd desktop && bun x tsc --noEmit --ignoreDeprecations 5.0
Not-tested: Re-running full desktop/manual verification from the merged main worktree after merge completion
Related: 66edded
2026-04-21 23:29:11 +08:00
程序员阿江(Relakkes)
66edded2d7 Make desktop MCP management and slash entry points directly operable
Desktop MCP management now has a working server API, a global-only settings surface,
and slash-command entry points that surface MCP and skills from the composer before
routing users into the right settings view.

Constraint: Project-scoped MCP browsing in settings was too slow and noisy because it scanned multiple workdirs
Rejected: Keep project MCP aggregation on the settings homepage | duplicated entries and poor responsiveness
Rejected: Route /mcp directly on Enter without an intermediate card | removed the user's ability to choose a specific target first
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep settings focused on global MCP; add project-scoped MCP affordances in the chat-context slash surfaces instead of re-expanding the settings homepage
Tested: bun test src/server/__tests__/mcp.test.ts; cd desktop && bun run test -- pages.test.tsx mcpSettings.test.tsx; cd desktop && bun x tsc --noEmit --ignoreDeprecations 5.0
Not-tested: Manual IAB verification after this final commit/merge cycle
2026-04-21 23:28:06 +08:00
程序员阿江(Relakkes)
89911bd6bf Merge commit '375d587' 2026-04-21 23:21:25 +08:00
程序员阿江(Relakkes)
375d587ed1 Expose desktop plugin management through first-class APIs
The desktop app could read plugin-produced skills and agents, but it had no
plugin control plane of its own. This adds a dedicated Settings tab backed by
server-side plugin APIs so installed plugins can be inspected, enabled,
disabled, updated, reloaded, and uninstalled from the WebUI.

The implementation also teaches browser-based desktop dev sessions to honor a
custom backend URL, which made it possible to run isolated worktree ports for
real UI automation. During verification, the long-lived desktop server kept a
stale installed-plugin snapshot after external CLI mutations, so cache clearing
now resets that session-level plugin installation state as well.

Constraint: Desktop WebUI needed an isolated backend URL instead of the hard-coded 127.0.0.1:3456 fallback
Constraint: Reuse existing plugin operations and loaders instead of rebuilding plugin lifecycle logic in the desktop layer
Rejected: Fold plugin management into Skills or Adapters | mixed unrelated lifecycles and hid plugin-specific health/actions
Rejected: Expose only read-only plugin status in desktop | did not satisfy enable-disable-reload-uninstall verification needs
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop plugin actions routed through the shared plugin operation layer and clear installed-plugin session caches when plugin state changes externally
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/__tests__/pluginsSettings.test.tsx
Tested: bun test src/server/__tests__/plugins.test.ts src/server/__tests__/skills.test.ts
Tested: Browser automation against isolated ports 15120/38456 covering discord plugin list/detail/disable/apply/enable/update/uninstall flows
Not-tested: Full desktop session runtime parity with CLI /reload-plugins AppState refresh beyond the new desktop API path
2026-04-21 23:21:16 +08:00
程序员阿江(Relakkes)
044e9b6cc8 Make desktop rewind restore both transcript state and file checkpoints
The desktop client already had a conversation-level rewind UI concept on the
CLI side, but the web/desktop surface lacked the protocol, session trimming,
and file checkpoint restore path needed to make rewind trustworthy. This change
adds a desktop-specific rewind API, wires the message-level UI affordance and
confirmation modal, enables SDK file checkpointing for desktop sessions, and
covers the restore path with service tests plus a real agent-browser workflow.

Constraint: Desktop sessions run the CLI in SDK/print mode, so file checkpointing had to be enabled explicitly for that path
Constraint: main branch is checked out in a separate worktree, so merge-back must happen from the primary worktree after commit
Rejected: UI-only rewind that only trims local state | would leave persisted transcript and disk state inconsistent after refresh
Rejected: Reuse getLastSessionLog as the sole snapshot source | active rewind must read file-history metadata directly from the session file
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop rewind keyed to persisted user-message order unless the UI model starts carrying stable transcript UUIDs end-to-end
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/sessions.test.ts; desktop MessageList vitest; desktop tsc no-emit; live agent-browser E2E on isolated ports with file edit then rewind
Not-tested: Browser E2E matrix for multi-file and second-edit scenarios is still covered at service-test level rather than full UI level
2026-04-21 21:34:41 +08:00
程序员阿江(Relakkes)
5377eb596f Keep desktop session history and reconnect recovery trustworthy
Desktop session interruptions were leaving two different classes of stale UI
artifacts behind: reconnects could silently lose in-flight output, and history
reloads could replay synthetic interruption or internal command breadcrumbs as
if they were user-facing transcript content. This change rebinds active session
output to the latest client websocket on reconnect, preserves queued outbound
messages across transient disconnects, and filters synthetic transcript entries
before they reach the desktop history API so reloads reconstruct only the
messages users should actually see.

Constraint: Desktop transcript history is shared with CLI JSONL files and must stay compatible with persisted message shapes
Rejected: Hide these artifacts only in React renderers | other transcript consumers would still receive polluted history
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep transcript filtering aligned with real synthetic/internal message shapes before adding new hidden message types
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: cd /Users/nanmi/.codex/worktrees/e7ac/claude-code-haha/desktop && bun run test -- websocket.test.ts
Tested: cd /Users/nanmi/.codex/worktrees/e7ac/claude-code-haha/desktop && bun run lint
Not-tested: Manual browser verification of the refreshed session transcript after interrupt on this exact session
2026-04-21 17:55:15 +08:00
程序员阿江(Relakkes)
16d716e773 fix: keep completed task lists from leaking into the next turn
Completed desktop task bars were only being dismissed locally, which let the
persisted task list resurface on refresh or bleed into the next user turn.
This wires the existing server-side reset path into the desktop stores and
session flow so a finished task cycle is summarized once, cleared locally,
and removed remotely before the next round starts.

Constraint: Existing task persistence already lives behind the server task-list API and must stay compatible with persisted JSON task files
Rejected: Only hide the completed task bar in UI state | left stale persisted tasks behind and reintroduced them on reload
Rejected: Clear desktop state without a task summary | dropped useful completion context from the chat transcript
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop task dismissal and task-list persistence behavior aligned; do not reintroduce local-only clearing without covering reload and next-turn flows
Tested: `cd desktop && bun run lint`
Tested: `cd desktop && bun run test src/stores/cliTaskStore.test.ts src/stores/chatStore.test.ts src/components/chat/SessionTaskBar.test.tsx src/components/chat/ComputerUsePermissionModal.test.tsx`
Tested: `bun test src/server/__tests__/e2e/business-flow.test.ts --test-name-pattern "Task Lists API"`
Not-tested: Full `bun test src/server/__tests__/e2e/business-flow.test.ts` suite still has unrelated pre-existing failures in Models and Sessions sections
2026-04-21 00:46:29 +08:00
程序员阿江(Relakkes)
7651b24a50 Reduce desktop first-token latency in sdk-url sessions
Desktop-managed sessions were blocking the first visible token on slow or failing
regular MCP connections, which made the UI feel much slower than direct bin runs.
This keeps partial assistant streaming enabled for desktop sessions and moves
regular MCP connection work off the critical path only for sdk-url startup.

Constraint: Desktop sdk-url sessions must preserve chat responsiveness even when local MCP endpoints fail or hang
Rejected: Leave MCP startup fully blocking in sdk-url mode | first visible token stayed several seconds behind direct CLI runs
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep blocking MCP startup for plain -p sessions unless you re-measure first-turn latency and tool availability tradeoffs
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/conversations.test.ts; same-prompt timing comparison for direct bin vs sdk-url session path
Not-tested: Immediate first-turn availability of slow regular MCP tools in desktop sdk-url sessions
2026-04-21 00:41:02 +08:00
程序员阿江(Relakkes)
9fdbfafd4d Prevent settings races and stale session teardown in local services
Concurrent writes to the same settings file could interleave and leave user
preferences in a partially updated state, and a restarted conversation session
could still be torn down by an older process exit callback. This serializes
per-file settings writes, hardens temp-file handling, adds regression coverage
for both service behaviors, and drops an unused sidecar external from the
build exclude list.

Constraint: Settings writes must remain atomic while allowing multiple service entry points to update the same JSON file
Rejected: Keep fire-and-forget writes with unique temp names only | still allows stale reads and last-writer races between callers
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Preserve the per-file write lock when adding new settings mutation paths so concurrent writes keep one serialization point
Tested: bun test src/server/__tests__/settings.test.ts src/server/__tests__/conversations.test.ts (new settings/conversation cases passed)
Not-tested: Root lint script is unavailable; Models API tests in src/server/__tests__/settings.test.ts still fail in this checkout with changed default model/effort expectations
2026-04-20 23:13:45 +08:00
程序员阿江(Relakkes)
0f7494006f Prevent misleading empty @-file menus when filesystem access fails
Desktop @-mentions were collapsing filesystem browse failures into an empty
state, which made /private/tmp aliases on macOS look like real empty
directories. The server now treats /private/tmp as the same temp root as
/tmp on Darwin, and the menu surfaces explicit access/load errors instead of
pretending the directory has no files.

Constraint: Desktop browse/file APIs must stay restricted to approved roots
Constraint: Native macOS pickers and realpath() may canonicalize /tmp to /private/tmp
Rejected: Expanding filesystem access to arbitrary system paths | too broad for this targeted fix
Rejected: Keeping silent empty-state fallback | hides permission and path bugs from users
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep filesystem error states visible in the composer; do not collapse API failures back into empty-directory messaging
Tested: bun test src/server/__tests__/filesystem.test.ts
Tested: cd desktop && bun run test -- FileSearchMenu.test.tsx
Tested: cd desktop && bun run lint
Not-tested: Manual desktop packaging or end-to-end Tauri app run after this patch
2026-04-20 23:07:15 +08:00
Relakkes Yang
4b513271ee fix: improve windows computer use and desktop rendering 2026-04-20 17:59:42 +08:00
程序员阿江(Relakkes)
921a352517 Integrate desktop skill discovery and empty-session composer fixes into main
This merges the detached-worktree desktop fixes back into main, preserving the
existing Computer Use modal changes on main while bringing over the project
skill discovery fallback, skills settings cwd-awareness, sidebar tab cleanup,
and the empty-session hero composer styling.

Constraint: Main already carried newer desktop session UI changes that had to be preserved during merge resolution
Rejected: Abort the merge and cherry-pick individual files | unnecessary once the only conflict was isolated to ActiveSession.tsx
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: When merging detached-worktree desktop branches into main, keep ActiveSession conflict resolution additive so session overlays and composer variants survive together
Tested: bun test src/server/__tests__/sessions.test.ts; cd desktop && bun run test -- --run src/__tests__/skillsSettings.test.tsx src/components/layout/Sidebar.test.tsx src/__tests__/pages.test.tsx src/pages/ActiveSession.test.tsx; cd desktop && bun run lint
Not-tested: Manual post-merge GUI verification in the local main worktree app bundle
2026-04-20 13:37:57 +08:00
程序员阿江(Relakkes)
261a85ab76 Restore desktop project-skill discovery and align empty-session composer
The desktop app previously lost project-level skills in two places: the
settings browser only queried user skills, and the slash-command picker for a
fresh session depended on CLI init state that does not exist before the first
turn. This change makes the skills APIs cwd-aware, falls back to project skill
loading before CLI init, syncs sidebar session deletion with open tabs, and
aligns the empty-session composer styling so the pre-message session UI stays
consistent.

Constraint: Fresh desktop sessions need slash-command discovery before the CLI websocket emits system/init
Constraint: Only repository code should be committed; build artifacts, installs, and /tmp smoke fixtures stay out of git
Rejected: Rebuild slash-command listings from full getCommands() in the sessions API | introduced auth-gated command dependencies unrelated to local skill discovery
Rejected: Unify EmptySession and ChatInput into one full component now | higher regression risk for normal chat interactions than a visual-only hero variant
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep pre-message skill discovery keyed to session workDir so project-level .claude/skills remain visible before the first turn
Tested: bun test src/server/__tests__/sessions.test.ts; cd desktop && bun run test -- --run src/__tests__/skillsSettings.test.tsx src/components/layout/Sidebar.test.tsx src/__tests__/pages.test.tsx src/pages/ActiveSession.test.tsx; cd desktop && bun run lint
Not-tested: Manual desktop smoke test against the rebuilt .app bundle in the local GUI
2026-04-20 13:36:19 +08:00
程序员阿江(Relakkes)
5cd6b5d07b Prevent desktop Computer Use from stalling on missing approvals and unstable text input
Desktop sessions were missing a visible request_access approval path and could
mis-detect their own app window as an unapproved frontmost target, which caused
Computer Use clicks to fail even after opening the intended app. On macOS, text
entry was also split across inconsistent clipboard and keystroke paths, making
Electron inputs unreliable for Chinese and short strings.

This change adds a desktop approval bridge over the existing session websocket,
renders a dedicated desktop approval modal, threads the real desktop bundle id
into the Computer Use executor, and switches macOS clipboard typing onto the
native pasteboard plus system paste shortcut path. It also makes tool error
results expandable in the desktop chat UI so frontmost-gate failures are fully
visible during debugging.

Constraint: Desktop sessions run the CLI over the SDK websocket path, so Ink tool JSX dialogs are not visible there
Constraint: macOS IME and Electron text inputs are unreliable with pyautogui.write and generic hotkey synthesis
Rejected: Reuse CLI setToolJSX dialogs in desktop mode | no transport for mid-call Ink UI over the SDK bridge
Rejected: Keep shell pbcopy/pbpaste for clipboard typing | inconsistent with NSPasteboard path and less reliable for Chinese text
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop Computer Use approvals and macOS text-entry behavior on a single bridge/path; avoid reintroducing separate CLI-only and desktop-only codepaths for the same action
Tested: python3 -m unittest runtime/test_helpers.py
Tested: bun test src/utils/computerUse/permissions.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test ComputerUsePermissionModal chatStore
Tested: cd desktop && bun run test chatBlocks
Tested: cd desktop && bun run lint
Not-tested: End-to-end manual Computer Use interaction against a live Electron target app on macOS
2026-04-20 12:12:02 +08:00
程序员阿江(Relakkes)
20806f406f chore: update cluade model name 2026-04-18 20:34:56 +08:00
程序员阿江(Relakkes)
9d29d8e32c Keep desktop replies stable across turns and fix OAuth callback routing
The desktop chat store was dropping the previous assistant draft when a new user turn began because streaming text was cleared before it was flushed into message history. At the same time, the desktop OAuth flow was using an unregistered /api/haha-oauth/callback redirect URI, which caused provider authorization failures. This change flushes visible assistant drafts before starting a new turn, restores the OAuth redirect to the registered localhost callback, and adds a root callback handler while keeping the legacy API callback path compatible.

Constraint: The OAuth provider only accepts the registered localhost /callback redirect URI
Rejected: Keep the desktop-specific /api/haha-oauth/callback path | provider rejects unsupported redirect URIs
Rejected: Rely on message_complete to persist visible assistant text | next user turn can begin before that event arrives
Confidence: high
Scope-risk: moderate
Directive: Any UI-visible assistant draft must be flushed into messages before a new user turn resets streaming state
Tested: bun test src/server/__tests__/haha-oauth-service.test.ts src/server/__tests__/haha-oauth-api.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Not-tested: Manual end-to-end desktop OAuth login after reinstall
2026-04-18 17:01:53 +08:00
程序员阿江(Relakkes)
d83efc1b04 Prevent desktop OAuth sessions from drifting out of sync
Desktop auth was reporting stale local token files as logged in, could inherit a parent OAuth token after logout, and kept polling even when browser launch failed. This change validates stored tokens through the refresh path, clears inherited OAuth env before spawning the CLI, fetches subscription metadata on initial login, and moves polling start until after the browser opens. Regression tests cover the server and desktop store paths.

Constraint: Desktop CLI must bypass macOS Keychain by injecting env OAuth when available
Rejected: Keep status endpoint file-based only | reports expired sessions as logged in
Confidence: high
Scope-risk: moderate
Directive: Official desktop auth state must be derived from ensureFreshTokens rather than oauth.json presence alone
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/haha-oauth-service.test.ts src/server/__tests__/haha-oauth-api.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test
Not-tested: Manual end-to-end desktop OAuth flow against the live provider
2026-04-18 01:45:18 +08:00
程序员阿江(Relakkes)
94b445cd06 feat(desktop): inject CLAUDE_CODE_OAUTH_TOKEN from haha oauth.json
在 buildChildEnv 官方模式分支下,调 hahaOAuthService.ensureFreshAccessToken()
(自动 refresh),成功则通过 env 注入给 CLI 子进程。

CLI 的 src/utils/auth.ts:1260 优先读 CLAUDE_CODE_OAUTH_TOKEN env,完全
不碰 Keychain —— DMG 装的 .app 在 macOS 下也能正常用官方 Claude,不再 403。

buildChildEnv 签名改为 async, 所有 caller 加 await。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 20:12:43 +08:00
程序员阿江(Relakkes)
7421db2857 fix(desktop): align haha-oauth handler with sibling error-handling convention
Code review follow-up on 142eb9cd:
- 加 outer try/catch + errorResponse wrapper (parity with providers.ts)
- 400 响应改用 ApiError.badRequest (shape 跟其他 handler 一致: {error: 'BAD_REQUEST', message})
- test 加上对 error shape 的断言

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 20:06:22 +08:00
程序员阿江(Relakkes)
142eb9cd11 feat(desktop): add /api/haha-oauth REST endpoints
Endpoints:
- POST   /api/haha-oauth/start    — 生成 PKCE,返回 authorize URL
- GET    /api/haha-oauth/callback — 浏览器 redirect 到此,完成 token exchange,
                                    渲染 success/fail HTML 页(自动关闭)
- GET    /api/haha-oauth          — 查询登录状态(不回传 token 本体)
- DELETE /api/haha-oauth          — 登出,清除 token 文件

前端用 start 拿到 authorizeUrl → shell.open(浏览器) → 浏览器走 OAuth → redirect
到 callback → callback 完成后显示 '登录成功,可关闭窗口' 页面。前端并行轮询
/api/haha-oauth 检测 loggedIn=true 后刷新 UI。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 19:59:42 +08:00
程序员阿江(Relakkes)
a1b2530fab fix(desktop): cleanup HahaOAuthService — 删除 dead code、加 fetch timeout、log refresh errors
Code review follow-ups on b0ca5861:
- 删除 dead code exchangeFn/setExchangeFn (completeSession 总走 exchangeWithCustomCallback)
- exchangeWithCustomCallback fetch 加 15s AbortController timeout (parity with client.ts)
- ensureFreshAccessToken refresh 失败时 console.error,不再静默吞异常
- test 删除未用的 mock import
- getOauthConfig 改为 static import
- saveTokens 加注释说明 atomic write 意图

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 19:55:12 +08:00
程序员阿江(Relakkes)
b0ca586190 feat(desktop): add HahaOAuthService for self-managed Claude OAuth
新增 service 层管理桌面端自己的 OAuth token 生命周期:
- PKCE + state 生成(复用 oauth/crypto.ts)
- Token 存 ~/.claude/cc-haha/oauth.json, 权限 0600
- In-memory session map (5min TTL)
- ensureFreshAccessToken: 自动 refresh (5min buffer)

目的: 绕开 macOS Keychain ACL 对未公证 .app 的静默拒绝,
让 DMG 装的 .app 也能正常走官方 Claude OAuth。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 19:38:31 +08:00
Relakkes Yang
5d243b657a feat(desktop): Windows 自定义标题栏 & 服务端改进
- Windows 上隐藏原生菜单栏和窗口装饰,前端渲染自定义窗口控制按钮(最小化/最大化/关闭)
- macOS 保持原生菜单栏不变(条件编译)
- Sidebar 在 Windows 上不再添加 macOS 红绿灯的额外顶部间距
- 新增 Windows 构建脚本和 NSIS 安装钩子
- 服务端 CORS、会话服务、对话服务改进及测试补充

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 21:29:35 +08:00
程序员阿江(Relakkes)
5fdf50436c test: 更新测试用例适配 provider 隔离与新 API
- providers-real.test.ts 重写适配 ModelMapping 对象格式
- e2e 测试适配 cc-haha/settings.json 路径
- settings.test.ts 验证写入隔离
- models.ts 小调整
2026-04-15 14:19:04 +08:00
程序员阿江(Relakkes)
23b31b397a Prevent local team sessions from dropping members and stalling adapters
This bundles the pending desktop/server team-session fixes with the local adapter recovery changes already in the worktree. The team path now keeps teammate membership stable under concurrent spawns, surfaces real teammate identities in the desktop UI, and allows direct interaction with member transcripts. The adapter changes recover automatically when stale thinking signatures invalidate an existing session.

Constraint: Team config writes can happen concurrently while multiple reviewers spawn in parallel

Constraint: Desktop member views must follow mailbox/transcript semantics rather than hijacking teammate runtime sessions

Rejected: Keep relying on config.json alone for member discovery | in-process teammates can be lost after concurrent writes

Rejected: Open teammate sessionIds as normal desktop sessions | would attach a second CLI instead of the running teammate

Confidence: medium

Scope-risk: moderate

Reversibility: clean

Directive: Preserve locked team-file mutation for any future teammate registration path and keep teammate labels sourced from member names before agent types

Tested: bun test src/server/__tests__/teams.test.ts src/server/__tests__/team-watcher.test.ts

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

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run build

Not-tested: Manual end-to-end validation against a live Agent Teams run in the desktop app
2026-04-14 17:27:07 +08:00
程序员阿江(Relakkes)
8888e1c3fb chore: comprehensive code review fixes — security, perf, leaks, quality, docs
- security: XSS sanitization with DOMPurify in Markdown/Mermaid/PermissionDialog;
  path whitelist in filesystem API; fake keys in test/config files
- perf: fine-grained Zustand selectors in Sidebar/StatusBar/ContentRouter;
  50ms throttle on streaming deltas; React.memo + useMemo in MessageList;
  useRef for frequent keyboard shortcut state; AbortController 30s timeout
- leaks: WS session TTL timers (5-min cleanup on close); batch splice for
  sdkMessages/stderrLines; folderPath validation in cronScheduler
- quality: optimistic update rollback in settingsStore; error state in
  providerStore/teamStore; i18n for all hardcoded English strings
- docs: desktop architecture and features docs updated; VitePress nav fixed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:27:17 +08:00
程序员阿江(Relakkes)
635a966c3e fix(desktop): unblock rollout with reliable session and IM flows
This folds together the desktop-side fixes needed before broader rollout.
Session resume no longer deadlocks waiting on init, Mermaid and inline image
output render inside chat, task and sub-agent state stay visible during
execution, local build/release paths are safer, and Feishu/Telegram now expose
lightweight mobile commands (/help, /status, /clear) without adding a new
adapter-specific protocol.

Constraint: Desktop releases must publish updater artifacts from non-draft GitHub releases
Constraint: IM commands need short, phone-friendly responses and low operational complexity
Rejected: Add a dedicated IM command API surface | re-used existing slash commands and session/task REST endpoints to keep adapters thin
Rejected: Wait for task_update push events in WebUI | added low-risk polling because the current frontend ignores that event path
Confidence: medium
Scope-risk: broad
Reversibility: clean
Directive: Keep IM command replies terse and mobile-first, and merge local fallback slash commands when server-provided lists are partial
Tested: cd desktop && bun x vitest run src/components/chat/MermaidRenderer.test.tsx src/components/markdown/MarkdownRenderer.test.tsx
Tested: cd desktop && bun x vitest run src/components/chat/composerUtils.test.ts src/pages/ActiveSession.test.tsx src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "SDK init arrives only after the first user turn" --timeout 60000
Tested: cd adapters && bun test common/ feishu/ telegram/
Tested: cd adapters && bunx tsc --noEmit
Not-tested: Full GitHub Actions release run on all three desktop platforms
Not-tested: Local DMG packaging end-to-end on Apple Silicon
Not-tested: Real Feishu/Telegram device sessions against a live adapter process
2026-04-10 16:41:59 +08:00
程序员阿江(Relakkes)
98a24f99b4 feat(proxy): add multi-protocol proxy for OpenAI-compatible providers
Add a protocol-translating reverse proxy that allows using OpenAI-compatible
API providers (DeepSeek, OpenRouter, Groq, etc.) with Claude Code.

The proxy intercepts Anthropic Messages API requests from the CLI, transforms
them to OpenAI Chat Completions or Responses API format, forwards to the
upstream provider, and transforms streaming/non-streaming responses back.

Key features:
- Request transform: Anthropic Messages → OpenAI Chat/Responses
- Response transform: OpenAI → Anthropic (streaming SSE + non-streaming)
- Provider-agnostic reasoning support (reasoning_content, thinking_blocks,
  reasoning fields from DeepSeek, OpenAI o-series, GLM-5, Groq, etc.)
- Event queue pattern for correct Anthropic SSE event ordering
- Two-step test: ① connectivity check ② full proxy pipeline validation
- Desktop UI: API format selector, two-step test results display
- License attribution for cc-switch (MIT, Jason Young)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 00:33:26 +08:00
程序员阿江(Relakkes)
211226c7a1 feat(agents): update API types, store, i18n, and tests for new agent model
- Update AgentDefinition type with agentType, source, isActive, modelDisplay fields
- Refactor agentStore to support activeAgents/allAgents with cwd parameter
- Add i18n strings for agent browser UI (source labels, summary, status)
- Update server agents API with serialization helpers and override resolution
- Update tests to match new agent data structure

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 16:45:51 +08:00
程序员阿江(Relakkes)
91b4d16dc5 Restore sub-agent context in the desktop chat transcript
The desktop chat view flattened Agent tool activity, which made sub-agent work
hard to follow and separated key evidence from the main conversation. This
change threads parent tool linkage through the server bridge and desktop store,
renders dispatched sub-agents as grouped cards with nested tool activity, and
moves long final outputs into a markdown preview dialog so the main transcript
stays readable on narrow layouts.

Constraint: Existing sessions and live websocket events both needed to preserve parent-child relationships
Rejected: Add brand-new subagent websocket event types | unnecessary protocol expansion when parent linkage already existed upstream
Rejected: Inline full sub-agent markdown in the card body | too cramped for narrow desktop chat layouts
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep Agent card summaries compact; route long-form sub-agent output through the preview dialog unless the main chat layout is widened substantially
Tested: cd desktop && bun run test -- MessageList.test.tsx chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/sessions.test.ts -t should\ reconstruct\ parent\ agent\ tool\ linkage\ from\ parentUuid\ chains
Not-tested: Full end-to-end visual verification against live CLI sessions with sub-agent text/thinking nested inline
2026-04-09 00:04:40 +08:00
程序员阿江(Relakkes)
ce92de29f0 fix: prevent transient scheduled task write failures
Cron task metadata writes can overlap closely enough that a timestamp-only
$temp filename is not stable. Harden the atomic write path by using a
collision-resistant temp name and retrying once when rename reports ENOENT.

Constraint: Scheduled task persistence must keep atomic replace semantics in ~/.claude/scheduled_tasks.json
Rejected: Add explicit file locking around every scheduled task write | more coordination overhead than the observed transient rename race warrants
Confidence: high
Scope-risk: narrow
Directive: Keep scheduled task writes collision-resistant and retriable; do not revert to timestamp-only temp file names without reproducing concurrent writes
Tested: bun test src/server/__tests__/scheduled-tasks.test.ts
Not-tested: bun test src/server/__tests__/cron-scheduler.test.ts (existing timeouts and unrelated assertion failures in this checkout)
2026-04-08 23:21:28 +08:00
程序员阿江(Relakkes)
985b1a8b7d feat: add Provider management system — multi-provider CRUD, activation, and connectivity test
Implement a full Provider management layer compatible with Claude Code's settings.json protocol.

- ProviderService: CRUD + activation + settings.json sync + HTTP connectivity test
- Provider REST API: 8 endpoints (list, get, create, update, delete, activate, test)
- API key masking in GET responses (sk-a****xyz)
- Models API refactored to read from active Provider dynamically
- 42 unit tests + 6 real-config integration tests, all passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 01:29:52 +08:00
程序员阿江(Relakkes)
993b96cd39 Stabilize the desktop transcript so long agent sessions stay readable
The desktop app now keeps the composer stable while turns are active,
reduces low-signal tool noise in the transcript, restores project context
under the composer after session creation, and relies on the CLI's own
permission requests instead of injecting broader desktop-side Bash asks.

This also brings in the supporting desktop app source tree and the server
routes/session metadata needed for git info, filesystem browsing, session
resume, slash commands, and SDK-backed permission bridging so the UI can
operate as a coherent feature instead of a partial patch.

Constraint: Desktop transcript needs to stay usable during long multi-tool sessions without hiding file-change diffs
Constraint: Permission prompts must mirror CLI behavior closely enough that read-only commands do not get desktop-only prompts
Rejected: Keep rendering Read/Bash bodies inline | too noisy and unlike the intended transcript model
Rejected: Commit only the touched desktop files | would leave the newly introduced desktop app incomplete in git history
Confidence: medium
Scope-risk: broad
Reversibility: messy
Directive: Treat non-writing tools as summary-first transcript events; do not re-expand them by default without validating the UX against long sessions
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run
Tested: bun test src/server/__tests__/conversations.test.ts
Not-tested: Manual visual regression against the exact screenshots in a live desktop session
Not-tested: Full root TypeScript check (repository still has unrelated extracted-native parse failures)
2026-04-06 20:37:44 +08:00
程序员阿江(Relakkes)
ce0908a4e0 feat: complete server-side gaps — tool visibility, cron execution engine, team real-time push
- Fix translateCliMessage to return ServerMessage[] with full tool lifecycle
  (tool_result, tool_use_complete, toolInput, thinking, stream_event)
- Add CronScheduler that actually executes scheduled tasks via CLI subprocess,
  with execution log persistence and GET /api/scheduled-tasks/:id/runs API
- Add TeamWatcher polling ~/.claude/teams/ every 3s, broadcasting team_update/
  team_created/team_deleted to all WebSocket clients
- 280 tests pass (51 new), real LLM integration verified via MiniMax API

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:42:32 +08:00
程序员阿江(Relakkes)
29c9fb8f4a feat: add CLI subprocess chat, Agent Teams API, and real background tasks
Three new modules completing the server-side functionality:

1. ConversationService: spawns CLI as subprocess with --input-format
   stream-json, forwards WebSocket messages to CLI stdin/stdout.
   CLI handles all AI communication, tool execution, and permissions.
   Graceful fallback to echo mode when CLI unavailable.

2. Agent Teams API: GET/DELETE /api/teams, member listing with status
   derivation (running/idle/completed), transcript reading from
   CLI-generated JSONL files. Teams created by CLI, API is read-only.

3. TaskService: replaces placeholder with real task file scanning
   from ~/.claude/tasks/, supports nested team directories.

229 tests passing (49 new: 12 conversations + 27 teams + 10 tasks).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:05:22 +08:00
程序员阿江(Relakkes)
0c6d2754b4 feat: add Desktop UI backend server with full REST API and WebSocket support
Add complete server-side implementation for the Claude Code Desktop App UI:

- REST API: sessions, conversations, settings, models, scheduled tasks,
  search, agents, and status endpoints (9 modules, 30+ endpoints)
- WebSocket: real-time chat streaming with state transitions, ping/pong,
  permission request forwarding, and stop generation support
- Services: sessionService (JSONL read/write, CLI-compatible),
  settingsService (atomic writes), cronService, searchService (ripgrep),
  agentService (YAML management)
- Middleware: CORS (localhost-only), auth, unified error handling
- Tests: 180 tests (unit + E2E + business flow), all passing
- Docs: PRD, UI design spec, server architecture design

Non-invasive: all new code under src/server/, no changes to existing CLI code.
CLI/UI data interop: reads/writes the same JSONL/JSON files as the CLI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 13:23:44 +08:00