89 Commits

Author SHA1 Message Date
程序员阿江(Relakkes)
e4e7026cde fix(provider): diagnose stalled streams
Classify stream watchdog timeouts by stream phase and include non-sensitive diagnostics for stalled provider streams.
Map known watchdog failures to stable desktop WebSocket error codes, and only retry watchdog aborts before content or tool activity starts.

Tested: bun test src/services/api/streamWatchdog.test.ts
Tested: bun test src/server/__tests__/ws-memory-events.test.ts --test-name-pattern "maps watchdog API errors to stable desktop error codes"
Tested: cd desktop && bun test src/stores/chatStore.test.ts --test-name-pattern "persists partial assistant text before an error"
Tested: bun run check:server
Tested: cd desktop && bun run check:desktop
Tested: 5x direct CLI replay, 5x desktop-chain replay, 5x desktop-chain no-entrypoint replay with session 446f8776-538d-46e6-96d6-67080b455b07
2026-07-08 14:13:37 +08:00
程序员阿江(Relakkes)
35f43e8289 fix: resolve post-release semantic conflicts
Fix cross-issue regressions found during post-0.4.4 merge review:\n\n- preserve permission mode across clear and empty-session replacement flows\n- keep provider effort passthrough and context-window estimates aligned with runtime metadata\n- invalidate recent project caches and trace message signatures when sessions change\n- recognize Windows ARM64 unpacked package-smoke output\n\nTested: bun test scripts/quality-gate/package-smoke/index.test.ts scripts/quality-gate/runner.test.ts\nTested: bun run check:desktop\nTested: bun run check:server\nConfidence: high\nScope-risk: moderate
2026-07-02 22:11:43 +08:00
程序员阿江(Relakkes)
531ce4063d fix: honor bypass permission mode (#910)
Make bypassPermissions skip permission-approval ask decisions while preserving explicit denies and tools that require user interaction. Keep desktop permission mode state authoritative by waiting for server/CLI confirmation and persisting CLI-originated mode broadcasts.

Tested:
- bun test src/utils/permissions/permissions.test.ts
- bun test src/server/__tests__/conversations.test.ts -t "permission switch|permission restart|permission-mode broadcasts|permission changes made before|bypass permissions back to default|runtime-only model switch"
- bun test src/server/__tests__/ws-memory-events.test.ts
- cd desktop && bun run test -- src/stores/chatStore.test.ts --run
- bun run check:server

Scope-risk: moderate
2026-07-02 21:23:30 +08:00
程序员阿江(Relakkes)
c6be08e471 fix(desktop): restore skill command history (#926)
Preserve user-invoked skill command metadata during session restore,
WebSocket replay, and session search while continuing to hide malformed
command breadcrumbs.

Tested:
- bun test src/server/__tests__/sessions.test.ts
- bun test src/server/__tests__/ws-memory-events.test.ts
- bun test src/server/__tests__/searchService.sessions.test.ts
- cd desktop && bun run test -- --run src/stores/chatStore.test.ts
- bun run check:server
- cd desktop && bun run build
- git diff --check

Not-tested:
- bun run check:desktop did not complete because an existing MessageList timestamp assertion fails independently of this change.

Confidence: high
Scope-risk: narrow
2026-07-02 21:01:06 +08:00
程序员阿江(Relakkes)
08c2d8cf5b fix(desktop): separate goal continuation turns
Emit a visible Goal continuing local-command marker before managed /goal continuations, then preserve it through server events, session history, and desktop history restore.

Render the marker as a compact divider so a new assistant continuation after end_turn is visually separated from the previous assistant output.

Related: #901

Tested: bun test src/query/stopHooks.test.ts src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/sessions.test.ts && cd desktop && bun run test MessageList.test.tsx chatStore.test.ts

Tested: bun run check:server

Tested: bun run check:desktop

Not-tested: live release build and Windows v0.4.3 repro path; keep issue open until release validation.

Scope-risk: moderate

Confidence: high
2026-06-23 19:20:03 +08:00
程序员阿江(Relakkes)
3ba8b44a23 fix: preserve cache token usage in live sessions
Map CLI result cache_read_input_tokens/cache_creation_input_tokens into the desktop websocket usage payload, and count cache read/write tokens in the active session header token badge.

Fixes locally: #842

Tested: bun test src/server/__tests__/websocket-handler.test.ts

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

Tested: bun run check:server

Tested: bun run check:desktop

Not-tested: live Windows v0.4.3 reproduction; GitHub issues remain open until release and retest.

Confidence: high

Scope-risk: narrow
2026-06-23 16:31:51 +08:00
zhb
eb5a1b0278
优化停止,出现API Error: Stream ended without receiving any events
优化停止,出现API Error: Stream ended without receiving any events
2026-06-21 13:35:52 +08:00
程序员阿江(Relakkes)
9111c0c242 fix(server): close prewarm idle timer race in CLI-startup blind window
#865 added guards against the prewarm_session/user_message concurrency
race, but both keyed on isSessionTurnActive (messageSent===true).
messageSent only flips true after CLI startup completes, so during the
startup window a user turn is registered yet invisible to the guards — the
prewarm idle timer could still arm on, and later fire against, an active
turn, killing the CLI and leaving the UI stuck on "执行中".

- Add hasPendingOrActiveUserTurn() (turn registered regardless of
  messageSent) and use it for both prewarm guards, covering the blind window.
- Structurally disarm the timer when a turn completes (clearPrewarmState in
  bindActiveUserTurnCompletion), so a timer armed by the race never outlives
  the turn regardless of ordering.
- Add test hooks + 3 regression tests: blind-window turn not killed, active
  turn not killed, truly-idle session still reclaimed.
2026-06-20 16:17:08 +08:00
zhb
db1ebabe40
优化客户端几乎同时发送 prewarm_sessionuser_messageclearPrewarmState 什么也没清除,导致kill CLI,客户端永远收不到 status: idle 或 error,页面卡在"执行中"
Added safety checks to prevent prewarm idle timer from interrupting active sessions.
2026-06-19 00:45:57 +08:00
程序员阿江(Relakkes)
1c554dc30d fix: stream background subagent tool activity to desktop (#829)
Background (run_in_background) subagents run detached from the main query loop, so their tool_use/tool_result never reached the desktop and their cards stayed on "no tool activity" even after completion. Route the activity through the SDK event queue as agent_tool_activity, re-emitted by the ws handler as tool_use_complete/tool_result tagged with the parent Agent tool_use id, so the UI groups them under the agent card like synchronous subagents.
2026-06-16 14:00:25 +08:00
程序员阿江(Relakkes)
0a7db60394 fix(desktop): hide slash command breadcrumbs on restore (#833)
Tested: bun test src/server/__tests__/ws-memory-events.test.ts
Tested: bun test src/server/__tests__/sessions.test.ts -t "command breadcrumb"
Tested: bun test src/server/__tests__/searchService.sessions.test.ts
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Scope-risk: narrow
Confidence: high
2026-06-15 23:00:46 +08:00
程序员阿江(Relakkes)
67660ab4e5 fix(desktop): surface non-streaming fallback in chat status (#766)
api_retry heartbeats already reach the desktop status bar, but the
streaming-to-non-streaming fallback had no signal at all: the CLI only
flipped an internal flag, and the one-shot fallback response can take
minutes with zero incremental output, so the UI showed a bare spinner
the whole time.

- CLI: yield a {type:'system', subtype:'streaming_fallback', cause}
  message at both fallback sites (stream error/watchdog and 404 stream
  creation), mirroring the existing api_error -> api_retry path through
  query.ts passthrough, QueryEngine SDK output, and the SDK schema.
- Server: translate it to a streaming_fallback ServerMessage;
  unrecognized causes normalize to 'unknown' instead of dropping the
  event.
- Desktop: track it as active-turn state (cleared at the same 12 sites
  as apiRetry; a fallback notice supersedes the stale retry banner) and
  render a neutral pill with the turn timer - expected state, not an
  error, so no amber styling and nothing in the diagnostics panel.
- Retry banner now shows "retrying now" once the countdown elapses
  instead of sticking at "waiting 0s".

With 62241a31 disabling the non-streaming fallback for desktop CLI
sessions, this notice mainly covers the 404 gateway path (which skips
the disable check), callers that re-enable fallback via env, and
non-desktop SDK consumers.

Constraint: Retries and fallbacks are expected states per the
diagnostics severity standard - lightweight active-turn UI only, no
error-panel entries, no transcript persistence.
Tested: bun test src/server/__tests__/ws-memory-events.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run test -- --run
Tested: cd desktop && bun run lint
Not-tested: live provider outage reproduction; verified via unit
coverage of the translation, store lifecycle, and indicator rendering.
2026-06-12 18:32:28 +08:00
程序员阿江(Relakkes)
5cf8c1bb67 fix(desktop): queue active-turn prompts (#755)
Add pending user-message queue controls while a desktop turn is running, replay queued prompts into the transcript when the CLI accepts them, and flush remaining prompts after turn completion.

Tested: cd desktop && bun run test -- src/components/chat/ChatInput.test.tsx
Tested: cd desktop && bun run test -- src/stores/chatStore.test.ts
Tested: bun test src/server/__tests__/ws-memory-events.test.ts
Tested: bun run check:desktop
Not-tested: bun run check:server remains blocked by unrelated E2E CORS preflight expectation, expected 403 but got 204.
Confidence: medium
Scope-risk: moderate
2026-06-12 16:44:52 +08:00
程序员阿江(Relakkes)
53d8e7ad77 feat(desktop): stabilize H5 access tokens, ports, and background sessions (#767, #764)
H5 远程访问的三处不稳定来源修复,让手机出门在外也能稳定连接、长任务不丢。

#767 令牌与端口固定:
- 令牌明文持久化到 cc-haha/settings.json,重启后二维码/令牌随时可查、可复制;
  enable 复用现有令牌、disable 保留令牌,仅 regenerate 才轮换。手改 token 字段
  即自定义固定令牌。完整令牌只经 local-trusted 面返回,远端 403。
- 新增可选固定端口 fixedPort,并在未配置时复用上次端口(desktop-server-state.json
  sticky,Electron/Tauri 双壳共享),反向代理/手机书签跨重启不失效;占用时回退随机。

#764 断连不杀正在运行的 CLI:
- 客户端断开时若该会话仍在跑一轮任务,不再 30s 后强杀子进程,而是等任务跑完;
  手机锁屏/切后台时长任务在后台跑完,重连即见结果。
- 空闲清理超时改为可配 disconnectGraceSeconds(H5 访问设置页,默认 30s),
  经 disconnectGraceConfig 同步缓存供 close 处理读取。

server / Electron / Tauri / React 四层 + 五语言 i18n + 配套单测全部覆盖。
2026-06-12 16:37:00 +08:00
程序员阿江(Relakkes)
d7b8fb032c fix(desktop): preview exit plan approvals (#793)
Show ExitPlanMode approvals as a rendered plan preview in desktop chat,
forward plan feedback and requested prompt permissions through the desktop
WebSocket permission response, and keep permission-mode restoration owned by
the CLI runtime.

Tested: bun run verify
Confidence: high
Scope-risk: moderate
2026-06-12 11:16:31 +08:00
程序员阿江(Relakkes)
e8fcccebf0 fix: defer permission restarts during active turns (#626)
Persist permission mode changes immediately, but defer restart-only permission transitions until the active desktop turn emits its terminal result.

Tested:
- bun test src/server/__tests__/conversations.test.ts -t "should defer bypass permission restarts until the active turn completes"
- bun test src/server/__tests__/conversations.test.ts -t "permission"
- bun run check:server

Scope-risk: narrow
2026-06-11 00:00:11 +08:00
程序员阿江(Relakkes)
d3b2f868a9 fix: guard prewarm resume shutdown paths (#611)
Prevent desktop prewarm launches from inheriting interrupted-turn resume state, while leaving explicit user-message startup unchanged. Also stop server-side background schedulers during shutdown so app quit cannot leave scheduled task runners alive.

Tested:

- bun test src/server/__tests__/conversation-service.test.ts

- bun test src/server/__tests__/conversations.test.ts -t "prewarm"

- bun test src/server/__tests__/conversations.test.ts -t "permission"

- bun test src/server/__tests__/diagnostics-service.test.ts -t "keeps fatal startup errors visible on stderr while recording diagnostics"

- bun test desktop/electron/services/windows.test.ts desktop/electron/services/tray.test.ts desktop/electron/services/sidecarManager.test.ts src/server/__tests__/server-shutdown.test.ts

- bun run check:server
2026-06-09 21:44:46 +08:00
程序员阿江(Relakkes)
385b996736 fix: defer runtime restarts during active turns (#626)
Avoid killing an in-flight desktop SDK turn when runtime config changes during tool execution. Persist the requested runtime immediately, then restart after the turn emits its terminal result.

Tested:
- bun run check:server
- real provider tool-turn model switch smoke
- bun run check:coverage (changed-line coverage passed; unrelated global lanes still fail)

Scope-risk: narrow
2026-06-09 21:06:24 +08:00
程序员阿江(Relakkes)
892f5e193a fix(desktop): restore permission mode after exiting plan mode (#623)
桌面端用 bypassPermissions 进入计划模式、退出后,权限选择器停留在"计划模式",
导致下一个工具调用又弹权限申请。两层根因都修:

- 回传链路断裂:服务端 WS handler 把 CLI 广播的权限模式变化(status:null +
  permissionMode)当成 thinking 丢弃,桌面端协议也没有承载权限模式的入站消息,
  CLI 恢复后的权限永远同步不到 UI。新增 permission_mode_changed 回传通道,桌面端
  据此校正选择器(只更新本地、不回发避免回环;未知模式忽略)。

- 重启抹掉 prePlanMode:bypass→plan 因策略"切换涉及 bypass 就重启"而重启 CLI,
  新进程直接以 plan 启动、prePlanMode 为空,ExitPlanMode 只能恢复成 default 而非
  bypassPermissions。收窄 needsRestart 为只在"进入 bypass"时重启;从 bypass 切出
  保持进程不变、走进程内 transition,CLI 才会栈存 prePlanMode 并在退出 plan 时
  正确恢复 bypass,与 TUI 行为一致。

测试:服务端 ws-memory-events 新增权限回传 + 重启策略用例;桌面端 chatStore 新增
回传校正 + 防回环 + 未知模式忽略用例。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 22:29:01 +08:00
程序员阿江(Relakkes)
837337901d fix(diagnostics): stop logging expected states as errors and isolate tests
The diagnostics panel filled with red ERROR/WARN entries during normal use
because the capture layer treated "wrote to console" as "something failed",
with no severity gatekeeping. Much of the noise was also test runs leaking
into the user's real ~/.claude/cc-haha/diagnostics.

- diagnosticsService: default unclassified events to info (not error);
  drop writes under NODE_ENV=test when no CLAUDE_CONFIG_DIR is set; document
  the console-capture contract (expected states use console.debug/info).
- index: don't install console/process capture under bun test.
- api/diagnostics: an ingested event with missing severity defaults to info.
- oauthRefreshLog (new): token refresh failure is gracefully handled and is
  never an error — expected expiry (401/403/revoked) logs at debug, anything
  else at warn. Wired into both Haha OAuth services.
- conversationService: classify cli_runtime_exit severity by exit code, so
  clean/SIGTERM/SIGKILL exits are info and only abnormal codes stay error
  (real "chat died" crashes remain perceivable).
- ws/handler: streaming partial tool-input JSON is normal — debug, not warn.

Tests: oauth-refresh-log + cli-exit-severity unit tests; diagnostics-service
covers the info default and the test-isolation guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:12:45 +08:00
程序员阿江(Relakkes)
349955dbf2 fix: route agent slash through normal chat (#653)
/agent should behave like a normal session prompt that tells the main model to use the selected Agent tool. The previous forked command path buffered the subagent run behind local command output and forced desktop clients to wait for command completion instead of seeing the normal Agent interaction stream.

Constraint: Desktop /agent must preserve ordinary chat/session semantics and still require /agent <agent> <prompt>.
Rejected: Keep slash_agent SDK event bridging | it preserves the separate forked execution path instead of fixing the product flow.
Confidence: high
Scope-risk: moderate
Directive: Do not move /agent back to context: fork without proving the desktop normal Agent tool stream still works.
Tested: bun test src/commands/agent.test.ts src/utils/processUserInput/processSlashCommand.test.ts src/server/__tests__/ws-memory-events.test.ts
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx
Tested: git diff --check
Not-tested: bun run check:server is blocked before source checks by expired quarantine entries: server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
2026-06-03 11:41:12 +08:00
程序员阿江(Relakkes)
0ebb0582b0 fix: stream agent slash progress to desktop (#653)
The desktop /agent slash path runs before the normal query loop, so the old implementation waited for the forked command to finish and only surfaced the final local-command stdout. Emit foreground slash-agent task events from /agent, drain SDK events while QueryEngine waits for pre-query slash processing, and allow those current-turn events through the WebSocket pre-send mute gate.

Constraint: /agent is a forked slash command that does not produce ordinary assistant deltas until its final stdout is synthesized.

Rejected: Frontend-only loading state | would not expose real forked-agent progress or tool/tool-result updates.

Confidence: high

Scope-risk: moderate

Directive: Keep slash_agent task events scoped to /agent; ordinary slash command stdout and /goal event handling should remain on the existing local-command paths.

Tested: bun test src/QueryEngine.test.ts src/utils/processUserInput/processSlashCommand.test.ts src/server/__tests__/ws-memory-events.test.ts

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

Not-tested: bun run check:server is blocked by expired quarantine review entries: server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
2026-06-03 11:25:36 +08:00
程序员阿江(Relakkes)
f060d20b3b fix: keep generated session titles in user language
Async title refresh runs after assistant output, so provider prompts can be biased by assistant language and English title examples. The title language now resolves from the first meaningful user message before consulting the response-language fallback, then retries once when a generated title violates that language.

Constraint: Desktop response-language settings still control assistant prose, not automatic title metadata when the first user message has a clear language
Rejected: Follow response-language setting for every generated title | Chinese sessions flip to English when replies are configured in English
Rejected: Persist mismatched generated titles after retry failure | preserves a bad UX over the safer first-message placeholder
Confidence: high
Scope-risk: moderate
Directive: Do not change automatic title language precedence without re-testing image/text first-turn sessions and English-response-language sessions
Tested: bun test src/server/__tests__/title-service.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts -t "refreshes the first-turn AI title"
Tested: git diff --check
Tested: real MiniMax-M3 /tmp session 58a47fcd-6f9d-4685-bc73-f9317e3f330e with temporary response language english; assistant replied English and final aiTitle was 标题语言验证
Not-tested: bun run check:server blocked before tests by expired quarantine entries server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
2026-06-03 11:22:29 +08:00
程序员阿江(Relakkes)
be51b4cbc8 fix: generate session titles after assistant turns
Desktop title generation previously upgraded the first sidebar title from the raw user prompt before assistant output existed. That made image-plus-text sessions vulnerable to keeping a title based only on the user's initial wording. This moves polished title generation to completed turns, reuses the CLI title prompt and transcript extractor, and routes title calls through the configured haiku model with a retry when disabled thinking is rejected.

Constraint: The first user message still needs an immediate placeholder title so the sidebar is responsive.
Rejected: Generate the AI title before assistant output | image-derived context is only available after the assistant turn completes.
Confidence: high
Scope-risk: moderate
Directive: Keep polished automatic titles tied to completed transcript turns, not pre-response user prompts.
Tested: bun test src/server/__tests__/title-service.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts -t "refreshes the first-turn AI title"
Tested: git diff --check
Tested: Real MiniMax-M3 /tmp session f89073ed-3467-492a-b63c-e1886c146a3a with image attachment wrote ai-title "Validate session title generation"
Not-tested: bun run check:server is blocked by existing expired quarantine manifest entries
2026-06-03 10:06:34 +08:00
程序员阿江(Relakkes)
d075276a17 fix: prevent idle tabs showing active during prewarm (#681)
Desktop reconnects can attach a normal output callback while a session prewarm is still pending. CLI startup status then reaches the renderer and marks restored idle tabs as running even though no user turn is active.

Treat pending prewarm like completed prewarm and bind metadata-only output until a real user turn claims the session.

Constraint: Prewarm still needs init and slash-command metadata without streaming UI status.\nRejected: Filter status in TabBar | leaves other clients and sidebar state exposed to the stale activity signal.\nConfidence: high\nScope-risk: narrow\nTested: bun test src/server/__tests__/websocket-handler.test.ts\nTested: bun test src/server/__tests__/conversations.test.ts -t prewarm\nTested: bun run check:server\nNot-tested: Manual desktop restart visual smoke
2026-06-01 16:53:11 +08:00
程序员阿江(Relakkes)
998e85efab fix: localize synthetic business errors
Synthetic API errors were carrying only English display text, so desktop chat could not distinguish our compatibility guidance from raw upstream failures and recovery logic depended on those English strings.

Add stable business error codes for media, PDF, request-size, prompt-length, and auto-mode errors. Desktop chat now resolves those codes through locale keys and suppresses the stale English fallback for known business errors, while normalizeMessagesForAPI still supports legacy text matching for old transcripts.

Constraint: Raw upstream provider details must remain available for diagnostics but should not drive localized business UX.

Rejected: Localize src/services/api/errors.ts strings directly | that would make persisted transcript text and media recovery depend on the active UI language.

Confidence: high

Scope-risk: moderate

Directive: Add new user-facing synthetic API errors with businessErrorCode values before localizing their display text.

Tested: bun test src/services/api/errors.test.ts tests/mediaRecoveryAndEstimation.test.ts src/server/__tests__/ws-memory-events.test.ts

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

Tested: bun run check:server

Tested: bun run check:desktop

Not-tested: Manual desktop screenshot smoke for the rendered Chinese error card
2026-06-01 00:36:29 +08:00
程序员阿江(Relakkes)
1f9af7e578 feat: scope desktop reasoning effort to sessions
Desktop reasoning effort now follows the selected session runtime instead of the General settings surface. Session metadata records provider, model, and effort so reconnects and prewarm restarts preserve the per-session runtime, while the default effort is max for new sessions.

Constraint: Existing transcript and localStorage runtime data must remain readable without a required migration.
Rejected: Keep effort as a General setting | it made one session change leak into every other session.
Confidence: high
Scope-risk: moderate
Directive: Do not reintroduce global desktop effort controls without proving multi-session isolation.
Tested: bun run check:server
Tested: bun run check:desktop
Tested: bun run check:native
Tested: bun run check:policy
Tested: bun run check:coverage
Tested: cd desktop && bun run test -- src/components/controls/ModelSelector.test.tsx src/stores/chatStore.test.ts
Tested: bun test src/server/__tests__/settings.test.ts --test-name-pattern "effort"
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "runtime"
Not-tested: Live provider smoke for domestic providers without local credentials.
2026-05-31 17:42:30 +08:00
程序员阿江(Relakkes)
1bc65321e0 fix: avoid desktop image Read failures (#615)
Desktop image attachments were sent as @path references, which made the model invoke Read on the original file before the existing CLI image resizing path could run. This aligns desktop attachment materialization with the CLI behavior by resizing image data locally and sending SDK image content blocks, while preserving @path fallback for non-image files and failed image normalization.

Constraint: Desktop still needs local source paths for UI/context metadata while model input should avoid first-turn Read for images

Rejected: Route all attachments through @path | keeps the oversized image failure path for desktop images

Confidence: high

Scope-risk: moderate

Directive: Keep desktop image attachments aligned with CLI pasted-image processing before changing this path

Tested: bun test src/server/__tests__/conversation-attachments.test.ts

Tested: bun run check:server

Not-tested: Live GPT-5.5 proxy request with real provider credentials
2026-05-31 17:40:45 +08:00
程序员阿江(Relakkes)
3f4d731cc7 fix: scope permission mode to sessions
Permission mode is a per-session runtime choice, while scheduled tasks cannot rely on a human approval loop. This removes the General settings permission surface, persists session permission metadata, and forces scheduled tasks to run with bypass permissions. Runtime permission changes now handle both directions across bypass boundaries, including startup/prewarm races, by persisting first and restarting only when the CLI launch mode must change.

Constraint: Scheduled tasks must be able to execute without a human standing by for authorization.

Constraint: The CLI only honors bypass permissions when launched with the skip-permissions flag, so switching to or from bypass requires a restart.

Rejected: Keep a global General permission default | it can leak across sessions and scheduled runs in ways the user cannot reason about.

Confidence: high

Scope-risk: broad

Directive: Do not reintroduce a global UI permission selector without proving it cannot affect unrelated sessions or automations.

Tested: bun test src/server/__tests__/conversations.test.ts -t "permission" --timeout 30000 (10 pass, 0 fail)

Tested: bun run check:server (858 pass, 0 fail)

Tested: cd desktop && bun run check:desktop (760 tests plus production build passed)

Tested: desktop/scripts/build-macos-arm64.sh and codesign verification passed

Tested: Real DeepSeek smoke validated plan, bypass, and scheduled task permission behavior

Not-tested: Did not repeat the full DeepSeek smoke after the final startup-race hardening; mock WebSocket permission regression and full server gate were rerun after that change.

Fixes #632
2026-05-31 17:40:45 +08:00
程序员阿江(Relakkes)
d327b7fb30 fix: preserve pending user questions across reconnects
Desktop AskUserQuestion waits on the SDK permission bridge, and a transient renderer disconnect could previously let the short no-client cleanup kill the CLI. That turned a live user question into an AbortError and left history restore showing a stale interactive card.

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

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

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

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

Confidence: high

Scope-risk: moderate

Reversibility: clean

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

Tested: bun run check:desktop

Tested: bun run check:server

Tested: git diff --check

Not-tested: Live Sub2API provider reconnect repro.
2026-05-23 19:06:59 +08:00
程序员阿江(Relakkes)
673523f3fd fix: keep IM session streams subscribed across desktop views
Telegram and desktop can attach to the same session while a turn is streaming. The WebSocket handler now tracks output callbacks per client and broadcasts session messages instead of replacing the previous subscriber. Telegram thinking deltas are accumulated before editing the placeholder so the preview does not collapse to the latest tiny chunk.

Constraint: Desktop and IM adapters may observe the same active session concurrently
Rejected: Keep a single session output callback | a later desktop view can steal Telegram's live content and completion events
Confidence: high
Scope-risk: moderate
Directive: Do not collapse session output callbacks back to one callback without a multi-client streaming regression
Tested: bun run check:server (828 pass); bun run check:adapters (358 pass); git diff --check
Not-tested: Live Telegram Bot API smoke against the packaged app
2026-05-23 16:19:39 +08:00
程序员阿江(Relakkes)
853e3db6ec fix: keep AskUserQuestion state aligned with CLI answers
The CLI transcript already records each AskUserQuestion once, but the desktop bridge dropped the structured toolUseResult answers from both history replay and live WebSocket tool results. That made answered question cards look unresolved and allowed stale cards to reappear during the gap before the next permission request.

Constraint: Desktop should render the CLI question stream directly without inventing separate question lifecycle state.
Rejected: Hide all previous AskUserQuestion cards after submit | would remove resolved answer history instead of preserving transcript context.
Confidence: high
Scope-risk: moderate
Directive: Preserve toolUseResult metadata when adapting CLI transcript entries; AskUserQuestion answer state depends on the structured answers object, not the prose tool_result string.
Tested: bun test src/server/__tests__/sessions.test.ts --timeout 30000
Tested: bun test src/server/__tests__/ws-memory-events.test.ts --timeout 30000
Tested: cd desktop && bun run test src/stores/chatStore.test.ts
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx
Tested: bun run check:server
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
Not-tested: Full bun run verify and live desktop browser smoke.
2026-05-23 15:58:15 +08:00
程序员阿江(Relakkes)
cb922b018d fix: prevent desktop compact state from sticking
Desktop compact events can arrive as a start status followed by a null
CLI status or by a summary boundary. Preserve the visible transcript while
making either completion path clear transient compacting UI so the session
cannot remain stuck after auto-compact or cancellation.

Constraint: Desktop receives compact state through translated CLI SDK status events.
Rejected: Collapse old transcript into a single compact card | hides useful history and caused issue #568.
Confidence: high
Scope-risk: moderate
Directive: Do not drop status=null compact exits without validating desktop chat state transitions.
Tested: bun run check:desktop
Tested: bun run check:server
Tested: bun run check:policy
Tested: bun run check:native
Tested: bun run check:coverage
Tested: SKIP_INSTALL=1 PRESERVE_TAURI_TARGET=1 ./desktop/scripts/build-macos-arm64.sh
Tested: Real packaged macOS app with GPT-5.5 completed two live turns without staying in running state.
Not-tested: Full live auto-compact threshold trigger in packaged app; Tauri sidecar did not inherit CLAUDE_AUTOCOMPACT_PCT_OVERRIDE during manual smoke.
Related: https://github.com/NanmiCoder/cc-haha/issues/567
Related: https://github.com/NanmiCoder/cc-haha/issues/568
2026-05-22 19:16:53 +08:00
程序员阿江(Relakkes)
e437199b81 fix: Surface API retry progress in desktop chat
CLI retry events already include retry attempts, delay, and upstream status, but
the desktop WebSocket translation dropped api_retry system messages. The desktop
chat now keeps the current retry state in session state and renders a compact
active-turn indicator with attempt count, HTTP status, and countdown.

Constraint: Desktop users need visible feedback during provider retry waits after auth or network failures.
Rejected: Add retry rows to the transcript | retry heartbeats would clutter chat history instead of describing the active turn.
Confidence: high
Scope-risk: moderate
Directive: Keep api_retry as active-turn state; do not persist retry heartbeats into transcript history without a product decision.
Tested: bun test src/server/__tests__/ws-memory-events.test.ts
Tested: cd desktop && bun run test src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:server
Tested: Browser smoke with temp CLAUDE_CONFIG_DIR and mock OpenAI-compatible 503 endpoint, screenshot /tmp/cc-haha-retry-ui.png
Not-tested: Live ChatGPT logout retry path against OpenAI production auth.
2026-05-20 18:48:36 +08:00
程序员阿江(Relakkes)
02bf65ce07 fix: Preserve ChatGPT Official after desktop restart
The WebSocket runtime treated every active provider id as stale unless it existed in the saved custom-provider list. ChatGPT Official is an in-memory built-in provider, so restarting the desktop could activate Claude Official and rewrite the provider index back to null before a new session launched. Keep built-in OpenAI provider ids valid in runtime validation, while preserving the stale custom-provider cleanup path.

This also keeps the General network timeout UI aligned with the authoritative timeout behavior by allowing precise typed values and updating the user-facing hint.

Constraint: ChatGPT Official is a built-in provider id and is not stored in the custom providers array.
Rejected: Persist ChatGPT Official as a synthetic saved provider | it would mix token-backed built-ins with user-managed providers and complicate secret persistence.
Confidence: high
Scope-risk: moderate
Directive: Runtime provider validation must include built-in provider ids as well as saved custom provider ids.
Tested: bun test src/server/__tests__/conversations.test.ts -t "preserve ChatGPT Official"
Tested: bun test src/server/__tests__/conversations.test.ts -t "stale persisted|preserve ChatGPT Official"
Tested: bun run check:server
Tested: bun run check:desktop
Not-tested: Full bun run verify after the final interruption; user explicitly asked to stop verification and commit directly.
Not-tested: Manual desktop restart with a real ChatGPT OAuth account in the packaged app.
2026-05-20 18:00:22 +08:00
程序员阿江(Relakkes)
a4aba9ec5a Show context compaction as transcript state
Desktop users need to see automatic context compaction as a transient transcript state instead of a stray stdout bubble or large card. The UI now collapses prior visible content into a compact timeline divider, shows the compacting phase, then updates the same divider with the completed summary affordance.

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

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

Confidence: high

Scope-risk: moderate

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

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

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

Tested: bun run check:desktop

Not-tested: full root bun run verify after the final UI iteration
2026-05-19 23:13:01 +08:00
程序员阿江(Relakkes)
2323e6ec47 Keep subagent progress readable in desktop sessions
Desktop sessions can receive task tools, background-agent lifecycle events, and child tool stream events in the same turn. The UI now keeps root Agent cards separate from task-management tools, preserves parent links when later stream events omit them, and surfaces completed background-agent result text from task notifications.

Constraint: Existing desktop session history can contain task-notification XML and live WS events with incomplete parent metadata.
Rejected: Render local_agent lifecycle messages as standalone cards | duplicates the Agent tool card and makes ownership unclear.
Confidence: high
Scope-risk: moderate
Directive: Do not collapse Agent and TaskCreate/TaskUpdate root tools into one generic group without checking multi-agent screenshots.
Tested: SKIP_INSTALL=1 PRESERVE_TAURI_TARGET=1 ./desktop/scripts/build-macos-arm64.sh
Tested: Real gpt-5.5 desktop E2E sessions under /tmp/cc-haha-subagent-e2e-20260519-025454 for 3 subagents, child tools, result dialog, and background Bash completion.
Not-tested: Full bun run verify gate.
2026-05-19 03:15:52 +08:00
程序员阿江(Relakkes)
3fb3d24911 Reveal background agent progress during goal runs
Desktop sessions previously only showed the final local-agent output, so long verification phases looked stalled even while the CLI was still working. The websocket bridge now forwards task start/progress events, and the desktop session state renders them as a compact background-agent panel below the active goal.

Constraint: CLI already emits task lifecycle events; desktop needed to consume the existing stream instead of polling transcript files.
Rejected: Wait for final task notifications only | that keeps the long-running phase invisible.
Confidence: high
Scope-risk: moderate
Directive: Keep task_started/task_progress available as structured desktop notifications when changing CLI event translation.
Tested: bun run verify (passed=8 failed=0 skipped=2)
Tested: bun run check:desktop
Tested: bun run check:server
Tested: bun run check:native
Tested: bun run check:coverage
Not-tested: Post-formatting check:server rerun was intentionally stopped at user request.
2026-05-16 18:21:33 +08:00
程序员阿江(Relakkes)
174c6757f2 Drop prelaunch goal transcript compatibility
The /goal feature has not shipped, so the desktop and CLI paths should only understand the current set/clear protocol instead of carrying transitional transcript formats. This removes Goal created/replaced and old status-block parsing from hydration, WebSocket forwarding, and desktop history mapping.

Constraint: Feature is still pre-release, so historical Goal created/replaced transcript compatibility is unnecessary.

Rejected: Keep old status-block hydration | it preserves a surface that users never received and makes the simplified /goal protocol harder to reason about.

Confidence: high

Scope-risk: narrow

Directive: Do not re-add legacy /goal status-block parsing unless a shipped transcript format requires it.

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

Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx src/components/chat/composerUtils.test.ts src/__tests__/pages.test.tsx
2026-05-16 12:39:27 +08:00
程序员阿江(Relakkes)
606f3c0dc2 Simplify goal command into an immediate set-clear loop
The previous /goal surface exposed management subcommands that made the CLI and desktop behavior hard to reason about. This keeps the user-facing command close to Claude Code's set/clear interaction while preserving historical transcript hydration and the existing evaluator loop.

Constraint: Desktop must see a live goal event before the model reaches the next tool call.\nRejected: Keep status/pause/resume/complete slash subcommands | requested UX is only set and clear.\nConfidence: high\nScope-risk: moderate\nDirective: Keep /goal user-facing syntax compact; extend internals without expanding the slash picker unless product explicitly asks.\nTested: bun test src/commands/goal/goal.test.tsx src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/conversations.test.ts\nTested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx src/components/chat/composerUtils.test.ts\nTested: bun run check:server\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: ./bin/claude-haha -p '/goal smoke verify immediate goal output' --output-format stream-json --include-partial-messages --max-turns 1 --no-session-persistence --verbose emitted immediate system.local_command_output before model stream\nTested: ./bin/claude-haha -p '/goal clear' --output-format stream-json --include-partial-messages --max-turns 1 --no-session-persistence --verbose exited with duration_api_ms=0 and No active goal\nNot-tested: Browser click smoke was blocked by Chrome DevTools transport closing and Computer Use cgWindowNotFound on this machine.
2026-05-16 03:28:48 +08:00
程序员阿江(Relakkes)
87caeb8cb0 Keep typed slash command events visible in desktop
Desktop muted CLI output until the user turn was fully enqueued, which dropped early local_command events for typed slash commands such as /goal. The transcript still recorded the goal, but the live desktop client missed the goal event and active-goal indicator. This forwards only the current typed command lifecycle through the pre-turn mute gate while keeping unrelated startup chatter and stale command output suppressed.

Constraint: Desktop must show /goal creation immediately while preserving the pre-turn mute gate for SDK startup noise

Rejected: Forward all pre-turn local command output | would reintroduce stale or unrelated CLI chatter into fresh desktop turns

Confidence: high

Scope-risk: narrow

Directive: Do not widen this gate without verifying /cost, /context, /goal, and reconnect startup behavior

Tested: bun test src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/conversations.test.ts -t 'local command output|/cost|/context|goal|pre-turn mute'

Tested: bun run check:server

Not-tested: live desktop manual smoke after rebuilding/restarting the app
2026-05-16 00:09:39 +08:00
程序员阿江(Relakkes)
376fc6de62 Make /goal durable across CLI and desktop sessions
The goal flow needs to drive the agent loop and remain visible after desktop reconnects, so this change treats /goal output as a structured lifecycle signal across CLI, server, and desktop history restoration.

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

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

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

Confidence: high

Scope-risk: moderate

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

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

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

Tested: bun run verify

Not-tested: Live provider baseline with a real model-driven goal loop.
2026-05-15 23:26:54 +08:00
程序员阿江(Relakkes)
059d2f0ee0 Merge desktop plugin hot refresh into main 2026-05-15 19:51:22 +08:00
程序员阿江(Relakkes)
de63d82633 fix: keep desktop plugin changes visible without restarting chats
Plugin enablement already has a live CLI reload control path, so desktop now applies plugin changes by refreshing the active session instead of waiting for a future conversation startup. The server forwards reload_plugins to the active CLI session, refreshes session slash-command cache, and notifies the client. The desktop plugin store automatically reloads after mutating plugin state, and the empty-session composer refetches skills when plugin capabilities change.

Constraint: Existing CLI exposes reload_plugins as the supported hot-refresh mechanism for commands, agents, plugins, and MCP state.

Rejected: Start a hidden replacement CLI process | higher cost, extra process lifecycle risk, and less precise than the existing control channel.

Confidence: high

Scope-risk: moderate

Directive: Keep plugin refresh routed through reload_plugins unless the CLI control contract is removed or changed.

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

Tested: cd desktop && bun run test --run src/stores/pluginStore.test.ts src/__tests__/pluginsSettings.test.tsx src/pages/EmptySession.test.tsx

Tested: bun run check:server

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run build

Not-tested: bun run check:desktop is blocked by a pre-existing color-mix compatibility failure in desktop/src/theme/globals.css.
2026-05-15 19:51:01 +08:00
程序员阿江(Relakkes)
c866adc951 Honor desktop thinking controls for provider streams
Desktop sessions must treat the global Thinking toggle as the source of truth across active CLI sessions, title generation, and provider-compatible request bodies. DeepSeek-style streams can start with reasoning blocks before text, so the WebSocket bridge now keeps the UI in thinking state until text content actually starts.

Constraint: DeepSeek and MiniMax use Anthropic-compatible surfaces but expose thinking behavior through provider-specific request and stream shapes
Rejected: Keep provider-specific disabled-thinking env defaults | stale defaults kept overriding the user's global Thinking setting
Rejected: Treat every non-tool stream block as text | reasoning-only startup blocks created a blank streaming gap before the visible thinking bubble
Confidence: high
Scope-risk: moderate
Directive: Do not change thinking stream state transitions without replaying reasoning-before-text provider events
Tested: bun test src/server/__tests__/ws-memory-events.test.ts
Tested: bun run check:server
Tested: bun run verify
Not-tested: Live DeepSeek provider smoke with real quota
2026-05-15 19:35:12 +08:00
程序员阿江(Relakkes)
30d9cc8254 Limit slash command help fix to desktop
The /goal command already exposes description and argument hints in the CLI. Revert the broader SDK, system/init, and server metadata changes so the remaining behavior change is only the desktop fallback and rendering path.

Constraint: Desktop help should not require changing core CLI or SDK wire contracts.

Rejected: Add slash_commands_metadata to system/init | unnecessary for the reported desktop-only display issue.

Confidence: high

Scope-risk: narrow

Directive: Fix missing desktop slash command descriptions in desktop fallback/rendering unless the CLI wire contract is explicitly being redesigned.

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

Tested: bun test src/commands/headless.test.ts src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts
2026-05-13 21:44:07 +08:00
程序员阿江(Relakkes)
49b7198da0 Merge slash command metadata into main 2026-05-13 21:36:11 +08:00
程序员阿江(Relakkes)
8b819bff29 Expose slash command usage metadata
Goal commands already had local CLI metadata, but desktop and SDK init consumers only received a name list. Keep the legacy slash_commands string array intact and add explicit metadata so clients can render descriptions and usage hints without breaking older readers.

Constraint: Existing SDK/system init consumers may depend on slash_commands staying string-only.

Rejected: Replace slash_commands with objects | would risk breaking older SDK and desktop consumers.

Confidence: high

Scope-risk: moderate

Directive: Keep slash_commands as the compatibility name list unless all SDK consumers have migrated.

Tested: bun test src/server/__tests__/websocket-handler.test.ts src/utils/messages/systemInit.test.ts src/commands/headless.test.ts src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts

Tested: cd desktop && bun test src/components/chat/composerUtils.test.ts

Tested: bun run check:server

Tested: bun run check:desktop
2026-05-13 21:36:01 +08:00
程序员阿江(Relakkes)
df9775a6eb Surface project memory where desktop users can act on it
The CLI already emits memory_saved events and stores Markdown memory files, but the desktop app had no usable surface for seeing or editing those writes. This adds a project memory API, a Settings memory editor, chat memory event cards, and routing from /memory or /context into the memory UI.

Constraint: Memory files live under Claude project storage and must remain plain Markdown editable by users.
Rejected: Only expose raw filesystem links | users need an in-app review and edit flow from chat.
Confidence: high
Scope-risk: moderate
Directive: Keep memory storage project-scoped and preserve unknown Markdown content when editing.
Tested: bun test src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/memory.test.ts
Tested: bun run check:server
Tested: bun run check:desktop
Tested: agent-browser E2E for chat memory card, Open Memory navigation, and responsive Markdown editor layout
Not-tested: Live model auto-memory trigger rate with real provider credentials
2026-05-13 14:07:13 +08:00
程序员阿江(Relakkes)
eea8ea5cbd fix(desktop): avoid duplicate worktree startup status
Continuing an already materialized worktree session should behave like a normal existing session. The launch path now checks whether the session has already moved into its worktree before showing the startup bubble or passing worktree creation flags again.

Constraint: Existing session metadata can retain repository.worktree after the worktree transcript becomes the active session file
Rejected: Frontend-only suppression | the server could still launch the CLI with duplicate worktree creation flags
Confidence: high
Scope-risk: narrow
Tested: bun run check:server
Tested: bun test src/server/__tests__/conversations.test.ts -t worktree --timeout 30000
Tested: bun test src/server/__tests__/sessions.test.ts -t "newest duplicate session file" --timeout 30000
Not-tested: bun run verify still fails on unrelated desktop Settings.tsx and MessageList.tsx gates from the broader worktree
2026-05-10 12:16:15 +08:00