* feat(ai): unify capability registry, harness tools, and agent runtime
Centralize MCP/CLI/Catty tool surfaces in the capability catalog with codegen,
move Catty-only harness tools into catalog with local execution, converge
mcpServerBridge builtin dispatch via registry, and wire AgentRuntime with
vault bridge, permission grants, and Round 2 catalog parity.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(ai): vault host tools, artifact cards, and external agent vault guidance
Unify vault host create/import with capability RPC routing, add chat artifact cards with Vault navigation, and align external MCP agents with Catty rules so host creation never falls back to Vault Notes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): address Bugbot findings for streaming, vault bridge, and grants
Clear Catty streaming by chat sessionId, read latest vault state via getters on agent mutations, and stack grant persisters so unmounting one AI panel does not break others.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(vault): clear pending openNoteId after chat navigation focus
NotesManager notifies VaultView once the one-shot openNoteId request is applied so the id does not stick for the rest of the session.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(vault): serialize vault agent IPC mutations to prevent lost writes
Queue vault agent bridge handlers so concurrent hosts.create or note.create tool calls cannot overwrite each other via stale read-modify-write snapshots.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(vault): keep synchronous vault snapshot for agent IPC reads
Mirror hosts, notes, and groups in a ref updated immediately on agent writes so serialized mutations see the latest state before React re-renders.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): sync Catty MCP sessions and defer runtime cleanup on delete
Register terminal sessions with the main process before Catty tool calls, and clear ToolOutputStore only after stopping an active turn when deleting a chat session.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): avoid deleting a newer abort controller on late stop
Only remove the session abort-controller entry when stop finishes if it still refers to the controller that was stopped.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): sync vault snapshot on render and await active turn on stop
Refresh vault agent reads when React props change without clobbering in-flight agent writes, and wait for runTurn to finish before clearing ToolOutputStore on session delete.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): reload permission grants from storage before auto-approve
Re-read persisted grant rules on each approval check so revocations in Settings take effect without requiring a chat hook remount.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): sync permission grants to main process immediately on write
Push MCP permissionGrantsSnapshot as soon as Always allow persists a rule so the next tool call in the same agent turn sees the grant without waiting for useEffect.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Send auto-run multi-line snippets line-by-line with delay instead of bracketed-paste burst. Fixes#1590.
Also limit broadcast line delay to explicit snippet options so normal multi-line broadcast pastes stay byte-for-byte.
- Remove outdated SFTP upload message and replace it with ZMODEM-specific messages in English, Russian, and Chinese locales.
- Add a new function to handle ZMODEM drag-and-drop uploads in the terminal backend.
- Update terminal components to support ZMODEM drag-and-drop functionality.
- Enhance error handling for file uploads and provide user feedback for no files to upload.
- Introduce tests to verify ZMODEM upload behavior and fallback to SFTP for network devices.
Introduce workspace-aware System side panel with remote process/tmux/Docker management, terminal popup for interactive attach, capability warmup, review-hardened IPC, performance optimizations, toast action errors, and SSH channel recovery on reconnect.
* feat(terminal): add remote command history side panel
Read remote shell history over SSH/ET/Mosh exec channels, browse it in a virtualized side panel with search, paste, and save-as-snippet actions. Closes#1381.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): expand command detail inline below selected row
Move the detail strip from a fixed slot above the list into the row
immediately below the clicked entry so expansion reads top-to-bottom.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): filter Netcatty AI PTY commands from remote history
Drop shell history lines containing the __NCMCP_ marker so AI exec noise
does not clutter the command history panel.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): tighten detail strip and add run action
Size the expanded row to its content, add a run-in-terminal button, and
use clearer snippet icon/tooltip for save-as-snippet.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): address review findings before merge
Key cache by host+session, retry Mosh pending reads, and clamp virtual
list scroll position when filtered items shrink.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix terminal drag-and-drop uploads so they target the active terminal cwd and avoid fallback home/login-shell cwd when the active cwd cannot be confirmed.
Expose whole-window transparency via setOpacity with settings and a top-bar quick control, persisting across restarts and syncing across windows.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds a tab context menu action to duplicate a terminal into an independent peer window, with per-window active-tab titles and multi-window lifecycle safeguards.
When sourceSessionId is requested but the bridge falls back to a
fresh connection, the pane remains non-interactive (loading=true)
with stale cached files shown — acceptable trade-off vs always
showing the distracting spinner for near-instant reused connections.
* feat(sftp): add Open with system default for native Windows file association (fixes#1236)
Adds a new 'Open with system default' option to the SFTP context menu
that uses Electron's shell.openPath() to invoke the native OS file opening
mechanism. On Windows, this uses ShellExecute, which works with UWP/WinUI
apps (Photos, Paint, etc.) whose executable paths change with updates.
The existing 'Open with...' (browse for executable) is preserved.
Closes#1236
* fix(sftp): add file watch support to openWithSystemDefault for SFTP auto-sync
P2: The new default-app open flow was not starting a file watch
when SFTP auto-sync was enabled, so edits saved in the system
default app were not uploaded back to the remote host.
Mirrors the downloadToTempAndOpen behavior — accepts an optional
{ enableWatch } parameter and starts a file watch when set.
* fix(sftp): mark transfer as failed when system default open fails
P2: When shell.openPath fails for a remote file, the transfer queue
still shows success because downloadToTemp had already completed the
temp download. Now updates externalTransferId to 'failed' before
throwing, matching downloadToTempAndOpen's behavior.
* fix: route Cmd+W through existing tab close flow
Keep the original tab-close behavior intact, and close the main or settings window only when there is no active closable tab to handle.
* fix: fall back to closing non-listener windows on Cmd+W
Treat BrowserWindow instances that do not participate in Netcatty's command-close bridge as regular closable windows. Keep the existing command-close path for the main and settings windows, and add tests that cover both the fallback close behavior and the renderer-capable send path.
* fix(auto-update): commit app to quit before quitAndInstall on macOS (#1215)
macOS in-place auto-update downloaded and unpacked the new version but
never installed it: the app appeared to close, ShipIt never ran, and no
restart happened. A full uninstall + reinstall did not help; only a
manual DMG replace worked.
Root cause is a code-level coordination bug, not the release pipeline.
The published mac zips are correctly Developer-ID signed, notarized, and
stapled (Team H7WS5L2ML4, consistent across 1.1.17 and 1.1.20), and
latest-mac.yml is well-formed — so Squirrel.Mac signature validation
passes. The failure is that quitAndInstall() drives app.quit() while two
normal-quit behaviors keep the process alive:
1. the main-window close handler hides to tray when close-to-tray is
enabled (it only closes when isQuitting is true), and
2. the before-quit dirty-editor guard preventDefault()s the quit for a
5s renderer round-trip.
Either keeps the parent process running, so Squirrel.Mac's ShipIt helper
— which waits on the parent PID to die before swapping the bundle —
lands in launchd "pending spawn / on-demand-only" limbo and the service
is removed without installing. This matches the reporter's diagnosis
exactly ("ShipIt 没有真正启动安装器", launchd on-demand-only).
Fix: before quitAndInstall fires app.quit(), mark the app as quitting
for an update via windowManager.setQuittingForUpdate(true). That sets
isQuitting (bypassing close-to-tray) and the before-quit handler now
returns early when isQuittingForUpdate() is true (skipping the
dirty-editor round-trip), so the process exits cleanly and ShipIt can
run. The same fix also covers the latent Windows NSIS case where
close-to-tray would block an in-place update.
If the install never actually quits the app (quitAndInstall throws, or
returns without app.quit() on a Squirrel follow-up error / stale
download), the quitting-for-update flags are rolled back — synchronously
on throw, and via a short unref'd watchdog otherwise — so the app does
not get stuck permanently bypassing close-to-tray and the quit guard.
Tests: unit tests for the new windowManager flags and for the install
handler — ordering (setQuittingForUpdate before quitAndInstall), tray
cleanup still runs, no-op when the updater fails to load, rollback on
synchronous throw, and watchdog rollback when the app never quits.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(auto-update): keep dirty-editor guard during update install
setQuittingForUpdate only bypasses close-to-tray (so the window actually
closes and Squirrel.Mac's ShipIt can swap the bundle); it must NOT skip the
unsaved-work guard, or clicking "Restart Now" with a dirty SFTP editor would
silently lose edits. If the user cancels to save, the quit aborts and
autoUpdateBridge's watchdog clears the quitting-for-update flags.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(auto-update): clear update-quit state when the quit is cancelled
When the user clicks "Restart Now" with a dirty editor open, the before-quit
guard cancels the quit (settle "stay"). The update path had already called
setQuittingForUpdate(true) (which flips isQuitting=true to bypass close-to-tray
for the install), so without clearing it the app stays in a quitting state —
close-to-tray and other !isQuitting-gated behavior bypassed — until the 10s
watchdog fires. Clear it immediately on the cancelled-quit path (#1215 review).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(auto-update): check for unsaved editors before quitAndInstall (#1215)
The previous fix bypassed close-to-tray so the app process actually exits and
Squirrel.Mac's ShipIt can swap the bundle, while keeping the before-quit
dirty-editor guard as the unsaved-work safety net. But on macOS that net has a
hole: quitAndInstall() closes the window FIRST and only then fires before-quit.
Once setQuittingForUpdate(true) lets the main window truly close (instead of
hiding to tray), the before-quit guard can run after the window is already gone
— isReachableByUser is false, so it commits the quit and silently drops unsaved
SFTP edits.
Fix: move the dirty-editor check to the moment the user clicks "Restart Now",
in the install handler, BEFORE setQuittingForUpdate / quitAndInstall — while the
window and renderer are still alive:
- dirty -> abort the install (don't set the quitting flags, don't
quitAndInstall) and broadcast netcatty:update:needs-save so the
renderer prompts the user to save and retry.
- clean -> proceed with the existing flow (commit-to-quit, tray cleanup,
quitAndInstall, watchdog).
- no reachable main window / crashed renderer -> install directly (no user to
ask), matching the before-quit fail-open path.
The before-quit dirty guard is kept as defense-in-depth: if the window is still
reachable it re-checks (clean, since we just verified), and if it's already gone
it lets the quit through — which is now safe because the install handler already
confirmed there were no unsaved editors.
The request/reply/timeout round-trip is extracted into a shared helper,
electron/bridges/dirtyEditorGuard.cjs (queryDirtyEditors), so the install
handler and main.cjs's before-quit guard use one implementation. main.cjs's
before-quit is refactored onto it (behavior preserved: sender-filtered reply,
fail-open timeout, and the setQuittingForUpdate(false) rollback when the user
cancels to save).
The needs-save notice is BROADCAST to every window, not just the queried main
window: "Restart to Update" can be clicked from the Settings window, which would
otherwise see the click do nothing. preload exposes onUpdateNeedsSave; the
subscription lives in useUpdateCheck (state layer), and both consumers — App.tsx
(main window) and SettingsPage (settings window) — pass an onNeedsSave callback
that shows an actionable toast ("save your editors, then click Restart Now
again") in en / zh-CN / ru.
Also lengthen the quitting-for-update rollback watchdog from 10s to 60s. On
macOS quitAndInstall() can return while Squirrel is still pulling the downloaded
ZIP from the local update server before it closes the windows; on a large/slow
update that can exceed 10s. Clearing isQuitting that early would let the eventual
native quit hit a non-quitting close-to-tray handler and strand the install
again — the exact #1215 failure. The longer window only fires when the app is
realistically stuck, at the cost of close-to-tray staying bypassed a little
longer in the rare genuine-failure case.
Tests: queryDirtyEditors (result / no-dirty / timeout / wrong-sender / dead or
crashed webContents / send-throws / no-ipcMain paths); install handler pre-check
(dirty -> no quitAndInstall + needs-save broadcast to all windows; clean ->
quitAndInstall runs; no main window -> installs without asking). Existing
install-handler tests updated for the now-async handler.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add startEtSession to the preload API surface, routing through the
netcatty:et:start IPC channel. Define the startEtSession options type
in netcatty-bridge-session.d.ts with ET-specific parameters (etPort,
terminalPath, jumpHosts, etc.).