Add non-null assertions and type casts for mockSaveOptions.mock.calls
access to satisfy strict undefined checks.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- MCP server tests (21 cases): initialize, tools/list, list_providers
with capabilities, generate_image validation, edit_image capability
check, SSRF protection (IPv4/IPv6/protocol), model capabilities
matching, unknown tool, ping
- Server API tests: GET/POST /api/plugins/options with schema filtering,
sensitive value masking, missing field validation
- Desktop component tests: PluginConfigModal render, fetch, save with
masked sensitive field skip, error handling, cancel flow
- Fix MCP server async message handling: track pending promises and
wait for completion before process exit
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HIGH fixes:
- Complete SSRF protection: add IPv6 private (fc00::, fe80::),
IPv4-mapped IPv6 (::ffff:10.x), and 0.0.0.0 to blocklist
- Validate provider baseUrl against private IP ranges at load time
- Extract shared validateUrlSafety() for reuse
MEDIUM fixes:
- Skip unchanged masked sensitive fields on save (prevent '********'
from overwriting real API keys)
- Filter POST /api/plugins/options to only schema-declared keys
- Whitelist img src to http/https/data protocols only
- Improve model capabilities matching: exact → prefix → contains
- Cache providers at startup instead of per-tool-call
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Combine main's Workspace LSP release notes with image-gen plugin
feature notes. Keep main's editor-lsp-foundation content and add
new sections for image-gen MCP plugin, desktop plugin config, and
inline image rendering.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Strip brackets from hostname returned by new URL() for IPv6 literals
like [::1]. Previously the check compared against '::1' (unbracketed)
but new URL() returns '[::1]' (bracketed), allowing the SSRF block
to be bypassed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tool results containing MCP image content blocks ({ type: 'image',
data, mimeType }) are now rendered as inline images in the chat UI.
Changes to ToolCallBlock.tsx:
- Add extractImageBlocks() to parse image and image_url blocks
- Render image blocks as a responsive grid above the text output
- Support both MCP image blocks and OpenAI-style image_url blocks
- Result pane auto-expands when image blocks are present
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Agent can now query provider capabilities via list_providers to know:
- Supported image sizes per model
- Whether the model supports image editing
- Whether transparent background is supported
- Max number of images per request (n)
- Response format (url vs b64_json)
Built-in database covers: Agnes image, GPT-image-2, DALL-E 2/3,
Gemini image, Flux, Stable Diffusion. Unknown models get pattern-based
defaults or "unknown" with full compatibility fallback.
generate_image now validates size against capabilities and warns on
mismatch. edit_image checks if any provider supports editing before
attempting.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HIGH fixes:
- SSRF protection: block private/link-local IPs and file:// URLs
in edit_image image_url parameter
- Mask sensitive values in GET /api/plugins/options response
MEDIUM fixes:
- Clamp n parameter to 1-10 to prevent API bill exhaustion
- Validate prompt is non-empty string before API call
- Return empty array when no providers configured (clear error)
- Add 1MB buffer limit for JSON-RPC line accumulation
- Fix PluginConfigModal useEffect re-fetch loop on parent re-render
LOW fixes:
- Remove unused requestId variable
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ensure Solo and coordinator modes are replayed before sending a user message so the server cannot lose the runtime mode after cleanup or reconnects.
Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
- Add Ctrl+B / Cmd+B keyboard shortcut to toggle sidebar visibility
- Add thinkingAutoCollapse setting (default: true) to auto-collapse
thinking blocks when they finish; active thinking streams stay expanded
- Add animated brain SVG icon for active thinking state with pulse
animation; static psychology icon for completed thinking
- Add settings toggle for auto-collapse under Settings > General >
Thinking section
- Add i18n translations for all 5 locales (en/zh/zh-TW/jp/kr)
- Update ThinkingBlock tests to account for default-collapsed behavior
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Show local image-path attachments as UI previews while keeping preview URLs out of the websocket payload.
Co-Authored-By: Claude GPT-5.5 <noreply@anthropic.com>
- Update hardcoded notification title in generalSettings.test.tsx to
match the Code Council brand rename.
- Simplify diskOutput symlink test to not depend on platform-specific
symlink behavior (Linux allows dangling symlinks, Windows does not).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
* 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>
* 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>
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>