* feat(scripts): add terminal script automation, recording, and side panel
Introduce nct script runtime in the main process with terminal-side recording,
execution overlay, Monaco editor, connect-script queues, and scripts side panel
integration. Fix recording to wire scriptRecorderRef into xterm, use waitForPrompt
for root/user shells, and save recorded scripts through the global editor dialog.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): address Bugbot review findings for recording and side panel
Refresh script run state in side panel memo, guard toolbar recording against
multi-session conflicts, reset connect scripts on reconnect, resolve focused
session via getActiveTerminalSessionId, and avoid wiping host edit form when
snippets list updates.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): prevent pending script re-run and improve workspace run feedback
Track one-shot pendingScript execution separately from onConnect queue so
reconnects do not replay Run-now scripts. Fall back to focused session when
running on all tabs outside a workspace, and toast when every tab is still
connecting.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): consume connect-script window once and filter serial hosts
Mark the onConnect script evaluation as consumed even when the queue is
empty so later snippet/host edits do not rerun automation mid-session.
Restore serial-host filtering in SelectHostPanel confirm handler.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): grace period before giving up on connect-script queue
Only mark onConnect automation as consumed after scripts run or a short
post-connect grace window expires, so late-loading snippets still trigger
while mid-session Vault edits do not.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): gate connect-script consumption on vault hydration
Replace the grace timer with vault-init and unresolved host script binding
checks so late-loading snippets still run onConnect automation while
mid-session edits stay suppressed. Clear pending script run state on
disconnect so reconnect retries workspace pending scripts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): honor run targets and retry failed connect automation
Route QuickScriptEditorDialog Run through onRunSnippet for multi-host
workspaces. Defer connect/pending script completion markers until each
script succeeds and retry the queue after failures.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): stabilize pending runs and global connect queue sync
Keep pending script run state across reconnects, run onConnect scripts
before one-shot workspace pending scripts, and strip per-host queue entries
when an onConnect script targets all hosts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): defer host queue snippet sync and guard connect runs
Persist snippet target links only when the host panel is saved, block
duplicate connect-script scheduling while a batch is running, and require
runnable hosts before showing Run in the snippets editor footer.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): retry connect failures and surface output-trigger errors
Use a retry tick in connect-script effect deps, toast automation failures
from output triggers, and require runnable hosts before QuickScript Run.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): track id-less pending runs and re-arm output triggers
Remember anonymous pendingScript objects after first run and allow
onOutput triggers to fire again when new terminal output arrives.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): unblock paused connect runs and pass session meta
Resolve waitForScriptRun on pause, run workspace scripts per session
with hostname context, defer output-trigger arming until runs start, and
add noRunnableHosts i18n for QuickScript Run.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): serialize output triggers and defer empty connect finalize
Fire at most one onOutput trigger per scan while a run is active, and
only finalize an empty connect queue after vault snippets are loaded.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): guard output launch race and script run waits
Block overlapping onOutput launches, timeout stuck waitForScriptRun
promises, and disable vault Run when no connectable targets exist.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): match output triggers on new chunks only
Scan onOutput patterns against recent terminal output instead of buffer
length dedupe, and restore overflow-visible on SelectHostPanel new-host flow.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): await sequential workspace runs and output launch
Wait for each tab script to finish in sequential workspace mode, and hold
output-trigger launch guard until the session run becomes active.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): wait through pause and hold output trigger until done
Keep connect and sequential workspace queues blocked until scripts
complete or fail, and retain output-trigger guard until the launched run
finishes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): harden output trigger wait helpers
Add explicit timeouts to output-trigger wait helpers and stop rescanning
the buffer after script completion to avoid sticky-match rerun loops.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): allow empty connect queue and match output in new chunks
Treat an empty connectScriptIds array as an explicit cleared queue instead
of re-migrating from snippet targets, and only fire onOutput triggers when
the latest terminal output contains the pattern.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): require fresh output overlap for onOutput triggers
Only fire output-matched scripts when the regex match extends into the
latest terminal chunk, preventing reruns from stale buffer text.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): promote manual scripts added to host connect queue
Diff connect queue changes from raw connectScriptIds so saving a host
updates manual scripts to onConnect when they are newly queued.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): dedupe output triggers by match position
Track the buffer end offset of each output trigger match and ignore
stale re-scans, with a longer wait for run registration before retry.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): record output trigger matches only after run starts
Propagate automation launch failures to output triggers and commit match
dedupe only once the session run is registered as active.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): address Codex review on wait cursor and sensitive codegen
Track scan offset so waitFor/waitForAny only match output after the previous wait, and emit sendLine after sensitive prompt in recorded scripts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): harden onOutput triggers against PTY echo and local input
Filter server output before pattern matching so onOutput fires only after
Enter on real stdout, not on keystroke/paste echo, OSC/SGR sequences, or
alternate-screen apps. Add dev diagnostics and scope triggers to the active
session when no explicit host targets are configured.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(scripts): remove onOutput trigger debug logging
Drop temporary diagnostics module and console traces now that PTY echo
filtering is verified; keep the production trigger pipeline unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Adopts streamText instructions/runtimeContext/toolApproval APIs, lifecycle events, and denied-tool result rendering so observer blocks and approval rejections stay visible in chat history.
Co-authored-by: Cursor <cursoragent@cursor.com>
* 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>
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.
* fix(ai): make SDK deps optional, degrade gracefully when missing
- Move @anthropic-ai/claude-agent-sdk and @github/copilot-sdk
from dependencies to optionalDependencies so npm install does
not fail when they are unavailable
- claudeDriver.listClaudeModels: catch import error, return [] silently
- copilotDriver.listCopilotModels: catch import error, return [] silently
- sdkStreamHandlers: downgrade log from console.error to console.debug
The renderer already falls back to curated model presets when
list-models returns [], so no functional change.
* fixup: honor queryFn before SDK import; regenerate lockfile with optional markers
* fixup: guard runTurn against missing SDK modules
runClaudeTurn and runCopilotTurn now catch dynamic import errors
and emit a user-friendly error message instead of crashing with
a raw module-not-found error.
* chore(ai): upgrade ACP packages and unwrap Skill+CLI command in tool-call panel
Package bumps:
- @zed-industries/claude-agent-acp 0.22.2 → @agentclientprotocol/claude-agent-acp 0.37.0
(old npm package is deprecated; scope rename)
- @zed-industries/codex-acp 0.10.0 → 0.15.0
- @mcpc-tech/acp-ai-provider 0.2.8 → 0.3.3
- electron-builder asarUnpack glob + bridge require.resolve switched to the new scope
After the upgrade Codex tool-call cards started showing the local
worktree path for every step — "Run /Users/.../netcatty-tool-cli session
--session …" — instead of the remote command. Three things lined up:
1. The new acp-ai-provider maps ACP's `title` to `toolName`, and Codex's
title is the full shell invocation it's about to run.
2. Codex local_shell ships args as ["/bin/zsh","-lc","<full>"], so the
old `typeof args.command === 'string'` branch in ToolCall never fired
and we fell through to printing `name` (i.e. the title).
3. The bridge serializes tool args under `args`, but the ACP adapter
only read `event.input`, so even when args were available the
renderer received {}.
Fixes:
- acpAgentAdapter: read tool input from both `event.input` and
`event.args` so bridge-serialized chunks and direct AI SDK chunks
both work.
- ai-elements/tool-call: new extractDisplayCommand() unwraps the shell
array, then the netcatty-tool-cli wrapper (exec/job-start … -- <cmd>),
and renders the real remote command. session/env/job-poll/etc. fall
back to short labels ("netcatty: inspect session", …) instead of
exposing the binary path.
- shellUtils.cjs: defensive JSON-parse the ACP wrapper input in case
the AI SDK ever stops auto-parsing it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(build): exclude bundled Claude CLI binaries from the installer
@anthropic-ai/claude-agent-sdk@0.3.x bundles the native Claude Code CLI
(~211MB per arch) as optional sibling packages. Including them would
silently regress Netcatty's "bring your own Claude" design — the project
has always required users to install Claude Code locally, and the entire
path-discovery flow exists precisely to honor that contract:
- useAgentDiscovery.ts scans the user's PATH for `claude` and writes
the absolute path into the agent config's CLAUDE_CODE_EXECUTABLE env.
- aiBridge.cjs runs normalizeClaudeCodeExecutableEnvForAcp on every ACP
spawn, forwarding the env var to the child process.
- The @agentclientprotocol/claude-agent-acp wrapper's claudeCliPath()
(acp-agent.js) prefers process.env.CLAUDE_CODE_EXECUTABLE over the
bundled binary and only falls back to sibling-package resolution when
the env var is empty.
So the right place to enforce the design is electron-builder: exclude
node_modules/@anthropic-ai/claude-agent-sdk-* from `files`. Dev mode is
unaffected (optional deps still install for `npm run dev`); only the
packaged installer drops the binaries, saving ~150MB. Users without
Claude Code installed get the same SDK error they got pre-upgrade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ctrl-w): add ps-node + windows-process-tree + tsx deps for close-priority feature
* fix(ctrl-w): drop ps-node dep and add windows-process-tree to asarUnpack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): add ptyProcessTree bridge with per-platform child-process enumeration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ctrl-w): ptyProcessTree uses args= for full command + warns on pid overwrite
- Replace `comm=` with `args=` in defaultListPosix so the full command
line is captured on both macOS (BSD ps) and Linux (GNU ps), avoiding
the 15-char TASK_COMM_LEN truncation.
- Add console.warn in registerPid when the same sessionId is overwritten
with a different pid, making the race condition visible in logs.
- Add test: registerPid warns exactly once on a pid change, not on a
same-pid re-registration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): register local PTY pid with ptyProcessTree on spawn/exit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ctrl-w): unregister pids in cleanupAllSessions to match per-delete invariant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): add IPC handlers for pty child processes and confirm-close dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ctrl-w): guard BrowserWindow.fromWebContents null and document dialog dismiss contract
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): expose ptyGetChildProcesses and confirmCloseBusy on window.netcatty
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): add i18n strings for close-busy-terminal dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): add resolveCloseIntent pure function with 8 unit tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): expose handleCloseSidePanel via ref to App.tsx
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): wire resolveCloseIntent + local-shell busy confirmation into closeTab hotkey
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ctrl-w): add re-entrancy guard, aggregate busy count, sync sidebar ref, dedupe intent branches
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ctrl-w): auto-close workspace when its last session is closed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ctrl-w): sidebar close wins over focused terminal in priority chain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ctrl-w): sidebar priority applies to single-session tabs too, not just workspaces
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ctrl-w): compute empty-workspace auto-close outside setSessions updater
Addresses Codex P2 on #739: React 18+ does not guarantee updater
execution timing under concurrent scheduling. Moving the decision
outside the updater makes the microtask queue deterministic.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add ZMODEM (lrzsz) file transfer support for terminal sessions
Adds ZMODEM protocol detection and file transfer capability to all
terminal session types (Local, SSH, Telnet, Mosh, Serial). Uses
zmodem.js library with main-process sentry pattern to intercept
binary data before string decoding, avoiding IPC pipeline changes.
- zmodemHelper.cjs: shared ZMODEM sentry with Electron dialog integration
- terminalBridge.cjs: encoding:null for PTY + sentry wrappers for all session types
- sshBridge.cjs: sentry wrapper for SSH stream data
- preload.cjs + global.d.ts: ZMODEM event IPC bridge and TypeScript types
- useZmodemTransfer.ts: React hook for ZMODEM transfer state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: preserve charset decoding and add ZMODEM progress UI
- zmodemHelper: pass raw Buffer to onData, let callers handle decoding
- terminalBridge: use StringDecoder for telnet/serial, UTF-8 for local/mosh
- sshBridge: restore iconv decoder for SSH session charset support
- ZmodemProgressIndicator: floating progress bar with cancel button
- Terminal.tsx: wire useZmodemTransfer hook + toast notifications
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: ZMODEM listener cleanup, stream leak, and toast dedup
- preload: clean up zmodemListeners on session exit (memory leak)
- zmodemHelper: add ws.on('error') handler to close write stream on failure
- Terminal: use ref guard to prevent duplicate toast notifications
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address code review findings for ZMODEM
- cancel/consume error now send IPC event to renderer (prevents stuck UI)
- sanitize download filename with path.basename (path traversal prevention)
- add on_detect concurrency guard (deny if transfer already active)
- formatBytes: handle negative, zero, and TB+ values safely
- closeSession: cancel active ZMODEM before destroying transport
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prevent double-notification on cancel and stream error resilience
- Guard .then()/.catch() in promise chain: skip if cancel() already handled
- Download: add writeAborted flag to stop on_input after stream error
- Upload: pre-compute file stats to avoid O(N²) statSync calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use zsession.abort() instead of close() on dialog cancel
close() is only available on Send sessions. Calling it on a Receive
session throws, leaving the sentry's internal _zsession dangling and
causing subsequent terminal data to be consumed by the abandoned
ZMODEM session (terminal freeze). abort() is defined on the base
ZmodemSession class and properly fires session_end to reset the sentry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: handle ZFIN/OO mismatch as successful transfer
When sz exits over SSH, the shell prompt often arrives before the
ZMODEM "OO" end marker, causing zmodem.js to throw a protocol error.
Since ZFIN was already exchanged (= all file data transferred), treat
this specific error as a successful completion and forward the shell
prompt data back to the terminal via sentry re-consume.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: codex review — UTF-8 decoder, ZFIN abort, session exit cleanup
- terminalBridge: use StringDecoder for local/mosh PTY to handle
multi-byte UTF-8 split across buffer boundaries (prevents garbled
CJK/emoji output)
- zmodemHelper: on ZFIN/OO success path, use _on_session_end() instead
of abort() to avoid sending CAN (Ctrl-X) bytes to the remote shell
- useZmodemTransfer: listen to onSessionExit to reset state when the
session dies mid-transfer (prevents stuck progress indicator)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: codex review — file collision handling and stream flush
- Download: auto-rename with (1), (2), etc. if file already exists
in the target directory, preventing silent overwrite
- Download: wait for all write streams to finish flushing before
resolving the session_end promise, ensuring data is on disk when
the UI reports completion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: codex review — Windows PTY string compat and Telnet binary safety
- Local/Mosh PTY: handle string data from Windows node-pty which
ignores encoding: null; convert to Buffer before sentry.consume()
- Telnet: bypass IAC negotiation during active ZMODEM transfer to
preserve 0xFF bytes in binary data
- Telnet writeToRemote: escape 0xFF as 0xFF 0xFF per Telnet spec
so ZMODEM binary data is not treated as IAC commands
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: codex review — Windows PTY guard, Telnet IAC, stream cleanup
- Local/Mosh: skip ZMODEM sentry on Windows where node-pty can't
provide raw bytes; fall back to original string pipeline
- Telnet: always run IAC negotiation (even during ZMODEM) since the
Telnet layer still escapes 0xFF as IAC IAC; the existing handler
already correctly collapses IAC IAC → single 0xFF
- Download: destroy un-ended write streams on session_end to prevent
hanging promises and leaked file descriptors on abort
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: codex review — early session start, progress throttle, no dup start
- Download: call zsession.start() before showing folder picker dialog
so lrzsz doesn't time out waiting for ZRINIT
- Download: throttle progress IPC to ~10 updates/sec (100ms interval)
to avoid overwhelming renderer on fast links
- Download: remove duplicate zsession.start() at bottom of Promise
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: handle ZRPOS and prevent terminal flood after ZMODEM abort
- Add 500ms cooldown after ZMODEM abort: suppress residual protocol
bytes from remote rz/sz that would otherwise flood the terminal
- Send 8x CAN (Ctrl-X) on abort/cancel/error to force remote end to
stop transmitting even if the initial abort sequence was lost
- Handles "Unhandled header: ZRPOS" gracefully (zmodem.js doesn't
support error recovery, so abort is the correct response)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: send Ctrl+C after abort in all cancel/error paths
Debian's rz stays attached to the TTY after receiving CAN sequences.
The cancel() path already sent Ctrl+C via scheduleRemoteInterruptAfterCancel,
but dialog-cancel and consume-error paths did not. Now all three abort
paths (dialog cancel, consume error, explicit cancel) send Ctrl+C after
150ms to ensure the remote rz/sz process exits and the shell regains control.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add interruptRemote for SSH ZMODEM sentry
Pass SSH stream.signal("INT") as interruptRemote callback so the
ZMODEM helper can send SIGINT to the remote process when cancelling
transfers, complementing the Ctrl+C byte sent via writeToRemote.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: dialog-cancel abort uses module-level helper to avoid ReferenceError
sendExtraAbortBytes and writeToRemote are closure-scoped inside
createZmodemSentry, not accessible from handleUpload/handleDownload.
Extract abortRemoteProcess as a module-level function that takes
writeToRemote as a parameter, used in both dialog-cancel paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: dialog cancel throws instead of returning to avoid false complete
When user dismisses the file/folder picker, handleUpload/handleDownload
now throw "Transfer cancelled" instead of returning normally. This
ensures the .catch() handler fires (sending error event) rather than
.then() (which would incorrectly send complete event).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: codex review — preserve transferType in progress events
- useZmodemTransfer: copy transferType from progress events so the
transfer direction is preserved if renderer re-subscribes after
the initial detect event was missed
- zmodemHelper: clean up upload loop comments (backpressure handled
via 64KB chunks + setImmediate yield per iteration)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: codex review — guard stale session cleanup, delete partial downloads
- Promise chain .then/.catch/.finally now compare currentZSession
identity (=== zsession) instead of truthiness, preventing a new
transfer from being clobbered by the old promise settling
- Aborted/incomplete downloads are deleted from disk on session_end
so users don't end up with corrupt partial files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: unconditional cooldown suppression after ZMODEM abort
The previous cooldown checked if data "looks like residual ZMODEM"
which fails for sz's file content (arbitrary printable bytes). Now
cooldown unconditionally drops ALL incoming data for 2 seconds after
abort, with repeated CAN bursts to ensure the remote sz stops. This
prevents the terminal flood seen when cancelling large sz downloads
on fast connections.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Load @xterm/addon-unicode11 and set activeVersion to '11' for better
character width handling of Nerd Fonts, Powerline glyphs, and CJK
characters. This matches the approach used by tabby terminal.
Closes#543 (Nerd Fonts portion)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Load @xterm/addon-unicode11 and set activeVersion to '11' for better
character width handling of Nerd Fonts, Powerline glyphs, and CJK
characters. This matches the approach used by tabby terminal.
Closes#543 (Nerd Fonts portion)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: bundle claude-code-acp to prevent crash when binary is missing (#400)
When users select Claude Code in the AI module, the app spawns
`claude-code-acp` via ACP. Previously only the `claude` CLI was checked
during agent discovery, so if `claude-code-acp` was not on PATH the
spawn would fail with ENOENT and crash the Electron main process.
- Add `@zed-industries/claude-code-acp` as a bundled dependency
- Add `resolveClaudeAcpBinaryPath()` that checks PATH first, then
falls back to the npm-bundled binary (mirrors Codex pattern)
- Use the resolver in both the primary and fallback ACP provider paths
- Update agent discovery to detect agents via bundled ACP binary when
the standalone CLI is not installed
Closes#400
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add claude-code-acp and its deps to asarUnpack
In packaged Electron builds, files inside app.asar cannot be executed
by child_process.spawn. Add claude-code-acp and its runtime dependencies
to asarUnpack so the binary is accessible in production.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: migrate from deprecated claude-code-acp to claude-agent-acp
The @zed-industries/claude-code-acp package has been renamed to
@zed-industries/claude-agent-acp (bin: claude-agent-acp). Update all
references across the codebase:
- package.json: replace dep with @zed-industries/claude-agent-acp@0.22.2
- electron-builder.config.cjs: update asarUnpack entries, remove stale
deps (diff, minimatch) no longer needed by the new package
- shellUtils.cjs: update binary name and require.resolve path
- aiBridge.cjs: update acpCommand, ALLOWED_AGENT_COMMANDS, isClaudeAgent
- settings types, i18n locales: update command references
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The beta version had native module loading issues on Arch Linux AppImage
builds (ERR_DLOPEN_FAILED). The stable release uses an improved module
loading strategy with prebuild support for macOS/Windows and better
build-from-source fallback for Linux.
Related: #264
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integrate Claude Agent SDK for direct streaming chat, add Codex login/logout
flow with OAuth support in settings, improve AI chat panel UI and agent
discovery, and update build config for new dependencies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add auto-discovery of CLI agents (Claude Code, Codex, Gemini) from system PATH
- Integrate ACP (Agent Client Protocol) for real-time streaming with codex-acp
- Bundle @zed-industries/codex-acp binary for reliable agent spawning
- Add ThinkingBlock component with shimmer animation and auto-collapse
- Refactor chat UI: no avatars, bordered user bubbles, plain assistant text
- Support {prompt} placeholder in agent args for flexible invocation
- Add persistent ACP sessions with proper cleanup on app exit
- Detect auth errors and show actionable messages to users
- Fallback to raw process spawn for agents without ACP support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add auto-update support via electron-updater (#289)
- Add autoUpdateBridge.cjs wrapping electron-updater for check/download/install
- Register bridge in main.cjs, expose IPC in preload.cjs
- Add auto-update methods to NetcattyBridge type in global.d.ts
- Extend updateService.ts with electron-updater bridge functions
- Add Software Update section in Settings > System tab with state machine UI
- Add i18n keys for update UI (en + zh-CN)
- Add publish config for GitHub Releases in electron-builder.config.cjs
- Update CI workflow to upload update metadata (*.yml, *.blockmap, *.zip)
- Fallback to manual GitHub download for unsupported platforms or errors
* fix: address Codex review - guard bridge call and pin sender window
- Guard optional bridge call in SettingsSystemTab to prevent TypeError
when getAppInfo is unavailable (e.g. browser/dev/test rendering)
- Capture senderWindow at download initiation in autoUpdateBridge so
progress/downloaded/error events always go to the requesting renderer,
even if focus changes during download
* fix: use semver ordering for version check and clean up listeners on rejection
- Replace strict equality (===) with localeCompare for version comparison
to avoid false positives on pre-release/nightly builds
- Clean up download-progress/update-downloaded/error listeners in the
catch path when downloadUpdate() rejects before emitting events
* feat: redirect update toast to Settings window for in-app update
- Update toast notification now opens Settings window instead of
GitHub Releases page, enabling the in-app download/install flow
- Add 'update.viewInSettings' i18n key (en + zh-CN)
- Remove unused openReleasePage from App.tsx destructuring
- Move useWindowControls() before the update effect to fix declaration order
On Windows/Linux, the frameless title bar had ~20px of dead space
(12px right padding + 8px drag shim) to the right of the close button.
- Replace Tailwind `px-3` with conditional inline paddingRight (0 on
Windows, 12px on macOS) so the close button sits at the window edge
- Render the drag shim only on macOS where it's useful as a drag region
macOS layout is unchanged.
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>