214 Commits

Author SHA1 Message Date
程序员阿江(Relakkes)
37bdc4e6e7 Protect remote server access with H5-aware auth and CORS
Remote browser access needs an explicit path that preserves localhost desktop behavior while allowing H5 tokens for REST and client websocket upgrades. This keeps the existing synchronous Anthropic auth helper intact, adds async request validation for H5 tokens, and centralizes origin handling so preflight, API, proxy, websocket, and health responses behave consistently.

Constraint: Existing validateAuth(req) must stay synchronous for current tests and callers
Constraint: Default localhost and Tauri desktop behavior must remain unchanged
Rejected: Replacing validateAuth with an async-only API | would break existing sync tests and call sites
Rejected: Allowing wildcard or fallback remote origins | violates the H5 explicit-origin security model
Directive: Restore ProviderService serverPort in server integration tests that call startServer with custom ports to avoid leaking global proxy runtime state
Confidence: high
Scope-risk: moderate
Tested: bun test src/server/__tests__/h5-access-auth.test.ts --timeout 30000
Tested: bun test src/server/__tests__/h5-access-service.test.ts src/server/__tests__/h5-access-api.test.ts src/server/__tests__/settings.test.ts --timeout 30000
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/cron-scheduler-launcher.test.ts --timeout 30000
Tested: bun test src/server/__tests__/e2e/full-flow.test.ts --timeout 30000
Not-tested: Full non-live server gate end-to-end; an attempted broader run was interrupted after uncovering and then fixing a test-side ProviderService port leak
2026-05-09 23:43:29 +08:00
程序员阿江(Relakkes)
3276a19da0 Prevent managed settings races from dropping H5 or provider state
Move cc-haha managed settings writes behind one shared transaction helper so
H5 access mutations and provider settings sync serialize against the same file.
Also treat persisted H5 state without a valid token hash as disabled so origin
allowlists cannot remain active after partial corruption.

Constraint: H5 and provider config both share ~/.claude/cc-haha/settings.json
Rejected: Keep per-service locks | concurrent read-modify-write still drops unrelated fields
Rejected: Only harden isOriginAllowed | corrupted enabled state would still surface as active in settings reads
Confidence: high
Scope-risk: narrow
Directive: Any future writer of cc-haha/settings.json must go through the shared managed settings transaction path
Tested: bun test src/server/__tests__/h5-access-service.test.ts src/server/__tests__/h5-access-api.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/provider-presets.test.ts
Not-tested: Full repo typecheck remains blocked by the existing bun-types/tsconfig environment issue in this worktree
2026-05-09 23:32:39 +08:00
程序员阿江(Relakkes)
e357c978fe Add a durable H5 access control surface for remote browser setup
Persist H5 access state in cc-haha managed settings, expose a narrow server API
for enable/disable/regenerate/verify flows, and keep token responses sanitized
so the raw secret is only returned at generation time.

Constraint: H5 config must live in ~/.claude/cc-haha/settings.json and preserve unknown fields
Rejected: Store raw token for later display | violates hash-only persistence requirement
Confidence: high
Scope-risk: narrow
Directive: Do not move H5 settings into ~/.claude/settings.json or expose tokenHash through the API
Tested: bun test src/server/__tests__/h5-access-service.test.ts src/server/__tests__/h5-access-api.test.ts
Not-tested: Full repo typecheck via tsc is blocked locally by missing bun-types and a TS 6 baseUrl deprecation in the current config
2026-05-09 23:22:55 +08:00
程序员阿江(Relakkes)
da477c2b4b Merge session title sanitation into main
Bring the detached worktree fix onto local main after verifying the title-generation regression path and local quality checks.

Constraint: The worktree commit was based on an older main commit, while local main already contained later chat scroll fixes.
Confidence: high
Scope-risk: moderate
Directive: Keep title sanitation shared across CLI, server, and desktop read paths.
Tested: bun run check:server
Tested: bun run check:desktop
Tested: bun run verify (7 passed, 1 failed on existing aggregate agent-utils coverage baseline)
Not-tested: live model title-generation smoke
2026-05-09 22:34:54 +08:00
程序员阿江(Relakkes)
179f79b794 fix: sanitize generated session titles
Slash-command and skill prompts can enter the title-generation path as internal XML breadcrumbs. The async title request previously treated that transport metadata as user prose, so a generated ai-title could persist raw command tags and override the normal session title.

This routes title sources and generated title output through a shared sanitizer, preserving user-visible command names and arguments while dropping unrelated internal XML metadata. Desktop fallback title rendering now applies the same cleanup so existing transcripts with bad ai-title entries recover on read.

Constraint: Session titles can be produced by both desktop server logic and the SDK generate_session_title control request.
Rejected: Only clean desktop session display | leaves CLI and remote title generation able to persist bad titles again
Confidence: high
Scope-risk: moderate
Directive: Keep generated-title input and persisted-title readback on the shared sanitizer; do not add a title path that reads command XML directly.
Tested: bun test src/utils/__tests__/sessionTitle.test.ts src/utils/__tests__/sessionTitleText.test.ts
Tested: bun run check:server
Tested: bun run check:desktop
Tested: bun run verify (7 passed, 1 failed on existing aggregate agent-utils coverage baseline)
Not-tested: live model title-generation smoke
2026-05-09 22:34:27 +08:00
程序员阿江(Relakkes)
50d4679f51 Merge chat scroll recovery into main
Bring the detached worktree fix into the local main line while preserving the newer main behavior that passes session context into interactive chat message blocks.

Constraint: Local main already contains six unpublished commits on top of origin/main
Constraint: Merge conflict only affected MessageList message rendering around sessionId propagation
Rejected: Overwrite main's MessageBlock call shape | would regress AskUserQuestion and permission interactions that need sessionId
Confidence: high
Scope-risk: moderate
Directive: Keep MessageBlock sessionId propagation when editing chat rendering; AskUserQuestion depends on it
Tested: cd desktop && bun run test -- MessageList.test.tsx
Not-tested: Full verify after merge; previous verify remains blocked by agent-utils global coverage baseline
2026-05-09 21:52:31 +08:00
程序员阿江(Relakkes)
6382115de9 fix: keep chat tabs from losing scroll context
Chat sessions need predictable navigation when users switch tabs or read history during streaming. This preserves each session's scroll position, defaults fresh sessions to the latest message, and adds a compact jump-to-latest affordance when auto-follow is paused.

The PR gate also exposed that provider-scoped scheduled tasks must force the sdk-cli entrypoint when launched through the sidecar, so the same change records that runtime contract and its regression assertion.

Constraint: Desktop chat should not force-scroll while the user is reading older messages
Constraint: Scheduled task provider env must not inherit stale parent model runtime values
Rejected: Persist scroll positions in localStorage | session scroll is transient UI state and should not survive app restarts
Confidence: high
Scope-risk: moderate
Directive: Do not remove the sdk-cli entrypoint marker from provider-scoped cron tasks without rerunning the sidecar launcher regression
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/cron-scheduler-launcher.test.ts src/utils/__tests__/themeWords.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Tested: bun run check:native
Not-tested: bun run verify still fails on pre-existing agent-utils global coverage baseline below threshold
Not-tested: Chrome extension E2E blocked by Codex Chrome Extension communication timeout after plugin diagnostics passed
2026-05-09 21:51:34 +08:00
程序员阿江(Relakkes)
b88daccda9 Merge worktree fixes for agent cache invalidation 2026-05-09 21:49:39 +08:00
程序员阿江(Relakkes)
e2b1ac9fd1 fix: launch scheduled tasks with the sdk cli entrypoint
Scheduled task subprocesses should normalize their child environment instead of inheriting a stale parent entrypoint. Setting the default task entrypoint keeps launcher tests and non-OAuth task execution on the SDK CLI path while still allowing the official OAuth environment overlay to provide its desktop-specific entrypoint.

Constraint: Official OAuth tasks intentionally overlay provider-specific environment after the default child task environment is built
Rejected: Preserve parent CLAUDE_CODE_ENTRYPOINT | stale parent values can leak into scheduled task launches
Confidence: high
Scope-risk: narrow
Directive: Keep default task launcher environment deterministic before provider-specific overlays are applied
Tested: bun test src/server/__tests__/cron-scheduler-launcher.test.ts
Tested: bun run check:server
Not-tested: Full verify still blocked by existing agent-utils global coverage baseline
2026-05-09 21:48:06 +08:00
程序员阿江(Relakkes)
571d771025 fix: keep agent listings fresh after file edits
Agent definitions are cached across command invocations, so edits made through the shared file helper must invalidate both the agent definition cache and the underlying markdown file cache. This keeps the TUI creation path and /agents command output consistent after creating, updating, or deleting custom agents.

Constraint: The TUI agent wizard and /agents command share cached agent definition loaders
Rejected: Clear only getAgentDefinitionsWithOverrides | the nested markdown file memoization can still return stale files
Confidence: high
Scope-risk: narrow
Directive: Any future agent file mutation path must call clearAgentDefinitionsCache after a successful write or delete
Tested: bun test src/tools/AgentTool/loadAgentsDir.cache.test.ts
Tested: bun run check:server
Not-tested: Full verify still blocked by existing agent-utils global coverage baseline
2026-05-09 21:47:58 +08:00
程序员阿江(Relakkes)
b31be5161c feat: support custom Computer Use Python interpreters
Computer Use setup could fail on machines where PATH discovery misses a valid Python installation, especially Windows or conda-style environments. Store an optional interpreter path, prefer it during environment checks and venv creation, and expose a desktop settings control for selecting or clearing it.

Constraint: Python discovery is environment-specific and cannot always be inferred from PATH.
Rejected: Continue falling back to PATH after an invalid custom path | hides a user-selected broken interpreter and makes diagnosis ambiguous.
Confidence: high
Scope-risk: moderate
Directive: Preserve unknown Computer Use config fields and keep blank interpreter paths normalized to automatic detection.
Tested: bun test src/utils/computerUse/preauthorizedConfig.test.ts src/server/__tests__/computer-use-python.test.ts src/server/__tests__/computer-use-api.test.ts
Tested: cd desktop && bun run test src/pages/ComputerUseSettings.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Computer Use browser smoke saved /opt/homebrew/bin/python3 and backend persisted then reset pythonPath to null
Not-tested: Full bun run check:server; existing cron-scheduler-launcher test expects CLAUDE_CODE_ENTRYPOINT=sdk-cli but received undefined.
Related: https://github.com/NanmiCoder/cc-haha/issues/331
2026-05-09 21:46:11 +08:00
Relakkes Yang
dfb69976fd fix: flush late transcript writes 2026-05-08 23:19:22 +08:00
程序员阿江(Relakkes)
f379a9928a fix: remove pr-checks lane and fix test env isolation
- Remove pr-checks lane from quality gate (not needed locally)
- Fix launcherRouting tests to pass explicit null envAppRoot to avoid CLAUDE_APP_ROOT pollution
- Fix cron-scheduler-launcher test: CLAUDE_CODE_ENTRYPOINT is correctly set to sdk-cli for scheduled tasks, update assertion and restore env var in cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 21:00:06 +08:00
程序员阿江(Relakkes)
f92083ac0c fix: prefer native worktree identity for session badges
CLI transcripts persist native worktree-state entries for resume, so the desktop git-info endpoint should treat that state as authoritative for worktree identity. Desktop launch metadata can be stale after placeholder cleanup or reopen, and should only fill gaps.

The displayed business branch still prefers the desktop launch branch because CLI originalBranch means the source checkout before worktree creation, not necessarily the selected base ref.

Constraint: CLI worktree-state carries runtime identity; Desktop repository metadata carries launch intent.

Rejected: Let desktop repository metadata override worktree-state | stale placeholder metadata can resurrect the wrong slug, path, or source cwd after reload.

Confidence: high

Scope-risk: narrow

Directive: Worktree identity fields should prefer CLI worktree-state; only branch display may prefer desktop launch intent.

Tested: bun test src/server/__tests__/sessions.test.ts --test-name-pattern worktree-state|worktree identity|git-info

Tested: cd desktop && bun run lint

Tested: git diff --check
2026-05-08 18:37:42 +08:00
程序员阿江(Relakkes)
f46b0c80cd fix: restore worktree badges from CLI transcript state
Desktop sessions already persist repository launch metadata, but CLI worktree sessions also write native worktree-state entries to the JSONL transcript for resume. Relying only on desktop session-meta leaves reopened or older transcripts vulnerable to losing the footer worktree identity when in-memory state is gone.

This teaches SessionService to recover the latest CLI worktree-state entry and lets git-info use it as a fallback for worktree badge fields. Desktop repository metadata still wins when present because it carries the selected business branch, while the CLI state keeps reopened sessions stable.

Constraint: CLI records worktree state as transcript metadata, not only as cwd.

Rejected: Infer worktree identity only from .claude/worktrees path | path heuristics miss hook-based worktrees and do not carry original cwd or slug.

Confidence: high

Scope-risk: narrow

Directive: Prefer explicit transcript metadata over path guessing for worktree session identity.

Tested: bun test src/server/__tests__/sessions.test.ts --test-name-pattern CLI worktree state

Tested: bun test src/server/__tests__/sessions.test.ts --test-name-pattern worktree-state|worktree identity|git-info

Tested: cd desktop && bun run lint

Tested: git diff --check
2026-05-08 18:29:17 +08:00
程序员阿江(Relakkes)
5f0ddaecb2 fix: keep worktree session metadata after CLI init
Worktree sessions briefly showed the expected footer badge and then reverted because the CLI init path produced a second transcript under the actual worktree cwd. The desktop placeholder still held the repository launch metadata, but the startup cleanup removed that placeholder after writing metadata through a lookup that could target the stale source transcript. The runtime session also kept the launch cwd instead of the init cwd.

This moves session metadata writes to the transcript matching the CLI init cwd, carries repository launch metadata across duplicate session files, and updates the live ConversationService workDir when the init event reports the real cwd.

Constraint: Native CLI worktree creation happens inside the CLI after desktop has already created a placeholder session.

Rejected: Patch the footer to remember the first worktree badge | it would hide stale backend state and leave other session APIs on the source checkout.

Confidence: high

Scope-risk: narrow

Directive: Treat the CLI init cwd as authoritative for active desktop sessions; do not let placeholder cleanup discard repository launch metadata.

Tested: bun test src/server/__tests__/sessions.test.ts --test-name-pattern worktree|git-info|placeholder

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run test -- src/components/shared/ProjectContextChip.test.tsx

Tested: git diff --check
2026-05-08 18:24:16 +08:00
程序员阿江(Relakkes)
3794a9d9ef fix: surface worktree identity in session badge
The footer project chip previously kept showing only the source project and selected branch even after a session launched inside an isolated CLI worktree. That made an active worktree session look identical to a normal checkout, especially while agent tools were already operating under .claude/worktrees. The server now returns explicit worktree metadata from the session launch intent, and the desktop chip preserves the user-facing project and branch while appending the isolated worktree slug.

Constraint: CLI worktree branches are internal implementation details and should not replace the user-selected branch label.

Rejected: Show only the actual worktree git branch | it would expose worktree-desktop-* internals and make the business branch harder to read.

Confidence: high

Scope-risk: narrow

Directive: Keep normal checkout footer rendering unchanged; only add worktree identity when session metadata says isolation is enabled.

Tested: bun test src/server/__tests__/sessions.test.ts --test-name-pattern git-info

Tested: cd desktop && bun run test -- src/components/shared/ProjectContextChip.test.tsx

Tested: cd desktop && bun run lint

Tested: git diff --check
2026-05-08 18:08:30 +08:00
程序员阿江(Relakkes)
678ba18a6f fix: keep session git badge on the active CLI checkout
The chat footer previously read git-info from the persisted session file and refreshed only on tab changes. Repository launches can update the active CLI cwd after the composer has already rendered, so the footer could keep showing an older selected branch even while the running model was operating in the correct checkout.

Constraint: The active CLI process is the source of truth for a live session cwd
Rejected: Derive the footer branch only from persisted session metadata | metadata can lag behind startup and does not prove the current running cwd
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/sessions.test.ts --test-name-pattern "git-info should prefer"
Tested: cd desktop && bun run lint
Tested: git diff --check
2026-05-08 18:02:12 +08:00
程序员阿江(Relakkes)
95b181dea2 feat: align desktop repository sessions with CLI worktrees
Desktop repository launches now defer isolated worktree creation until the
first user turn so the CLI owns worktree setup, cwd initialization, and
session metadata. The chat UI surfaces the pre-startup Git phase so users see
when a session is creating a worktree or switching a branch before model
output begins.

Constraint: Desktop must preserve the selected source checkout until a user actually sends a message
Constraint: CLI setup is the canonical owner for worktree creation and cwd initialization
Rejected: Create the worktree eagerly in the desktop session picker | it diverges from CLI session startup and creates worktrees before a conversation exists
Confidence: high
Scope-risk: moderate
Directive: Keep repository session startup routed through CLI worktree flags; do not reintroduce eager desktop worktree creation without testing transcript cwd and cleanup behavior
Tested: bun test src/server/__tests__/sessions.test.ts src/server/__tests__/conversation-service.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "worktree startup status"
Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
Tested: CLI init-only native worktree smoke from feature/rail
Tested: agent-browser UI flow with MiniMax-M2.7-highspeed in a /tmp repository
2026-05-08 16:46:51 +08:00
程序员阿江(Relakkes)
6e5dd94dad Stop bearer providers from inheriting local API keys
Desktop Anthropic-compatible relay providers use ANTHROPIC_AUTH_TOKEN for
Authorization, but the CLI process can still load a stale ANTHROPIC_API_KEY
from inherited env, original settings, keychain-backed config, or the SDK
constructor fallback. That made Provider Test pass while real sessions sent
both Authorization and x-api-key to relays such as Packy and Shengsuanyun,
which then rejected the request as an invalid token/key.

The client now avoids falling back to a local API key when bearer-token
provider auth is active. Provider-managed auth_token env also writes an empty
ANTHROPIC_API_KEY so cc-haha settings explicitly override stale original
settings during desktop/sidecar startup. Regression tests pin both the SDK
client behavior and the desktop child-env behavior.

Constraint: Anthropic-compatible relay providers can reject mixed Authorization plus x-api-key headers even when direct provider tests pass
Rejected: Treat this as a provider/model-name issue | local proxy captures showed the failing request shape before provider handling
Rejected: Rely only on client.ts fallback suppression | settings/env layering can leave ANTHROPIC_API_KEY populated before the client is constructed
Confidence: high
Scope-risk: moderate
Directive: Bearer-token provider sessions must keep Authorization without inheriting or reintroducing x-api-key from user/global settings
Tested: bun test src/services/api/client.test.ts
Tested: bun test src/server/__tests__/providers.test.ts src/server/__tests__/providers-real.test.ts src/server/__tests__/conversation-service.test.ts
Tested: bun run check:server
Tested: Source and packaged sidecar proxy captures with stale original ANTHROPIC_API_KEY showed Authorization=true and x-api-key=false after the fix
Tested: desktop/scripts/build-macos-arm64.sh
Tested: PackyCodex2ccCustom and 胜算云 packaged-sidecar live calls returned ok
Not-tested: Full manual UI click-through in the rebuilt macOS app window
2026-05-08 15:20:45 +08:00
程序员阿江(Relakkes)
a31b3045fd Preserve explicit desktop runtime selection
Desktop sessions can prewarm before the user-selected runtime reaches the server, and an empty-session draft could previously persist an inferred default runtime as if the user had explicitly selected it. The fix keeps new-session defaults implicit while preserving explicit draft selections, and waits for queued runtime restarts before the first turn is delivered to the CLI process.

Constraint: Runtime selection is session-scoped and may arrive while prewarm or first-turn startup is already in flight

Rejected: Always persist the current default provider/model into new sessions | this masks whether the user actually selected a runtime and can launch stale providers

Confidence: high

Scope-risk: moderate

Directive: Do not send user turns across pending runtime restarts without re-checking the session runtime override

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

Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx

Tested: git diff --check
2026-05-08 13:45:02 +08:00
程序员阿江(Relakkes)
c9cc5e68a6 Merge IM deleted-session recovery into main
The detached worktree fix has been verified on adapter and server lanes
and is now integrated into the local main branch without pushing.

Constraint: main already contains follow-up local commits beyond the worktree base
Confidence: high
Scope-risk: moderate
Tested: bun test common/__tests__/session-store.test.ts common/__tests__/ws-bridge.test.ts
Tested: bun test src/server/__tests__/websocket-handler.test.ts src/server/__tests__/sessions.test.ts --timeout 30000
Tested: cd adapters && bunx tsc --noEmit
Tested: bun run check:adapters
Tested: bun run check:server
Not-tested: bun run verify fails on pre-existing agent-utils coverage ratchet; changed-line coverage is 95.92% and affected lanes pass
Related: 1f62bcd
2026-05-08 09:58:42 +08:00
程序员阿江(Relakkes)
1f62bcd919 Prevent IM chats from reusing deleted desktop sessions
Deleting a desktop session removed the transcript but left IM adapter
chat mappings and live WebSocket state able to point at the old session.
The server now closes the active session socket and removes adapter
mappings after deletion, while adapters refresh the shared session store
before reads so a running process cannot reuse stale in-memory data.

Constraint: Adapter session mappings are shared through adapter-sessions.json across long-running IM processes
Rejected: Patch each adapter ensureSession path separately | shared store refresh fixes all current adapters and avoids drift
Confidence: high
Scope-risk: moderate
Directive: Do not cache adapter session mappings without invalidating after server-side session deletion
Tested: bun test common/__tests__/session-store.test.ts common/__tests__/ws-bridge.test.ts
Tested: bun test src/server/__tests__/websocket-handler.test.ts src/server/__tests__/sessions.test.ts --timeout 30000
Tested: cd adapters && bunx tsc --noEmit
Tested: bun run check:adapters
Tested: bun run check:server
Not-tested: bun run verify fails on pre-existing agent-utils coverage ratchet; changed-line coverage is 95.92% and affected lanes pass
Related: https://github.com/NanmiCoder/cc-haha/issues/305
2026-05-08 09:58:24 +08:00
程序员阿江(Relakkes)
7696e21dc2 Support provider runtimes for scheduled tasks
Scheduled tasks used the model selector in model-only mode, so a task could store a third-party model id without the provider runtime needed to execute it. The desktop task editor now stores the selected provider with the model, and the scheduler injects the same provider-scoped runtime env that desktop sessions use before launching the sidecar.

Constraint: Scheduled tasks run outside an active desktop websocket session, so provider runtime must be serialized on the task itself.
Rejected: Reuse session runtime localStorage for tasks | scheduled executions can run long after the UI session is gone.
Confidence: high
Scope-risk: moderate
Directive: Keep task execution provider env isolated from inherited process env; stale parent ANTHROPIC_* values must not override task provider choices.
Tested: cd desktop && bun run test -- src/components/tasks/NewTaskModal.test.tsx src/components/controls/ModelSelector.test.tsx
Tested: bun test src/server/__tests__/scheduled-tasks.test.ts src/server/__tests__/cron-scheduler-launcher.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Tested: git diff --check
Not-tested: Live third-party provider API call; covered by sidecar launch integration with provider proxy env.
2026-05-08 09:49:16 +08:00
程序员阿江(Relakkes)
57a77453ed fix: isolate desktop provider runtimes per session
Desktop sessions can run against different saved providers at the same time, so the runtime selection must remain scoped to the session from WebSocket startup through the CLI request path. This adds parallel-session regression coverage, a browser-driven fake-provider E2E, and refreshes idle connected sessions after provider saves so stale provider edits are reapplied without disturbing active turns.

Constraint: Desktop provider credentials must stay session-scoped and must not leak across concurrent CLI child processes
Rejected: Rely on provider test connectivity alone | it does not exercise the desktop WebSocket, session runtime, or CLI request path
Confidence: high
Scope-risk: moderate
Directive: Do not change runtime selection persistence or provider env injection without rerunning the parallel provider browser E2E
Tested: cd desktop && bun run test src/stores/providerStore.test.ts src/stores/chatStore.test.ts
Tested: CLAUDE_CONFIG_DIR=/Users/nanmi/.claude bun test src/server/__tests__/conversations.test.ts --timeout 30000
Tested: desktop/scripts/e2e-parallel-providers-agent-browser.sh
Tested: bun run quality:smoke --provider-model custom:main:custom-main --provider-model minimax:main:minimax-main
Tested: cd desktop && bun run lint
Tested: git diff --check
Not-tested: bun run verify passes fully; coverage lane still fails on the existing agent-utils ratchet below baseline
2026-05-08 08:53:05 +08:00
程序员阿江(Relakkes)
64913c9242 fix(desktop): prevent config restarts from masquerading as turns
Runtime and permission changes can require a CLI restart, but the desktop UI should not interpret that maintenance restart as an active assistant turn. The restart path now stays out of the running chat state while preserving the stop/start behavior that applies model and bypass-permission changes.

Constraint: bypassPermissions changes require a CLI restart so startup args are applied.
Rejected: Hide the stop button in the frontend | the backend would still conflate configuration restarts with agent turns and quick-send races would remain untested.
Confidence: high
Scope-risk: narrow
Directive: Keep runtime and permission restart transitions serialized before user_message handling.
Tested: bun test src/server/__tests__/conversations.test.ts -t "in-flight runtime restart|in-flight permission restart"
Tested: bun run check:server
Not-tested: Browser screenshot; this change is the WebSocket state contract, not layout rendering.
2026-05-07 19:44:08 +08:00
程序员阿江(Relakkes)
fd5dc097a7 fix: isolate desktop session streaming state
Desktop chat sessions can stream and reconnect concurrently, so shared in-process buffers must not be keyed only by process lifetime. This change scopes streaming delta buffers, task-tool refresh bookkeeping, and CLI task mutations to the session that produced the event. It also ignores stale client socket closes after a newer socket has replaced the active connection for the same session.

Constraint: Desktop users can keep multiple sessions and tabs active at the same time.
Rejected: Serialize all desktop streaming through one active session | would hide the race instead of preserving multi-session behavior.
Confidence: high
Scope-risk: moderate
Directive: Do not reintroduce process-global chat/task pending state without session keys and multi-session regression tests.
Tested: cd desktop && bun run test -- src/stores/chatStore.test.ts src/stores/cliTaskStore.test.ts
Tested: bun test src/server/__tests__/websocket-handler.test.ts
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: bun run check:server
Tested: bun run check:coverage | changed-lines 100%, fails only existing agent-utils baseline
Tested: bun run verify | 8 passed, 1 failed on existing agent-utils coverage baseline, 2 skipped
Not-tested: Real desktop dual-window manual smoke.
Related: https://github.com/NanmiCoder/cc-haha/issues/302
Related: https://github.com/NanmiCoder/cc-haha/issues/303
2026-05-07 18:33:57 +08:00
程序员阿江(Relakkes)
165d914d5a fix: launch scheduled tasks through desktop sidecar
Scheduled task Run Now was still constructing a source checkout Bun command, so packaged desktop builds could try to execute src/entrypoints/cli.tsx from a missing or wrong root. Resolve the same launcher used by the desktop runtime first, and keep the source checkout fallback only for development.

Constraint: Desktop Release tasks must run through the configured sidecar and app root instead of assuming source files exist on disk

Rejected: Retain import.meta.dir path math with broader fallback | still bypasses packaged desktop launcher and repeats the issue root cause

Confidence: high

Scope-risk: narrow

Directive: Do not change scheduled task execution back to direct source CLI invocation without repeating desktop Run Now E2E

Tested: bun test src/server/__tests__/cron-scheduler-launcher.test.ts src/server/__tests__/cron-scheduler.test.ts

Tested: bun run check:server

Tested: bun run check:native

Tested: Desktop UI E2E via scheduled task page create task and Run Now with fake sidecar

Not-tested: Full packaged macOS .app click test
2026-05-07 18:32:38 +08:00
程序员阿江(Relakkes)
8b07c16f45 fix: keep startup failures visible in terminals
Diagnostics should capture fatal server failures without replacing the terminal stderr signal that developers rely on during local startup. Fatal uncaught exceptions now print their stack to stderr, persist the diagnostic event, and exit non-zero so repeated server starts expose port conflicts immediately.

Constraint: Startup diagnostics must still write sanitized runtime events for export bundles.
Rejected: Only document checking runtime-errors.log | developers should not lose the standard stderr failure path.
Confidence: high
Scope-risk: narrow
Directive: Do not install process-level exception handlers that swallow fatal startup errors without preserving stderr and non-zero exit behavior.
Tested: bun test src/server/__tests__/diagnostics-service.test.ts
Tested: bun run check:server
Tested: SERVER_PORT=3456 bun run src/server/index.ts returned exit=1 and printed the port-conflict stack while diagnostics recorded the event
2026-05-07 16:50:20 +08:00
程序员阿江(Relakkes)
7fbce35be6 fix: prevent malformed generated session titles
Session titles need to appear immediately, but the async AI title pass can return fenced or escaped JSON fragments. Keep the first-message title path fast, preserve optimistic client titles during background refresh, and only accept structured title responses that parse cleanly.

Constraint: Desktop sessions should stop showing Untitled/New Session after the first user turn.
Rejected: Accept short raw model output as a fallback | malformed JSON fragments are short enough to leak into the UI.
Confidence: high
Scope-risk: moderate
Directive: Do not loosen generated title parsing without regression coverage for fenced, escaped, and truncated JSON responses.
Tested: bun test src/server/__tests__/title-service.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/stores/sessionStore.test.ts src/pages/EmptySession.test.tsx
Tested: agent-browser smoke on http://127.0.0.1:1420/ confirmed sidebar and tab titles update without JSON fragments.
2026-05-07 16:28:08 +08:00
程序员阿江(Relakkes)
7ae885a114 fix: add safe Doctor rescue path for desktop upgrades
Online upgrades can strand users on stale desktop UI state or malformed local persistence. This adds a deny-by-default Doctor path that resets only regenerable desktop UI state, reports protected local files with redacted metadata, and keeps protected repair as a dry-run no-op until a reviewed backup-first flow exists.

Constraint: Chat transcripts, model/provider config, Skills, MCP, IM bindings, adapter sessions, OAuth tokens, plugins, and team/session records are user-owned protected state.
Rejected: Automatically rewrite malformed protected JSON | unsafe without schema-specific migrations and backups.
Rejected: Continue relying only on startup migrations | users need an explicit recovery action after a white screen.
Confidence: high
Scope-risk: moderate
Directive: Keep Doctor repair deny-by-default; do not mutate protected state without an explicit reviewed backup-first manual repair flow.
Tested: bun test src/server/__tests__/doctor-service.test.ts
Tested: cd desktop && bun run test src/components/ErrorBoundary.test.tsx src/lib/doctorRepair.test.ts src/__tests__/diagnosticsSettings.test.tsx src/components/layout/StartupErrorView.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:server
Tested: bun run verify (failed only existing agent-utils coverage baseline; changed-lines coverage 97.62%)
Not-tested: Packaged desktop manual Doctor click path.
2026-05-07 00:04:06 +08:00
程序员阿江(Relakkes)
fa47adc33a fix: prevent upgrade crashes from stale persistence
Desktop users can carry provider indexes, managed settings, localStorage state, and native update state from builds that no longer match current readers. This adds startup migrations and recovery paths before server and React state are consumed, plus a persistence upgrade gate so future storage protocol changes ship with old-format fixtures.

Constraint: Existing installs may contain malformed or legacy JSON/localStorage that must not block startup.
Constraint: Local verify should evaluate the current worktree diff rather than unrelated detached-worktree history.
Rejected: Treat invalid persisted state as fatal | reproduces white-screen and startup failure behavior for existing users.
Rejected: Bypass PR policy locally | hides real gate behavior and does not fix detached-worktree false positives.
Confidence: high
Scope-risk: moderate
Directive: Any local JSON, localStorage, or app config shape change must add a migration fixture and keep `bun run check:persistence-upgrade` green.
Tested: bun run check:persistence-upgrade; bun run check:policy; bun run check:desktop; bun run check:server; bun run check:native; bun run verify (9 passed, 1 coverage baseline failure)
Not-tested: Live provider baseline; existing user configs beyond covered fixtures
2026-05-06 23:20:21 +08:00
程序员阿江(Relakkes)
b156be8d8d feat: make PR quality verification self-enforcing
Contributors and coding agents need one local command that both reports and enforces the quality contract. This change turns the PR gate into the shared verification entrypoint, adds path-selected local lanes, tightens coverage accounting around changed lines, and documents the repair loop in contributor and agent-facing guidance.

Constraint: Ordinary PR verification must stay non-live and runnable without provider credentials
Constraint: Coverage policy updates in this commit require maintainer approval before push/merge
Rejected: Keep quality guidance only in docs | agents need executable scripts and AGENTS.md instructions to follow the loop consistently
Confidence: high
Scope-risk: broad
Directive: Do not bypass `bun run verify` for production changes; fix failed lanes and coverage reports instead of lowering thresholds
Tested: bun run check:policy
Tested: ALLOW_CLI_CORE_CHANGE=1 ALLOW_COVERAGE_BASELINE_CHANGE=1 bun run verify
Not-tested: live provider baseline; no provider credentials were required for this non-live PR gate
2026-05-06 22:33:43 +08:00
程序员阿江(Relakkes)
9719726cd2 feat: make quality gates observable and enforceable
The repository now has a measurable PR quality path instead of a loose set of
manual checks. Coverage, quarantine governance, provider smoke, desktop smoke,
and workflow wiring all produce durable reports that contributors and maintainers
can inspect without reconstructing terminal output.

This also fixes the desktop smoke current-runtime path so browser-driven smoke
runs use the desktop default active provider instead of forcing the official
current model, and records that runtime decision as an artifact.

Constraint: Default PR gates must remain non-live and contributor-safe while live model checks stay explicit.
Constraint: Release packaging is still GitHub Actions based, so release preflight must run before the build matrix.
Rejected: Make live provider or desktop smoke mandatory on every PR | secrets, quotas, and model availability are maintainer-controlled.
Rejected: Let PRs lower coverage baselines in the same change | base-branch ratchet comparison must remain authoritative.
Confidence: high
Scope-risk: moderate
Directive: Do not relax coverage or quarantine policy without a maintainer approval label and a fresh quality report.
Tested: ALLOW_CLI_CORE_CHANGE=1 ALLOW_COVERAGE_BASELINE_CHANGE=1 bun run quality:gate --mode pr
Tested: bun run quality:gate --mode baseline --allow-live --only provider-smoke:* --provider-model nvidia-custom:main:nvidia-custom-main --artifacts-dir /tmp/quality-gate-live-smoke
Tested: bun run quality:gate --mode baseline --allow-live --only desktop-smoke:* --provider-model current:current:current-runtime --artifacts-dir /tmp/quality-gate-desktop-smoke-fixed
Tested: git diff --check
Not-tested: Full live release mode with multiple providers in hosted CI; provider credentials and quota remain maintainer-controlled.
2026-05-06 16:25:10 +08:00
程序员阿江(Relakkes)
f262ec3029 feat: protect desktop branch launches with worktree isolation
Desktop sessions now resolve repository launch intent before creation, so users can pick a branch, choose whether to isolate it in a worktree, and get stable error messages instead of silent branch-switch failures. The server owns the Git safety checks and session metadata so the UI and real agent runtime agree on the actual working directory.

Constraint: Direct branch switching must not overwrite or hide uncommitted user changes
Constraint: Desktop worktree branches must stay out of normal branch selection and recent-project labels
Rejected: Let the UI call git directly | server-side checks keep session metadata and runtime launch paths consistent
Rejected: Auto-switch dirty checkouts | users need an explicit isolated-worktree choice to protect local edits
Confidence: high
Scope-risk: moderate
Directive: Do not relax dirty-worktree or checked-out-branch guards without adding equivalent business-flow tests
Tested: ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Tested: /tmp business-flow script with dirty checkout, checked-out branch, isolated worktree, missing branch, non-git, missing directory, and real LLM session
Tested: agent-browser desktop UI flow across dirty, checked-out, non-git, branch search, recent-project, and Run scenarios
2026-05-06 14:51:46 +08:00
程序员阿江(Relakkes)
578a967c5a Merge commit '13b765b5e94fe08009d04bdbce2195d084a4b894' 2026-05-06 11:57:12 +08:00
程序员阿江(Relakkes)
13b765b5e9 Expose built-in workflow agents outside internal experiments
External builds were inheriting internal feature-gate defaults, so Explore,
Plan, and verification disappeared from the active agent registry. Keep the
internal ant experiment behavior gated, but make public builds default to the
full workflow agent set while preserving the SDK opt-out path.

Constraint: Internal ant builds still rely on GrowthBook rollout gates.
Rejected: Force-enable all users unconditionally | would bypass internal experiment controls.
Confidence: high
Scope-risk: narrow
Directive: Do not move public built-in agents behind internal-only feature flags without verifying desktop /api/agents and real Agent subagent orchestration.
Tested: bun test src/tools/AgentTool/builtInAgents.test.ts
Tested: git diff --check
Not-tested: Full quality gate in this commit step; earlier quality:pr is blocked by cli-core approval policy.
2026-05-06 11:56:59 +08:00
程序员阿江(Relakkes)
7e369ab7c7 Merge commit '147d060' 2026-05-06 11:44:12 +08:00
程序员阿江(Relakkes)
147d060004 fix: preserve custom provider thinking settings
Custom providers can point at user-defined model IDs that the CLI cannot
classify by built-in model tables. Declare the existing tier capability
overrides for custom provider roles so explicit thinking and effort settings
survive request construction.

Constraint: Issue #285 reports local proxy logs dropping user-defined model thinking and effort as passthrough with no config.
Rejected: Disable model capability checks globally | would relax behavior for built-in presets and non-custom providers.
Confidence: high
Scope-risk: narrow
Directive: Keep this scoped to custom providers unless each preset has explicit capability semantics.
Tested: bun test src/server/__tests__/providers.test.ts src/server/__tests__/provider-presets.test.ts src/utils/__tests__/thinking.test.ts
Tested: bun run check:server
Tested: bun run quality:pr
Tested: bun run quality:gate --mode baseline --allow-live --provider-model custom:main:custom-main
Tested: agent-browser custom provider UI smoke with request log showing thinking adaptive and effort max
Related: https://github.com/NanmiCoder/cc-haha/issues/285
2026-05-06 11:44:03 +08:00
程序员阿江(Relakkes)
c0fe61b665 Merge commit '1cd90dc' 2026-05-06 11:40:59 +08:00
程序员阿江(Relakkes)
1cd90dc66a feat: let users opt out of bundled Computer Use
Computer Use is useful when explicitly needed, but exposing its MCP tools by default creates unnecessary desktop-control surface for users who want coding-only sessions. This adds a shared disable path for CLI flags, environment, and desktop settings while keeping preauthorized app state in one config file.

The same change also preserves Windows and WSL shell startup behavior by applying the MSYS argument-conversion guard only on WSL-bound launches.

Constraint: Computer Use MCP must not be exposed to the Coding Agent when disabled

Constraint: Desktop settings and CLI sessions need to read the same persisted Computer Use config

Rejected: Environment-only disable switch | desktop users need a persistent Settings control

Rejected: Remove Computer Use setup entirely | enabled sessions still need the existing built-in MCP path

Confidence: high

Scope-risk: moderate

Directive: Keep every new Computer Use entrypoint wired through loadStoredComputerUseConfig or the CLI disable flag before adding MCP tools

Tested: bun test src/utils/computerUse/gates.test.ts src/utils/computerUse/preauthorizedConfig.test.ts src/server/__tests__/computer-use-api.test.ts src/utils/shell/wslInterop.test.ts desktop/src/pages/ComputerUseSettings.test.tsx

Tested: bun run check:server; bun run check:desktop; bun run check:docs; bun run check:policy; bun run check:native; git diff --check

Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh; codesign verify; hdiutil verify; built CLI Computer Use E2E exposure and disable checks

Not-tested: Full screenshot/control action after granting macOS Screen Recording permission on this machine
2026-05-06 11:40:49 +08:00
程序员阿江(Relakkes)
f9f1f20e99 Fix local proxy tool calls for real provider responses
Local OpenAI-compatible proxies can return function arguments as parsed objects instead of JSON strings. Preserve object-shaped arguments directly and serialize them for streaming deltas so Write, Bash, and Edit tool calls keep the fields required by Anthropic tool validation.

Constraint: Local proxy implementations such as OneAPI/NewAPI may not preserve OpenAI's string-only arguments shape.
Rejected: Normalize every upstream response through JSON.stringify before parsing | loses already-valid object identity and leaves streaming object deltas vulnerable to [object Object].
Confidence: high
Scope-risk: narrow
Directive: Do not assume function.arguments is always a string on provider responses or stream deltas.
Tested: bun test src/server/__tests__/proxy-streaming.test.ts src/server/__tests__/proxy-transform.test.ts
Tested: bun run check:server
Tested: bun run check:native
Tested: bun run quality:pr
Tested: agent-browser with real Ollama qwen3:4b provider for Write/Bash/Edit and streaming Write
Not-tested: Real Windows OneAPI/NewAPI instance from the issue reporters was not available locally.
2026-05-06 11:39:10 +08:00
程序员阿江(Relakkes)
b72bca94e3 fix: wait for sdk socket before context inspection
Desktop context inspection could send get_context_usage while a prewarmed CLI process existed but before the SDK socket was connected. That first control request sat in the pending outbound queue and could time out, leaving the composer spinner or a misleading empty-state context display. The server now waits for the SDK socket before sending control requests, and the UI renders the actual initial context snapshot instead of masking empty active-session data.

Constraint: CLI /context and desktop get_context_usage must stay on the shared collectContextData/analyzeContextUsage path

Rejected: Hide zero-token active snapshots in the component | that masks backend timing bugs and prevents showing real initial system/tool context

Confidence: high

Scope-risk: narrow

Directive: Do not queue control requests before the SDK socket is connected; let user messages keep the pending outbound behavior

Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "not queue control|context-only|prewarmed empty session|structured session inspection"

Tested: cd desktop && bun run test -- pages.test.tsx --test-name-pattern "EmptySession shows draft context|first-paint spinner|empty live session|live context usage|runtime model"

Tested: bun run check:server

Tested: cd desktop && bun run lint

Tested: real dev API prewarmed empty session returned 31,190 / 262,144 (12%) on first context-only inspection and 16ms on repeat

Tested: browser at http://127.0.0.1:5174/ showed 上下文用量 12% for an empty active session
2026-05-05 20:10:04 +08:00
程序员阿江(Relakkes)
1e5bd47fe6 Merge commit '622b94011ff962fc80f010d98b4dab9132834878' 2026-05-05 19:59:03 +08:00
程序员阿江(Relakkes)
1029c9e7bc fix: align transcript context fallback with cli totals
The live desktop context path already routes through the CLI get_context_usage control request and the shared analyzeContextUsage implementation. The offline transcript fallback was still using its own prompt-token-only estimate, so disconnected sessions could report a lower context total than the live /context path. The fallback now reuses the same current-context total helper and includes output tokens as next-turn context.

Constraint: Desktop must still show context estimates when the CLI process is not running

Rejected: Keep transcript fallback prompt-only | it diverges from the live CLI /context total and can drop after responses

Confidence: high

Scope-risk: narrow

Directive: Transcript context fallback must stay aligned with calculateCurrentContextTokenTotal

Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "context-only|structured session inspection|reconstruct Sonnet 4.6 transcript usage"

Tested: bun test src/utils/__tests__/context.test.ts

Tested: bun run check:server
2026-05-05 19:52:48 +08:00
程序员阿江(Relakkes)
f02fe83e28 fix: keep context usage from dropping after responses
Context inspection was switching from the local context estimate to the latest
provider usage total once a response completed. That provider total omitted the
assistant output tokens, even though those tokens become part of the next turn's
context, so the desktop meter could fall from 11% to 10% after work finished.
The context total now includes output tokens and keeps the local estimate as a
lower bound.

Constraint: Provider usage and local context estimates use different token accounting paths
Rejected: Trust provider input tokens alone | it omits the latest assistant output from the next-turn context
Confidence: high
Scope-risk: narrow
Directive: Context usage totals must represent the next-turn conversation context, not billing-only input tokens
Tested: bun test src/utils/__tests__/context.test.ts
Tested: bun run check:server
2026-05-05 19:42:06 +08:00
程序员阿江(Relakkes)
71f851d60a fix: clear DingTalk bot credentials through explicit unbind
DingTalk bot unbind was routed through the generic adapter config update,
but undefined credential fields are dropped during JSON serialization. That
left stored Client ID and Secret values intact after clicking the unbind
button. Match the working WeChat path with a dedicated server endpoint and
frontend API call so credential removal is explicit.

Constraint: JSON request bodies cannot preserve undefined fields for generic config clearing
Rejected: Keep using updateConfig with undefined fields | serialized requests omit the fields before the server can clear them
Confidence: high
Scope-risk: narrow
Directive: Keep credential-clearing flows on explicit unbind endpoints rather than relying on partial config merges
Tested: bun test src/server/__tests__/adapters.test.ts
Tested: cd desktop && bun run test src/stores/adapterStore.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Not-tested: bun run quality:pr is blocked by repository policy requiring allow-cli-core-change approval for the broader branch impact
2026-05-05 19:30:43 +08:00
程序员阿江(Relakkes)
ac342f4bcd Merge commit 'e4f9825bc116e88487d3c479e4e7afc1ec3a34fe' 2026-05-05 18:26:05 +08:00
程序员阿江(Relakkes)
e4f9825bc1 fix: avoid misleading context loading on empty desktop sessions
The desktop composer now treats draft and not-yet-running sessions as pending
until a real CLI context snapshot exists. Context inspection has a lightweight
context-only path so the composer and /context panel do not wait on unrelated
usage or MCP status work, and desktop boot no longer blocks the first shell
render on tab restoration.

Constraint: Empty composer state has no live CLI context to inspect until a session starts
Rejected: Display 0% before live context exists | it implied one user message consumed the fixed prompt/tool baseline
Confidence: high
Scope-risk: moderate
Directive: Do not show numeric context usage without a real context snapshot
Tested: cd desktop && bun run test -- pages.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: bun run check:server
Tested: Browser reload at http://127.0.0.1:5174 with cleared open-tabs showed pending context and no /inspection request
2026-05-05 18:25:53 +08:00
程序员阿江(Relakkes)
851b33ab70 Merge actionable diagnostics logging into main
Bring the runtime diagnostics worktree back onto local main while preserving the existing local main commits for provider context windows and IM localization.

Constraint: Preserve both independent local main commits and the diagnostics worktree commit.
Confidence: high
Scope-risk: moderate
Directive: Keep diagnostics capture non-blocking and sanitized when modifying this area again.
Tested: merged cleanly on local main after prior check:server, check:desktop, and agent-browser diagnostics export verification
2026-05-05 17:59:46 +08:00