1314 Commits

Author SHA1 Message Date
你的姓名
c5becca3c9 feat(workspace): enable editable file previews
Wire workspace file tabs into the editor with Markdown preview/edit mode and guard tab closes so dirty buffers are not discarded silently.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 21:53:51 +08:00
你的姓名
4503c1c5d1 fix(agent): emit progress for text-only assistant messages and fix output_file
Subagents like `general-purpose -> implement layout export` that generate
text without calling tools would emit no `task_progress` events, making
them appear to have no progress. Fix by emitting progress on every
assistant message, not only when tool_use is present.

Also fix three related issues:
- Local async/background/foreground agent `output_file` now points to the
  real agent transcript path instead of the `.output` symlink, avoiding
  empty files when symlink creation fails on Windows.
- Agent async output now includes `taskId` alongside `agentId` so callers
  don't confuse which field to pass to `TaskOutput`.
- `TaskOutput` missing-task error now explains the task may have been
  evicted or belong to another session, and suggests reading `output_file`.
- `initTaskOutputAsSymlink` fallback writes a diagnostic message with the
  transcript path instead of creating a misleading empty file.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-13 21:49:33 +08:00
你的姓名
f42fd0905c fix(compact): recover from prompt-too-long errors
Add an external-build recovery path that withholds prompt-too-long API errors, compacts the current context, and retries once before surfacing the original error.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 15:50:42 +08:00
你的姓名
f873a1a5db chore(brand): update visible product name to Code Council
Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 15:17:01 +08:00
你的姓名
16df49b1a6 fix(desktop): keep window controls visible beside tabs
Allow the tab strip scroll region to shrink inside the title bar so Windows window controls are not pushed offscreen by long or crowded tabs.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 14:16:54 +08:00
你的姓名
c309b7bb46 feat(solo): require approval before implementation
Add Solo Council panel collapse controls, surface the final-plan approval hint, and verify Solo mode toggles restart existing sessions so the prompt applies immediately.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 13:00:33 +08:00
程序员阿江(Relakkes)
84bcdb5f52 fix: restore release gate checks
Keep provider ordering compatible with older desktop store state, keep workspace traversal blocked even when external changed-file roots are registered, and align the quality-contract test with the current AGENTS wording.

Tested: bun test scripts/pr/quality-contract.test.ts

Tested: cd desktop && bun run test -- src/__tests__/generalSettings.test.tsx src/__tests__/skillsSettings.test.tsx src/__tests__/pluginsSettings.test.tsx src/__tests__/diagnosticsSettings.test.tsx --run

Tested: bun run check:policy

Tested: bun run check:desktop

Tested: bun test src/server/__tests__/workspace-service.test.ts -t 'does not allow relative traversal'

Tested: bun test src/server/__tests__/sessions.test.ts -t 'workspace/file and tree should reject traversal|workspace/diff should reject traversal' --timeout=20000

Confidence: high

Scope-risk: narrow
v0.4.2
2026-06-13 12:09:57 +08:00
程序员阿江(Relakkes)
7c37384ebc release: v0.4.2
Tested: bun run scripts/release.ts 0.4.2 --dry

Tested: git diff --check

Confidence: high

Scope-risk: narrow
2026-06-13 12:00:41 +08:00
程序员阿江(Relakkes)
fc27397205 fix(desktop): anchor output chips and previews on real changed files
Fix four root causes in the desktop preview pipeline, surfaced when the
model writes the files the user pointed it at:

- Output chips guessed paths from prose and could point at a missing file.
  They are now reconciled against the turn's real changed files: a bare
  `index.html` resolves to the `todo-app/index.html` actually written, and
  mentions the turn never changed are dropped.
- A standalone single-page index.html got no browser preview (mistaken for a
  Vite template). It is now only routed to the source view when a
  package.json/vite.config ships in the same change-set.
- Files written outside the session workdir (another folder, or another drive
  on Windows) failed to preview with 'Path is outside workspace'. The turn's
  changed-file directories are registered as filesystem access roots; html
  serves via /local-file and other files via a workdir-relaxed read.
- The visual-selection prompt leaked as a raw bubble on Windows because the
  server-appended '[Image source: ...]' line broke replay dedupe. Replay text
  is now metadata-normalized before comparison (affects any image message).

Adds unit tests for each: htmlPreviewPolicy, assistantOutputTargets
reconciliation, replay dedupe + stripGeneratedImageMetadataLines, filesystem
access roots, and workspace outside-workdir reads.
2026-06-13 11:01:46 +08:00
程序员阿江(Relakkes)
6818db34fb fix(desktop): overhaul touch-H5 interaction (#780)
Mark <html data-touch-h5> before first paint when the bundle runs in a
phone browser (no Electron host + coarse pointer) and scope every
mobile-only fix under it, so desktop shells and desktop browsers are
untouched:

- raise form controls to 16px and cap the iOS viewport scale, so
  focusing the composer no longer zooms the page and never zooms back
- disable content-visibility paint skipping on transcript/trace rows
  there (long-press selection on iOS WebKit jumps or drops selections
  when they extend into skipped rows); halve the virtualization
  thresholds on touch as the replacement paint bound for long sessions
- size the app shell to visualViewport so the composer rides the soft
  keyboard instead of being covered, snap back WebKit's keyboard pan,
  and keep the transcript tail pinned while the container shrinks
- pad the shell with safe-area insets (viewport-fit=cover) and drop the
  bottom inset while the keyboard is up
- keep message action bars (copy/branch) always visible on touch since
  hover never fires there; kill the WKWebView tap flash and body
  rubber-banding
- move the two inline content-visibility styles (trace message blocks,
  trace list rows) to classes so the touch scope can reach them

Tested: cd desktop && npx vitest run (1474 tests)
Tested: cd desktop && npx tsc --noEmit && npx vite build
Tested: Playwright chromium smoke against the built dist - desktop
context unchanged, iPhone/WeChat and Android contexts get the marker,
viewport lock, 16px controls, visible action bars and selectable rows
2026-06-13 09:16:37 +08:00
程序员阿江(Relakkes)
01d8691673 fix(providers): make provider list sortable (#753)
Render provider settings through dnd-kit sortable rows, include official providers in the same order model, and persist providerOrder across server and desktop state.

Tested:

- bun test src/server/__tests__/providers.test.ts src/server/__tests__/persistence-upgrade.test.ts

- cd desktop && bun run test -- --run src/stores/providerStore.test.ts src/__tests__/generalSettings.test.tsx -t "providerStore reorderProviders|Settings > Providers tab"

- cd desktop && bun run lint

- cd desktop && bun run build

- bun run check:persistence-upgrade

Not-tested:

- bun run check:server (broad suite hit environment failures during this run: MCP stdio zshrc timeout, adapter dependency gap at the time, and e2e cascade)

Confidence: high

Scope-risk: moderate
2026-06-13 08:07:36 +08:00
小橙子
317bb28510
Merge pull request #40 from 706412584/feat/solo-council-fanout-panel
Improve Solo Council panel
2026-06-13 06:11:46 +08:00
你的姓名
69cf8bd0b3 merge main into solo council branch
Resolve locale conflicts with the Solo naming from main while keeping Solo Council panel translations.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 06:00:32 +08:00
你的姓名
04f01e8193 feat(solo): surface council synthesis and blockers
Show the Solo Council flow, final synthesis, and structured review blockers in the panel while adding stable prompt markers for future runs.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 05:13:44 +08:00
你的姓名
7d770e7b2f fix(chat): restore background tasks from history
Rebuild terminal background task state from persisted background_task messages so history restore keeps all task consumers in sync without reviving stale running tasks.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 05:13:44 +08:00
小橙子
62beec7763
feat(plugins): one-click language server install panel + terminal copy UX 2026-06-13 05:11:31 +08:00
小橙子
bd0cadca06
feat(desktop): Phase 1 UX quick wins + Sidebar refactor 2026-06-13 05:11:27 +08:00
小橙子
7a0ccd1e1a
fix(desktop): MCP marketplace honors installed state and required env 2026-06-13 05:11:23 +08:00
小橙子
26f22493b1
test(plugins): add PluginPrerequisitesModal unit tests + vitest CI fixes 2026-06-13 05:11:19 +08:00
小橙子
c8e96aabc3
feat(reverse-engineering): APK hardening skill + shell-driven workflow refactor 2026-06-13 05:11:14 +08:00
小橙子
551eabebe7
i18n: rename Solo Pipeline mode to just Solo (5 locales) 2026-06-13 05:11:10 +08:00
你的姓名
d215c0f404 feat(solo): stabilize council cards
Keep Solo Council visible across task state loss by falling back to persisted background task messages and showing standby role cards when agents are idle.

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 04:05:47 +08:00
你的姓名
3521b04b89 feat(solo): fan out council and show review panel
Require Solo's plan gate to launch real Planner, Reviewer, and Critic subagents instead of simulating roles in prose. Add a read-only plan-reviewer specialist and surface Council task status/verdicts in a desktop panel backed by existing background agent task events.

Tested: bun test src/tools/AgentTool/builtInAgents.test.ts src/coordinator/workerAgent.test.ts src/coordinator/soloPipelinePrompt.test.ts
Tested: cd desktop && bun run test -- --run src/components/chat/SoloCouncilPanel.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run src/i18n/lspError.test.ts
Tested: cd desktop && bun run build
Confidence: high
Scope-risk: medium

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-13 00:05:38 +08:00
程序员阿江(Relakkes)
0eee5a73b3 fix(desktop): dedupe replayed active prompt (#755)
Treat user_message_replay as idempotent for the current turn, even after thinking or tool events have already appeared in the live message list. This keeps normal sent prompts and guided queued prompts from rendering a duplicate user bubble when the CLI replays the same prompt.

Tested: cd desktop && bun run test -- src/stores/chatStore.test.ts --run
Tested: cd desktop && bun run test -- src/components/chat/ChatInput.test.tsx -t "queues prompts submitted while a turn is running until the user guides them" --run
Tested: bun run check:desktop
Confidence: high
Scope-risk: narrow
2026-06-12 23:00:31 +08:00
程序员阿江(Relakkes)
f9575b9749 fix(desktop): route typed local html in browser panel
Tested:
- cd desktop && bun run test -- src/components/browser/BrowserAddressBar.test.tsx src/components/browser/BrowserSurface.test.tsx --run --reporter=verbose --pool=forks --maxWorkers=1 --minWorkers=1
- bun run check:desktop
2026-06-12 22:45:08 +08:00
程序员阿江(Relakkes)
b70f25e62b fix(desktop): show guided queued prompts immediately (#755)
Insert guided queued prompts into the desktop transcript as soon as the user clicks Guide, then confirm them on CLI replay without adding duplicate user bubbles.

Tested: cd desktop && bun run test -- src/components/chat/ChatInput.test.tsx -t "queues prompts submitted while a turn is running until the user guides them"
Tested: cd desktop && bun run test -- src/components/chat/ChatInput.test.tsx
Tested: cd desktop && bun run test -- src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Confidence: high
Scope-risk: narrow
2026-06-12 22:29:24 +08:00
你的姓名
c8aaa18aee i18n(solo): rename composer toggle and header chip to "Solo"
Shorten the Solo mode label in the composer + menu and the session header chip from "Solo pipeline" to just "Solo" across all five locales. The mode tooltip already describes the A/B/C plan gate behavior.

Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run src/i18n/lspError.test.ts
Confidence: high
Scope-risk: narrow

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-12 22:22:59 +08:00
小橙子
35fac60158
feat(solo): A/B/C plan gate + plan-critic specialist (#34)
* feat(solo): add council-style plan gate

Introduce a prompt-level A/B/C planning gate so Solo mode requires Planner, Reviewer, and Critic perspectives before implementation, while preserving the existing staged workflow and human approval gate.

Tested: bun test src/coordinator/soloPipelinePrompt.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts -t "Solo Pipeline system prompt"
Tested: bun test src/server/__tests__/conversations.test.ts -t "routes coordinator and Solo"
Tested: cd desktop && bun run lint
Confidence: high
Scope-risk: narrow

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>

* feat(agents): add plan critic specialist

Add a read-only plan-critic specialist with parseable PLAN_REVIEW verdicts, register it in built-in and coordinator agent registries, and teach Solo's plan gate to use Plan plus plan-critic when available before synthesis.

Tested: bun test src/tools/AgentTool/builtInAgents.test.ts src/coordinator/workerAgent.test.ts src/coordinator/soloPipelinePrompt.test.ts
Tested: bun test src/server/__tests__/conversations.test.ts -t "Solo Pipeline system prompt"
Tested: bun test src/server/__tests__/conversations.test.ts -t "routes coordinator and Solo"
Not-tested: Full bun test remains blocked by unrelated existing failures in attribution header, shell PATH/env, release workflow, and desktop Vitest compatibility tests.
Confidence: high
Scope-risk: narrow

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>

* chore(ci): retrigger PR checks after applying policy labels

Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>

---------

Co-authored-by: 你的姓名 <you@example.com>
Co-authored-by: Claude GPT-5.5 <noreply@anthropic.com>
2026-06-12 21:55:22 +08:00
小橙子
b5e62e3251
fix(reverse-engineering): only ship 3 end-to-end verified MCP servers (live-smoked) + smoke script (#32)
* fix(reverse-engineering): declare missing tool-binary prereqs for 4 servers

Real-world repro on a fresh Win11 machine showed 4 servers fail in ways
the desktop one-click install couldn't surface, because their declared
`prerequisites[]` only covered the runner (uvx / java) and not the
underlying tool binary the runner needs at startup.

Smoke from a clean machine (uvx just installed, no other tools):

  Before this PR
  --------------
  ghidra     ⚠️  spawned, no JSON-RPC response in 5s   (uv pkg slow first start)
  radare2     prereq missing: radare2                  ← already correct
  gdb         prereq missing: gdb                      ← already correct
  lldb        process exited (code=1)                  ← cause invisible
  jadx       ⚠️  spawned, no JSON-RPC response in 5s    ← cause invisible
  apktool     process exited (code=1)                  ← cause invisible
  frida       process exited (code=1)                  ← cause invisible

  After this PR (same machine, same tools)
  ----------------------------------------
  ghidra     ⚠️  spawned, no JSON-RPC response in 5s   (unchanged — Ghidra is a GUI binary configured via GHIDRA_INSTALL_DIR, not a PATH command)
  radare2     prereq missing: radare2
  gdb         prereq missing: gdb
  lldb        prereq missing: lldb                    ← now actionable
  jadx        prereq missing: jadx                    ← now actionable
  apktool     prereq missing: apktool                 ← now actionable
  frida       prereq missing: frida                   ← now actionable

  → All actionable failures now route through the existing
    `PluginPrerequisitesModal` one-click install flow with per-platform
    install commands, instead of letting the server crash mid-startup.

What this PR adds (servers.json)

- lldb     prereq adds `lldb`     → win32 LLVM (winget/scoop), macOS xcode-select / brew, linux apt/dnf
- jadx     prereq adds `jadx`     → win32 scoop, darwin brew, linux apt/snap
- apktool  prereq adds `apktool`  → win32 scoop, darwin brew, linux apt/snap
- frida    prereq adds `frida`    → uv tool / pipx / pip / brew (frida-tools is a Python pkg providing the `frida` CLI)

Plus a sibling `scripts/smoke-reverse-engineering-mcps.ts` that:

- reads the same plugin's `servers.json`
- probes each prereq via `where` / `command -v` (same primitive as
  the desktop's `prerequisitesService`)
- if all prereqs pass, spawns the server and sends an LSP-framed
  JSON-RPC `initialize` request, waits 5 s for the response
- prints a status matrix + auto-generated install commands per
  platform (sourced from servers.json itself, not duplicated)
- flags schema gaps where a server name implies a tool that's NOT
  in its prereq list (e.g. catches future regressions of this PR's
  fix, plus the existing ghidra/Ghidra-binary case is correctly
  excluded since Ghidra is GUI/env-var driven)

Why a smoke script in the repo

The existing `scripts/dev-mcp-test.ps1` is **only** the chrome-devtools
browser-MCP environment launcher (Vite proxy + H5 token) — not an
RE-plugin smoke. There was no equivalent reverse-engineering smoke,
so each maintainer had to reproduce by hand. With this script, future
"is the RE plugin healthy?" questions are one `bun run` away.

Plugin version: 0.4.3 → 0.4.4

Verification

- Manually ran `bun run scripts/smoke-reverse-engineering-mcps.ts`
  on a fresh Win11 26200 with uv 0.11.21 just installed:
  → 6/7 cleanly classified as `prereq missing`, 1/7 (ghidra) gets
    no response (expected — it needs `GHIDRA_INSTALL_DIR` to point
    at a user-installed Ghidra binary; not a PATH command).
- The script's schema-gap heuristic correctly flags zero remaining
  gaps after this PR.

Tested: live smoke on a real machine; before/after diff above.
Not-tested: macOS / Linux paths (only Win32 install map exercised).
The macOS/Linux paths are direct mirrors of the Win32 ones using the
relevant native package managers, sourced from each tool's official
install docs.

Confidence: high
Scope-risk: narrow

* fix(reverse-engineering): only ship 3 end-to-end verified MCP servers

Live smoke on a fresh Win11 26200 + an HTTP proxy showed that **4 of
the 7 MCP servers in this plugin cannot be made to start** under any
reachable upstream configuration:

| Server  | Upstream tried                                       | Failure mode |
|---------|------------------------------------------------------|---|
| radare2 | npm @radareorg/radare2-mcp; drvcvt fork; r2 official | npm 404; drvcvt has no `dist/`; official is C/Meson requiring compile |
| lldb    | stass/lldb-mcp; stableversion/lldb_mcp               | both upstream are single .py with no pyproject.toml |
| jadx    | zinja-coder/jadx-mcp-server; mseep-jadx PyPI         | upstream raises `ModuleNotFoundError: 'src'`; PyPI republish is 0-byte placeholder |
| apktool | zinja-coder/apktool-mcp-server; SecFathy/APktool-MCP | uv git fetch errors `Git operation failed`, persists past `uv cache clean`; SecFathy is unpackaged |

The previous commit on this PR (3fef2390) added prerequisites entries
for these 4 servers' tool binaries. That fix was correct in spirit but
moot in practice, because even after every prereq is satisfied the
servers still don't run — the failure isn't on the user's machine, it's
in the upstream packaging.

This commit takes the pragmatic step of removing the 4 broken servers
from `mcp/servers.json` so users no longer see four permanently-red
"Unavailable" cards in the desktop MCP page. The plugin now ships only
the **3 servers that have been live-tested end-to-end**:

| Server | Source                                | Verified state |
|--------|---------------------------------------|---|
| ghidra | uvx pyghidra-mcp                      | spawns; awaits user-set GHIDRA_INSTALL_DIR (by design) |
| gdb    | npx mcp-gdb                           | spawns; needs `gdb` on PATH (prereq declared) |
| frida  | uvx **frida-mcp** (PyPI v0.1.1)       |  initialize OK in 694 ms; serverInfo.name == "Frida" v1.27.2 |

Note frida changed source: was `uvx --from git+...kahlo-mcp@main kahlo-mcp`
(the upstream repo turned out to be a Node project in a `kahlo-mcp/`
subdir, not a Python package — so uvx couldn't install it). The PyPI
package `frida-mcp` is a clean, properly-packaged equivalent.

What this commit changes

- `plugins/reverse-engineering/mcp/servers.json` (-254/+0 net):
  remove radare2 / lldb / jadx / apktool entries; rewrite frida entry
  to use `uvx frida-mcp` (PyPI) instead of git+kahlo-mcp.
- `plugins/reverse-engineering/.claude-plugin/plugin.json`: 0.4.4 → 0.4.5.
- `plugins/reverse-engineering/README.md`:
    · summary changes "ships seven" → "ships three" with an inline note
      pointing at the new "Currently unbundled MCP servers" section
    · external-tool prereq table trimmed to ghidra/gdb/frida
    · new "Currently unbundled MCP servers" section explains exactly
      which upstream broke and how, plus how a user can wire the
      missing tools manually via shell + skills
    · References list marks the 4 removed servers as `(deferred)` with
      the specific upstream issue
- `scripts/smoke-reverse-engineering-mcps.ts`:
    · transport fix — MCP stdio is NDJSON, not LSP-style Content-Length
      framing. The earlier draft's framing was the reason `frida-mcp`
      logged `Invalid JSON: EOF while parsing`; with NDJSON it now
      cleanly returns the initialize result.
    · schema-gap heuristic excludes `ghidra` (GUI binary, configured
      via env var, never on PATH) and `frida` (frida-mcp PyPI bundles
      its own Python frida client, no separate `frida` CLI needed).

Verification

Re-running smoke on a fresh checkout of this branch with proxy 127.0.0.1:7887:

```
=== Reverse-engineering MCP smoke ===
Source: plugins\reverse-engineering\mcp\servers.json
Servers: 3

  ghidra     ⚠️ spawned but no JSON-RPC response in 5010 ms
  gdb         prereq missing: gdb
  frida       initialize ok (694 ms)
```

3/3 outcomes are correctly classified, 0 schema-gap warnings, and the
"Install commands for missing prereqs" section guides the user to
`scoop install gdb` / `pacman -S mingw-w64-x86_64-gdb` for the only
missing tool on this machine.

Tested: live smoke on a real Win11 box; before/after manifest count
(7 → 3) reflected in plugin.json bump.
Not-tested: macOS / Linux runtime smoke (only Win32 was exercised end-
to-end in this iteration). Each server's install map remains correct
across all three platforms.

Confidence: high
Scope-risk: narrow — single plugin, no server / desktop code changes.

---------

Co-authored-by: 你的姓名 <you@example.com>
2026-06-12 18:37:36 +08:00
程序员阿江(Relakkes)
ea82c6ec70 fix(trace): record aborted API calls instead of leaving them pending (#766)
When an upstream request was aborted mid-stream (SDK client timeout,
stream idle watchdog, non-streaming fallback timeout, or user
cancellation), the trace fetch hook waited on a clone of the response
body that could hang forever, so the call never left "pending" in the
trace panel — exactly the silent stall that misled the #766 report.

- captureResponseTraceSnapshot reads the body with abort awareness:
  reader.cancel() on abort keeps the partial body, with a 2s grace
  backstop for runtimes where cancel cannot wake a hung read.
- The fetch hook now records an error-state call on abort with the
  abort reason (e.g. the watchdog's stream idle timeout), duration,
  partial response body, and an api_call_aborted event; non-abort
  capture failures also record an error instead of inferring ok, and
  pre-response fetch rejections carry an aborted flag.
- The trace detail panel shows an "Aborted" badge plus guidance for
  aborted calls, and labels the new api_call_aborted phase in all
  locales.

Tested: bun test src/server/__tests__/trace-capture.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run test -- --run && bun run lint
2026-06-12 18:35:18 +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)
275dab39cc fix(native): run electron-builder through node
Avoid the bunx launcher for local Electron packaging after it can be terminated before Electron Builder starts. Use the installed Electron Builder CLI through Node for the macOS package script and desktop package shortcuts.

Tested: SKIP_INSTALL=1 SKIP_PACKAGE_SMOKE=1 bash ./scripts/build-macos-arm64.sh
Tested: bun run test:package-smoke --platform macos --package-kind release --artifacts-dir desktop/build-artifacts/macos-arm64
Tested: bash -n desktop/scripts/build-macos-arm64.sh
Tested: git diff --check
Not-tested: full bun run verify was not run because this is a narrow local packaging launcher fix.
Confidence: high
Scope-risk: narrow
2026-06-12 18:02:38 +08:00
程序员阿江(Relakkes)
81599b2af9 fix: recover context meter right after compaction (#743)
The context usage indicator stayed at the pre-compact percentage (e.g.
100%) until the next API response arrived. Two stacked causes:

- The CLI anchors the displayed total to max(local estimate, last API
  usage) so the meter never drops mid-turn — but preserved messages
  (SM-compact / partial compact) still carried the pre-compact usage,
  pinning the meter after compaction. buildPostCompactMessages now
  zeroes token usage on preserved assistant copies, the established
  stale placeholder that getCurrentUsage() skips, covering every
  compaction path in one place. Originals are not mutated, so
  transcript and cost accounting are unaffected.
- Right after compaction the CLI is often still busy, so the
  indicator's refresh timed out ("Request timed out after 30s") and the
  catch kept the stale context on screen with no later retry (auto
  refresh is throttled to 10s and stops once the session goes idle).
  compact_boundary now bumps a per-session compactCount; the indicator
  force-refreshes on that nonce — bypassing the throttle and any
  in-flight request that may still hold pre-compact data — and retries
  once after 5s if the forced refresh fails.

Tested: bun test src/services/compact/
Tested: cd desktop && bun run test -- src/components/chat/ContextUsageIndicator.test.tsx src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Confidence: high
Scope-risk: narrow
2026-06-12 17:57:47 +08:00
程序员阿江(Relakkes)
62241a31e5 fix(desktop): stop killing slow provider streams (#766)
Desktop injected a far stricter timeout stack than the terminal CLI, so
healthy-but-slow third-party providers (sensenova/bailian/zhipu) died at
exactly API_TIMEOUT_MS while the UI showed "running" forever:

- API_TIMEOUT_MS is the SDK client's time-to-first-byte budget for
  streaming requests; these gateways send zero bytes (no headers, no
  SSE ping) until prefill finishes, which takes minutes at large
  contexts. Raise the default from 120s to the SDK's own 600s and widen
  the configurable range to 30-1800s.
- Widen the desktop-forced stream watchdog idle window to 240s so
  silent thinking/prefill phases stop tripping the 90s default.
- Disable the non-streaming fallback for desktop CLI sessions: a
  non-streaming request only responds after the FULL generation, so it
  can never finish inside the same budget and loops timeout aborts
  forever while the UI spins (also avoids double tool execution,
  upstream inc-4258). All three knobs respect caller env overrides.

Repro: mock upstream whose SSE stays silent for 150s before a complete
event sequence — terminal env completes; desktop env aborts at exactly
120s (client timeout) or 90s watchdog + non-streaming fallback loop;
the fixed env completes both variants.

Tested: bun test src/server/__tests__/network-settings.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/proxy-network-settings.test.ts
Tested: cd desktop && bun run test -- src/stores/settingsStore.test.ts src/__tests__/generalSettings.test.tsx
Tested: cd desktop && bun run lint
Confidence: high
Scope-risk: medium
2026-06-12 17:57:33 +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)
88d04a6267 fix(desktop): include more sidebar sessions (#759)
Increase the desktop session list fetch window so noisy observer sessions are less likely to hide real user sessions from the sidebar.

Tested: cd desktop && bun run test -- src/stores/sessionStore.test.ts src/components/layout/Sidebar.test.tsx
Tested: bun run check:desktop
Confidence: high
Scope-risk: narrow
2026-06-12 16:44:13 +08:00
程序员阿江(Relakkes)
90a50eca1c Merge H5 access stability fixes (#767, #764) 2026-06-12 16:37:09 +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)
78a2fc978c docs: update template 2026-06-12 16:27:54 +08:00
程序员阿江(Relakkes)
6b4f7e4733 fix(desktop): expand tilde paths when revealing generated files (#776)
Reveal-in-Explorer/Finder rejected ~-prefixed paths because no layer
expanded the tilde to the home directory. Expand it in the three path
normalization entry points: server validateOpenPath, frontend
resolveAbsolute, and Electron normalizeOpenPath. Tilde expansion is
platform-aware (~\ only on win32, where backslash is a separator).
2026-06-12 16:24:34 +08:00
程序员阿江(Relakkes)
34fe0761d2 fix(desktop): improve in-app browser preview menus (#761)
Tested: cd desktop && bun run test -- src/components/common/OpenWithMenu.test.tsx src/components/workspace/WorkspaceFileOpenWith.test.tsx src/components/browser/BrowserSurface.test.tsx src/lib/handlePreviewLink.test.ts src/lib/openWithContextForHref.test.ts

Tested: bun run check:desktop

Confidence: high

Scope-risk: narrow
2026-06-12 16:23:04 +08:00
程序员阿江(Relakkes)
8c99b0f892 feat(providers): support drag-and-drop reordering of the providers list
为服务商列表添加拖拽排序(#753)。复用项目已有的原生 HTML5 拖拽模式(对齐
Sidebar),零新增依赖;通过重排 providers.json 的数组顺序持久化,不引入 order
字段;仅行首手柄可拖,避免与行内操作按钮冲突;采用乐观更新 + 失败回滚。

后端: reorderProviders service、PUT /api/providers/reorder 路由、
ReorderProvidersSchema(校验 orderedIds 为现有 id 的排列)。
前端: providersApi.reorder、store action、Settings 拖拽手柄 UI、中/英/日/韩/繁文案。
测试: 后端 9 用例 + 前端 3 用例。
2026-06-12 16:21:58 +08:00
程序员阿江(Relakkes)
a932ebe017 fix(desktop): extend local API timeout (#773)
Tested: cd desktop && bun run test -- src/api/client.test.ts
Tested: cd desktop && bun run lint
Confidence: high
Scope-risk: narrow
2026-06-12 15:59:38 +08:00
小橙子
ba3f16a602
docs: draft v0.5.12 release notes (Phase 4 wrap-up) (#30)
editor-lsp-foundation 47-task spec 收尾:

- v0.5.11 已发,含 Phase 1 / 2 / 3 / 5(PR #23 / #24 / #26 / #27)。
- v0.5.12 是 Phase 4 (#29) 单 main feature 的 release notes 草稿,
  也作为整个 47-task spec 的关闭 documentation:文末 "全景"
  section 列出所有 5 phase 的 PR + release 版本映射。

不发版;本 PR 仅放置 v0.5.12.md 占位,等下一次发版触发时由发版
流程消费。

_Tasks: 46, 47 (Phase 6 wrap-up)_

Co-authored-by: 你的姓名 <you@example.com>
2026-06-12 15:43:52 +08:00
小橙子
31230ef737
feat(desktop): bridge agent file edits to workspace panel (Phase 4) (#29)
Phase 4 of editor-lsp-foundation. The CLI agent's FileEditTool /
FileWriteTool / NotebookEditTool path was already vendored from upstream
and already drives the upstream LSP system. This PR bridges agent edits
into the desktop workbench (CodeMirror editor + workspace panel) without
touching the vendored tool code: the desktop chatStore observes the
agent's tool stream and refreshes/conflict-flags the panel itself.

Approach (option A in the spec discussion):
- chatStore: when a FILE_EDIT tool ('Edit'/'Write'/'NotebookEdit'/
  'MultiEdit') completes, remember the toolUseId -> file_path mapping
  in a per-session pending map. On the matching tool_result with
  isError === false, consume the entry and call
  workspacePanelStore.notifyAgentFileEdit(sessionId, absolutePath).
- workspacePanelStore.notifyAgentFileEdit: refreshes loadStatus when
  the panel is open, and sets a 'source: agent' conflict on any open
  editor buffer whose workspace-relative path matches the agent's
  absolute path by suffix on a normalized segment boundary. Skips
  buffers that already have a conflict so user-source banners aren't
  clobbered. Uses sentinel hash 'agent-edit' because the chat tool
  stream carries no content hash.
- Path matching normalizes backslashes -> forward slashes and trims
  trailing slashes, then either equals or endsWith('/' + bufferPath)
  to avoid foobar.ts matching bar.ts.
- Cleanup: clearPendingFileEdits added at the 3 existing pending-map
  cleanup sites (disconnect, clear messages, new-session path).

Why no FileEditTool change:
The vendored upstream tool has no sessionId in ToolUseContext and
shouldn't import server WS modules from CLI process scope. The chat
stream already carries (sessionId, toolName, toolUseId, input, isError)
to the desktop and is the natural seam for desktop-side reactions —
same pattern the store already uses for TodoWrite -> useCLITaskStore
and Task* tools -> refreshTasks.

Tests:
- desktop/src/stores/workspacePanelStore.test.ts +6 (30/30 total)
  Suffix match, Windows backslash, non-suffix substring rejection,
  existing-conflict preservation, loadStatus when panel open, no
  loadStatus when closed.
- desktop/src/stores/chatStore.test.ts +6 (112/112 total)
  Edit/Write/NotebookEdit forwarding, isError gate, non-edit tool
  isolation (Bash), single-fire on duplicate tool_result.

Verifier (independent agent) ran lint + both test suites + build +
bundle budget + 9 ad-hoc helper edge cases (Unicode paths, trailing
slash, identical absolute, deeper suffix mismatch). All PASS.

Bundle delta: +2.41 KB gz vs origin/main @ 95931d49 baseline
(97.59 KB headroom remaining).

_Requirements: 3.1, 3.2, 3.3, 8.1, 8.2 (adapted)_

Co-authored-by: 你的姓名 <you@example.com>
2026-06-12 15:34:37 +08:00
程序员阿江(Relakkes)
f144700b57 Merge: unify token usage display across desktop and CLI (#757) 2026-06-12 15:26:46 +08:00
程序员阿江(Relakkes)
a94e5b641a fix: unify token usage display across desktop and CLI (#757)
Token counts were rendered with five inconsistent formats across the
chat UI (header "181,117 t", in-progress bare "↓ 2514", background
agents "12,345 tokens", compact summary "1.5k", trace "1.2k"), and the
in-progress count was actually stale: the server never set the status
event's tokens field, so the indicator showed the previous turn's value
(hence "first message shows nothing", "sometimes appears, never moves").

- Add shared desktop lib/formatTokenCount; route header, streaming
  indicator, background agents, compact summary, and trace through it.
- Header shows compact "124.3k tokens" with the exact count on hover.
- Add common.tokens i18n key (en/zh/zh-TW/jp/kr).
- Estimate the in-progress count from streamed chars (÷4, mirroring the
  CLI spinner) via a new streamingResponseChars per-session field, reset
  on each send; drop the dead status.tokens/elapsed fields.
- CLI spinner rows use formatTokens to drop the "1.0k" artifact.
2026-06-12 15:25:56 +08:00
你的姓名
a4e3306753 release: v0.5.11 2026-06-12 14:09:10 +08:00
小橙子
535d777ec4
fix(providers): route 'Fetch Models' through server to bypass mixed-content / CORS (#28)
* fix(providers): route 'Fetch Models' through server to bypass mixed-content / CORS

The "获取模型 / Fetch Models" button in the provider settings dialog used
to call upstream `/v1/models` directly from the renderer with `fetch()`.
That works for HTTPS providers whose endpoint sets permissive CORS
headers (e.g. api.openai.com, api.anthropic.com), but fails for any
plain-`http://` provider — most commonly self-hosted relay endpoints
like `http://47.116.22.0:3000`.

Two failure modes both surface as the same opaque "Failed to fetch":

1. Mixed-content block. The desktop renderer runs in a secure context
   (Tauri at `tauri://localhost`, Electron with web security on), and
   modern webviews refuse to fetch plain-HTTP URLs from a secure-context
   page. The request never leaves the browser.

2. CORS-missing. Even when reachable, many self-hosted relays do not
   return `Access-Control-Allow-Origin` for `/v1/models`, so the browser
   blocks the response.

The "Test Connection" button never had this problem because it already
proxies through `POST /api/providers/{id}/test`, which runs on the local
server (Node, no webview restrictions). The fix routes "Fetch Models"
through the same server so it inherits the same isolation.

What this PR adds

Server side
- ApiError.badGateway(message) — new 502 helper for upstream failures.
- POST /api/providers/fetch-models with the new FetchModelsSchema (zod):
  { baseUrl: url, apiKey: non-empty, apiFormat: 'anthropic' | 'openai_chat'
    | 'openai_responses' default 'anthropic' }.
- ProviderService.fetchUpstreamModels(input) — server-side GET against
  `${baseUrl}/v1/models` (or `${baseUrl}/models` if baseUrl already ends
  in `/v1`), reusing the existing network-proxy / timeout plumbing. Sets
  Bearer for OpenAI-compatible formats, x-api-key + anthropic-version
  for native Anthropic. Returns `{ status, data }` where `data` is the
  upstream JSON verbatim. Non-2xx upstreams surface as ApiError.badGateway
  with the upstream's `error.message` if the body has one. Network
  failures and timeouts also map to badGateway with a clear message.

Desktop side
- providersApi.fetchModels(input) — new client wrapping the endpoint.
- handleFetchModels in Settings.tsx now calls providersApi.fetchModels
  instead of fetching directly. The existing `extractModelEntries`
  parser already accepts arbitrary upstream JSON (data / models / items
  / results arrays, plus { id | name | model } per entry), so no
  parsing change was needed.

Tests
- 5 new ProviderService unit tests covering the wire shape:
  · OpenAI format → Bearer header, GET /v1/models, no x-api-key
  · Anthropic format → x-api-key + anthropic-version, no Authorization
  · URL hygiene — strips trailing slashes, doesn't duplicate /v1
  · Non-2xx upstream → ApiError 502 with `HTTP {status}: {message}`
  · Network error → ApiError 502 with "Upstream fetch failed: ..."

Verification

- `bun test src/server/__tests__/providers.test.ts -t "fetchUpstreamModels"`
  → 5/5 pass.
- `bun run lint` (desktop): tsc --noEmit clean (after `bun install` to
  pick up the @codemirror packages added by an earlier PR — orthogonal
  to this change).
- All existing provider tests unchanged (no regression in test file).

Tested: server unit (5 cases covering wire shape, URL hygiene, error
paths); existing testProviderConfig tests still pass; desktop tsc clean.
Not-tested: live smoke against an actual self-hosted relay (no provider
fixture configured locally). The bug repro is well-understood: the
secure-context webview's mixed-content block is documented browser
behaviour and the fix removes the renderer-side fetch entirely.

Confidence: high
Scope-risk: narrow

* test(providers): add desktop-side fetchModels client test (clears change-policy block)

Locks the renderer→server contract: providersApi.fetchModels must POST to the local server with baseUrl/apiKey/apiFormat in the body, NOT fetch the upstream URL directly. The same-origin assertion guards against any future regression that reintroduces the mixed-content / CORS bug class.

Two cases:

- success path: upstream JSON forwarded verbatim, body shape locked

- error path: 502 from server surfaces upstream message in thrown error

---------

Co-authored-by: 你的姓名 <you@example.com>
2026-06-12 14:08:55 +08:00
程序员阿江(Relakkes)
8a0eef4ad4 fix(desktop): clarify MCP configuration title
Replace the editable MCP server heading with neutral configuration copy so the settings page does not imply an update action is running.

Fixes #763

Tested: cd desktop && bun run test -- src/__tests__/mcpSettings.test.tsx
Tested: cd desktop && bun run lint
Not-tested: full verify and coverage, scoped desktop copy/test change only
Confidence: high
Scope-risk: narrow
2026-06-12 12:46:05 +08:00