Commit Graph

39 Commits

Author SHA1 Message Date
bincxz c87f488553 fix(ai): keep history markdown warmup off the first-type window
A 300ms initial delay let ChatMessageList start the Streamdown import
while the user was still opening the panel to type. Restore the 4s
expand grace and drop a pending Shiki plugin setState if the message
unmounts before the idle callback lands.
2026-08-15 03:18:25 +08:00
bincxz b02d52516c fix(ai): load Streamdown without Shiki until a code fence is idle
messageResponse pulled @streamdown/code on first markdown import, so
opening a chat with history paid for the full highlighter even when
the reply was just prose.

Render with CJK only first. When a fence exists and the composer is
idle, lazily import the isolated Shiki plugin.
2026-08-15 03:03:38 +08:00
bincxz b686686efb fix(ai): close parked menus and keep pending draft coherent
Review found three real gaps: send/new chat left pending composer text in
place, hiding the panel could leave body-portaled menus open, and idle IPC
could still start on the first frame after expand.

Discard pending text when the draft is cleared, close ChatInput and agent
menus when the panel is parked, and give composer-idle IPC the same expand
grace as Streamdown warmup.
2026-08-14 23:22:56 +08:00
bincxz b2b79443af fix(ai): stop first-type hitch after expanding the chat panel
Opening the AI panel and typing a few characters hit a main-thread stall.
Streamdown warmup, panel remount, draft-store fan-out, and expand-time
IPC all raced the first keystrokes. import() cannot be cancelled, so the
heavy renderer must not start while the composer is busy.

Keep the composer mounted on hidden retained panels, ignore composer-text
store notifications, debounce draft flushes, and defer discovery/skills/
model IPC until the composer is idle. History stays plaintext until
Streamdown is warm, then hydrates a few rows at a time.
2026-08-14 23:06:21 +08:00
Netcatty-Bot 9a3d96be76 fix(ai): improve approval command readability and cancel idle timeout on review (#2734)
* fix(#2697): automated Cursor CLI fix

* fix: address Codex review on PR #2734

* fix(ai): keep MCP absolute approval deadline and Escape reject

Canceling idle review no longer leaves MCP approvals unbounded past the
external agent tool-call window, and Enter-only key stopPropagation lets
Escape still reject from Copy/Expand controls.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ai): keep wrapped CLI target context on approval cards

Parse --session from netcatty-tool-cli wrappers into execution context and
still show invocation args when the pending card displays an unwrapped command.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ai): surface Codex reason on pending approval cards

Keep agent justification visible in the execution-context strip and still
show extra invocation args such as commandActions beside the command block.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ai): bound Catty approval cancel with absolute deadline

Keep Catty confirm-mode approvals from going unbounded after review:
cancelApprovalTimeout re-arms the creation absolute deadline (MCP-style)
so a late approve cannot outlive the original timeout/stream tool budget.

Also harden pending-card Enter: extract isNestedInteractiveApprovalTarget
so Copy/Expand (and other nested controls) never Approve Once via bubble.

* fix(ai): bound Codex approval cancel and unwrap CLI wrappers

cancelInteractionTimeout now re-arms the absolute creation deadline
(Catty/MCP style) instead of leaving review unbounded. Also treat
netcatty-tool-cli.cjs/.cmd as CLI wrappers so approval Command/Copy
shows the remote command after --.

* fix: address Codex review on PR #2734

* fix(ai): separate Catty approval idle timer from hard deadline

Review activity re-arms a full idle window from now instead of the original
absolute remainder, capped by a hard deadline (3x idle, max 30m). Stream
tool budgets use the hard deadline so mid-review approvals are not cut off.

* fix(ai): re-arm Catty approval idle and sync-deny hard deadline

Review interactions kept a one-shot timeoutCancelledRef, so later
cancelApprovalTimeout calls no longer re-armed idle. Remove the one-shot
and deny synchronously when the hard deadline has already elapsed so a
late approve cannot race setTimeout(0).

* fix(ai): re-arm approval idle on args-only scroll review

Pending approvals without a display command (Codex file-change/permissions,
write tools with JSON args) only show the overflow args block. Wire the same
onScroll/onWheel markReviewing path used by the command pre so continuous
wheel/trackpad review re-arms Catty and MCP/Codex idle timers.

---------

Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com>
Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 08:53:36 +08:00
Netcatty-Bot 4747c45a99 feat(ai): add in-chat jump list for long sessions (#2733)
* fix(#2679): automated Cursor CLI fix

* fix: address Codex review on PR #2733

* fix: address Codex review on PR #2733

* fix(ai): keep chat jump pin from collapsing loaded history

Preserve the rendered message window across jump release, ignore streaming isAtBottom flips, and make load-earlier advance from the effective pinned tail.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com>
Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 19:02:09 +08:00
bincxz 0950efb41b feat: resize AI chat composer 2026-08-03 15:18:39 +08:00
bincxz 3b0a519c0f fix(ai): constrain command tooltips 2026-08-02 17:17:11 +08:00
bincxz 81fe71dc4b perf(ai): defer heavy side-panel UI while hidden and streaming
Keep retained AI panels mounted for session continuity, but skip
ChatMessageList/ChatInput while CSS-hidden so tab switches do not
reconcile Streamdown trees. Render assistant text as plain content
during streaming, lazy-load Streamdown only after the turn settles,
and split message layout from the markdown chunk for a lighter open path.
2026-07-30 15:46:53 +08:00
ryan-wong-coder 646f705ca0 fix(ai): allow terminal-only Codex steering 2026-07-16 00:02:41 +08:00
ryan-wong-coder 064677f5c7 feat(ai): add Codex App Server turn steering 2026-07-15 23:19:34 +08:00
ryan-wong-coder 789f9d5007 fix(ai): honor Codex App Server review contracts 2026-07-15 16:51:39 +08:00
ryan-wong-coder 7203a61914 feat(ai): add Codex App Server runtime 2026-07-15 15:50:57 +08:00
bincxz 857dc789fc clean up deferred UI timers 2026-07-12 23:57:10 +08:00
陈大猫 97b1a2a336 Migrate Catty harness to AI SDK v7 and fix tool denial UI. (#1707)
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>
2026-06-26 11:15:46 +08:00
陈大猫 9aed2c41d4 feat(ai): unify capability registry, harness tools, and agent runtime (#1685)
* 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>
2026-06-26 00:08:39 +08:00
bincxz affe197f26 Improve Netcatty responsiveness 2026-06-23 01:26:12 +08:00
陈大猫 3fc56df111 refactor(ai): migrate agent backends from ACP to official SDKs (claude/codex/copilot) (#1229) 2026-06-04 17:11:02 +08:00
陈大猫 1fec5925eb Refactor large modules and fix runtime errors (#1136) 2026-05-28 15:12:19 +08:00
陈大猫 456ddcfe68 chore(ai): upgrade ACP packages and unwrap Skill+CLI command in tool-call panel (#1128)
* 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>
2026-05-27 23:26:14 +08:00
陈大猫 b30696c98b Clean up dead code and duplicated helpers (#1001) 2026-05-18 20:00:10 +08:00
DeepFal d20a18b862 Fix AI code block rendering fallback (#983) 2026-05-18 13:19:30 +08:00
陈大猫 ea5320d94a Fix #954: unify Tooltip styling + replace native selects (#961)
* Fix #954: unify Tooltip styling + replace native selects

Replace native HTML title= tooltips and native <select> dropdowns
with the existing Radix-based Tooltip / Select components so they
share the app's rounded styling, theme tokens and i18n pipeline.
Adds a global TooltipProvider in AppWithProviders so every
descendant Tooltip works without a per-file Provider wrapper.

Scope (driven by the issue #954 examples and "全部都处理" follow-up):

- TerminalLayer toolbar: Add Terminal / Split View / SFTP / Scripts
  / Theme / AI Chat / Move panel / Close panel.
- TopTabs middle bar: quick switcher, more tabs, AI assistant, theme
  toggle, settings; window-control buttons (min/max/close), tray
  close and hotkey reset/disable have their native title dropped per
  the user's explicit opt-out ("可以不用Tooltip,直接全局禁用
  原生title 属性").
- AI panels: AIChatSidePanel session history / new chat / delete,
  ConversationExport, AgentSelector, ChatInput attach / expand /
  permission, ModelSelector, ProviderCard, ai-elements/tool-call.
- SFTP: SftpSidePanel header, SftpBreadcrumb, SftpFileRow,
  SftpPaneToolbar, SftpTabBar, SftpTransferQueue.
- Settings: SettingsPage close, SettingsAppearanceTab theme/accent
  swatches, SettingsFileAssociationsTab edit/remove, SettingsSystemTab
  crash-log paths and global hotkey reset.
- Host vault: HostDetailsPanel (clear / suggestions / show-password /
  key path / browse key), GroupDetailsPanel, KnownHostsManager,
  ConnectionLogsManager, KeychainManager, SyncStatusButton,
  CloudSyncSettings, LogView, QuickSwitcher, ScriptsSidePanel,
  Terminal status bar copy-host + broadcast/focus, ZmodemProgressIndicator.
- Terminal subcomponents: HostKeywordHighlightPopover, TerminalComposeBar,
  TerminalConnectionDialog, TerminalSearchBar.
- Editor: TextEditorPane (subtitle, search, wrap, promote-to-tab).
- TrayPanel session rows and port-forwarding rows.

Native <select> migrated to custom Select component:
- SerialConnectModal (data bits, stop bits, parity, flow control)
- SerialHostDetailsPanel (same four fields)
- HostDetailsPanel backspace behavior
- GroupDetailsPanel backspace behavior
- SettingsTerminalTab local shell picker
- terminal/ThemeSidePanel font weight

Hardcoded English strings extracted to i18n. New keys for both
en and zh-CN: terminal.layer.*, topTabs.*, ai.chat.* (sessionHistory,
attach, collapse, expand, enableAgent), zmodem.*, settings.shortcuts.
resetToDefault. Inline help text on SnippetsManager package-name input
removed because the same hint is already shown in a visible <p> below
the input.

Existing per-file <TooltipProvider> wrappers (SnippetsManager,
ScriptsSidePanel, SelectHostPanel, RuleCard, HostDetailsPanel proxy
section) are left in place — they nest harmlessly under the global
provider and stay self-sufficient for component tests.

Tests:
- tsc clean for changed files (pre-existing repo-wide errors
  unrelated to this PR).
- All 802 tests pass (3 skipped pre-existing).
- HostDetailsPanel.proxyProfile.test and TextEditorPane.test
  updated to wrap with TooltipProvider, matching the runtime
  context now needed by the migrated components.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix #954: wrap Settings + Tray windows with TooltipProvider

Settings and the tray panel mount as separate Electron windows with
their own React root in index.tsx, so they do not inherit the global
TooltipProvider added under AppWithProviders. After the unified
Tooltip migration, any settings tab that used a Tooltip (Appearance,
Application, FileAssociations, System, Shortcuts, Terminal, AI
ProviderCard, AI ModelSelector) — and TrayPanel — threw
"Tooltip must be used within TooltipProvider" and rendered nothing.

Wrap both branches with TooltipProvider at the same level as
ToastProvider in index.tsx.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:14:24 +08:00
陈大猫 6a39ed05a9 [codex] Tighten AI chat spacing (#883)
* Tighten AI chat spacing

* Scope AI table spacing styles
2026-05-01 11:33:07 +08:00
陈大猫 2063a5ccfe Expose data-role CSS hooks on chat messages (#854)
Closes #838.

Adds stable `data-role="user|assistant|system|tool"` attributes plus
`ai-chat-message` / `ai-chat-message-content` classnames on the chat
message rows in Catty Agent's chat panel. Users can now distinguish
their own messages from agent replies via Settings → Appearance →
Custom CSS, e.g.

  .ai-chat-message[data-role="user"] .ai-chat-message-content {
    background: rgba(91, 124, 250, 0.12);
  }

The default theme is intentionally minimal (bordered user bubble,
plain assistant text). Rather than change the default — different
users want different distinctions — this exposes a hook so anyone
can colour the rows however they prefer without forking.

The attribute names are part of the UI's stable contract; a comment
on the Message component flags this for future renames.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 16:34:30 +08:00
Eric Chan 1f3e531d7b Fix AI skill selection handling 2026-04-13 11:03:43 +08:00
Eric Chan 84423a0096 fix: resolve TypeScript errors and optimize User Skills with async IO 2026-04-12 17:11:50 +08:00
Eric Chan 58bc08a045 Add user skills injection and picker UI 2026-04-10 20:53:39 +08:00
Rory Chou 7639191c50 fix: preserve AI chat history across reconnects (#541)
* fix: preserve AI chat history across reconnects

* fix: retarget restored AI sessions on reconnect

* feat: format tool call results with proper line breaks

Extract stdout/stderr from structured results and unescape \n/\t
so command output displays with real line breaks like terminal output.
Supports both JSON object {stdout,stderr} and executor text formats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: restrict unescape to stdout/stderr fields only

Plain strings may contain legitimate backslash sequences (file paths,
regex patterns) that should not be converted. Only apply unescape to
stdout/stderr fields extracted from command execution results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review findings for AI chat reconnect

1. Add explicit activeTerminalTargetIds guard in shouldRetargetActiveSession
   to prevent retargeting sessions owned by other terminals, making the
   invariant locally verifiable.

2. Only preserve orphaned terminal sessions with hostIds — workspace,
   local, and serial sessions generate fresh IDs and would be permanently
   unreachable, wasting MAX_STORED_SESSIONS quota.

3. Clear stale streaming state when restoring a session whose ACP handle
   was already cleaned up (e.g., reconnect during mid-response), so the
   user can send new messages.

4. Restore overflow-hidden on user message bubbles to prevent content
   bleeding past rounded border corners.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address round 2 review findings

1. Fix streaming state clear: only clear for sessions whose targetId
   doesn't match current scope (restored from different terminal),
   not for built-in Catty chats that never set externalSessionId.

2. Exclude local/serial sessions from preservation: their synthetic
   hostIds (local-*/serial-*) change on every open and can never be
   matched back.

3. Preserve non-zero exitCode in formatted tool results so failed
   commands show a visible failure signal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: only clear streaming state during retarget, not for all restored sessions

The previous condition (targetId !== scopeTargetId) also fired for
built-in Catty sessions during normal operation, killing active streams.
Now streaming is only cleared when shouldRetargetActiveSession is true,
meaning the session came from a disconnected terminal where any
in-flight response is guaranteed to be dead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address round 3 review findings

1. Clear externalSessionId during retarget to prevent stale ACP handle
   from surviving if retarget runs before orphan cleanup.

2. Only retarget in visible AI panels — hidden/background panels should
   not race to claim orphaned sessions.

3. Remove unescapeTerminalOutput — data flow trace confirms real newline
   characters arrive at the component. The unescape was corrupting
   legitimate backslash sequences in paths and patterns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: only ACP-cleanup deleted sessions, not preserved ones

Preserved sessions may be reused on reconnect. Running aiAcpCleanup
on them asynchronously could race with a newly started ACP conversation
on the same session ID, tearing down the fresh provider.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: abort in-flight streams during retarget and restore ACP cleanup

1. Abort the active request's AbortController when retargeting a session
   with stale streaming state. Prevents late chunks from the old run
   appending into the restored chat.

2. Restore ACP cleanup for all orphaned sessions (not just deleted ones).
   Preserved sessions get a new externalSessionId on next use, so
   cleaning the old one prevents subprocess leaks without affecting
   future conversations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: guard hidden panels from session ownership and skip null map entries

1. Only assign restored sessions in visible panels — hidden panels
   should not race to own sessions via setActiveSessionId, preventing
   MCP/tool calls from being bound to the wrong terminal.

2. Skip null entries in activeSessionIdMap when building
   activeTerminalTargetIds — deleted chats should not block same-host
   history matching on other terminals.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: guard MCP sync behind visibility and cancel exec/approvals on retarget

1. Only sync MCP session metadata from visible panels to prevent
   hidden panels from overwriting the scope mapping.

2. Cancel pending approvals and in-flight exec (Catty + ACP) during
   retarget, matching handleStop behavior. Prevents stale tool results
   and approval prompts from reappearing after session retarget.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: restore MCP sync for hidden panels

MCP scope is keyed by chatSessionId so hidden panels don't overwrite
visible panels' mappings. The isVisible guard was breaking background
chats that need updated terminal session metadata after reconnects
or workspace changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove unused deletedIds variable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:47:32 +08:00
陈大猫 34f9d2a663 chore: 死代码清理与架构分层修复 (#524)
* chore: 移除死代码并修复架构分层违规

- 删除未使用的 ACP 模块 (infrastructure/ai/acp/)
- 删除未使用的 AI 组件 (ExecutionPlan, PermissionDialog)
- 将 syncPayload.ts 从 domain 移至 application 层,修复分层违规
- 移除未使用的导出 (useSecurityState, useProviderStatus, GitHubAuthState,
  getAgentCommandLabel, ImageAttachment, HotkeyActions)
- 收窄 Electron bridge module.exports,移除未使用的导出函数
- 将仅内部使用的函数/类型取消导出 (isSupportedLocale, SyncDashboard)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: 二次审查清理 — 移除更多死代码和架构违规

- 移除未使用的 ConversationEmptyState 组件和类型
- 移除未使用的 PromptInputSelect 系列组件 (5 个导出)
- 移除 global.d.ts 中残留的 SMBConfig 类型和 cloudSyncSmb* 方法声明
- 移除 useAutoSync.ts 中未使用的 toast 导入 (同时修复 application→components 反向依赖)
- 清理因删除而产生的多余 import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: 消除直接 localStorage 访问,提取 safeSend 共享工具

localStorage 集中化:
- 新增 storageKeys 常量: SIDE_PANEL_WIDTH, PF_RECONNECT_CANCEL, DEBUG_HOTKEYS, DEBUG_UPDATE_DEMO
- TerminalLayer/SettingsApplicationTab/App.tsx/useUpdateCheck 改用 localStorageAdapter
- CloudSyncManager 内部方法改用 localStorageAdapter
- portForwardingService 改用 localStorageAdapter + 集中 key

safeSend 去重:
- 新增 electron/bridges/ipcUtils.cjs 共享模块
- sshBridge/sftpBridge/portForwardingBridge/sshAuthHelper/aiBridge 统一引用

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: 终审清理 — 移除未使用的 require 和废弃类型别名

- 移除 sftpBridge.cjs 中未使用的 require("node:net")
- 移除 aiBridge.cjs 中未使用的 require("node:path")
- 移除 types.ts 中已废弃的 ChatMessageImage 类型别名

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: 修复 ESLint 错误 — 组件不再直接导入 infrastructure

- 新增 useStoredNumber hook,TerminalLayer 通过 hook 访问侧边栏宽度
- SettingsApplicationTab 的 isUpdateDemoMode 改为从 useUpdateCheck hook 传入
- 移除 useCloudSync.ts 中未使用的 CloudSyncManager 导入和 GitHubAuthState 接口

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: 提取 notification port,消除 application 层对 components 的依赖

将 toast 通知抽象为 application/notification.ts 端口,
UI 层通过 setNotify 注入实现,useAutoSync 改用 notify 接口。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 14:14:37 +08:00
bincxz 4971f18bbe Fix AI terminal execution completion and tool UI 2026-03-24 22:41:40 +08:00
陈大猫 dcaf25ae57 feat: inline approval gate for tool execution (#423)
* feat: inline approval gate for tool execution

Replace SDK-level needsApproval with Promise-based approval gate inside
tool execute functions. The SDK stream stays alive while the UI shows
inline approve/reject buttons on ToolCall blocks.

Changes:
- Add approvalGate.ts: Promise-based approval system with event listeners
- tools.ts: requestApproval() inside execute for confirm mode
- tool-call.tsx: inline approval buttons and keyboard shortcuts
- ChatMessageList.tsx: subscribe to approval events, render approval UI
- useAIChatStreaming.ts: remove old useToolApproval hook integration
- AIChatSidePanel.tsx: remove old approval hook, clean up unused destructuring
- systemPrompt.ts: update confirm mode to not ask for text confirmation
- preload.cjs: filter pager env var prefixes from terminal display
- mcpServerBridge.cjs: add approval gate for ACP/MCP write operations
- aiBridge.cjs: wire IPC for MCP approval response and main window getter
- preload.cjs: add onMcpApprovalRequest/respondMcpApproval APIs

* fix: scope approval gate by chatSessionId and replay for late subscribers

Address Codex PR review comments:
- Add chatSessionId to ApprovalRequest for session isolation
- Scope clearAllPendingApprovals(chatSessionId?) to only clear
  approvals belonging to the target session
- Add replayPendingApprovals() so late-mounting ChatMessageList
  picks up approvals that fired while unmounted
- Scope MCP clearPendingApprovals in aiBridge cancel handler to
  effectiveChatSessionId instead of clearing all
- Pass chatSessionId through MCP approval IPC flow

* chore: remove old approval flow code

- Delete useToolApproval.ts (unused hook)
- Delete InlineApprovalCard.tsx (replaced by ToolCall inline buttons)
- Remove stale comments referencing old hook in AIChatSidePanel
- Remove unused ai.chat.toolApprovalTitle i18n key from en/zh-CN

* fix: session-scoped approval gate and MCP replay survival

- handleStop passes activeSessionId to clearAllPendingApprovals
- setupMcpApprovalBridge stores MCP approvals in pendingApprovals map
  so they survive ChatMessageList unmount/remount cycles
- ChatMessageList accepts activeSessionId prop and filters standalone
  MCP approval blocks to the current session only
- AIChatSidePanel passes activeSessionId to ChatMessageList

* fix: filter PTY exec marker echoes and exit code lines from terminal

Extend filterMcpMarkers in preload.cjs to strip all shell-visible
artifacts from AI command execution:

- Echoed printf start marker: printf '%s\n' '__NCMCP_..._S'
- Echoed exit code restoration: (exit $__nc)
- PowerShell: Write-Output, $global:LASTEXITCODE, $__nc assignment
- Fish: set __nc $status
- Cmd: echo __NCMCP_...
- Widen guard to also trigger on __nc and PAGER=cat strings

* fix: scope SDK approvals, deny MCP on no renderer, fix memo comparator

- createCattyTools accepts chatSessionId and passes it to
  requestApproval so SDK approvals can be matched by
  clearAllPendingApprovals(activeSessionId) on stop
- useAIChatStreaming passes sessionId to createCattyTools
- mcpServerBridge: deny (resolve false) when no renderer window is
  available instead of auto-approving, preserving confirm mode safety
- ChatMessageList: add activeSessionId to React.memo comparator so
  switching sessions triggers re-render for correct MCP approval filter

* fix: MCP listener lifecycle, approval timeout, and UI sync on stop

- Move setupMcpApprovalBridge from ChatMessageList to AIChatSidePanel
  so the IPC listener survives tab/panel switches
- Add 5-minute auto-deny timeout to requestApproval to prevent
  indefinite isStreaming hangs when user walks away
- Add onApprovalCleared listener system: clearAllPendingApprovals now
  notifies UI subscribers so ChatMessageList removes stale cards
- ChatMessageList subscribes to onApprovalCleared to sync local state

* fix: main-process approval timeout and full tool args in payload

- Add 5-minute auto-deny timeout to requestApprovalFromRenderer
  matching the renderer-side requestApproval behavior
- Forward all tool params (excluding chatSessionId) to approval UI
  instead of cherry-picking command/input/path, so sftpRename
  oldPath/newPath and other tool-specific args are visible

* fix: move MCP bridge to TerminalLayer, narrow terminal filter guard

- Move setupMcpApprovalBridge from AIChatSidePanel to TerminalLayer
  so the IPC listener stays alive regardless of side panel tab.
  AIChatSidePanel only mounts when activeSidePanelTab==='ai'.
- Narrow preload.cjs filter guard back to __NCMCP_ only, preventing
  false-positive stripping of user scripts containing __nc or PAGER=cat

* fix: eliminate PTY wrapper echo leakage and duplicate prompts

- Posix wrapper now emits 2 lines instead of 4: start marker + command
  on line 1 (joined with ;), end marker + exit on line 2. This
  eliminates the duplicate prompt echo from the separate start marker.
- Rename __nc to __NCMCP_rc in all shell variants (posix/fish/powershell)
  so every wrapper variable contains the __NCMCP_ prefix. The preload
  guard `data.includes("__NCMCP_")` now reliably catches ALL wrapper
  artifacts regardless of chunk boundaries.
- Update all filterMcpMarkers regex patterns to match the restructured
  wrapper format and renamed variable.

* fix: sync main-process approval timeout with renderer UI cleanup

- When requestApprovalFromRenderer times out, send IPC event
  netcatty:ai:mcp:approval-cleared to renderer so stale approval
  cards are removed
- Add onMcpApprovalCleared preload bridge for the new IPC channel
- setupMcpApprovalBridge now subscribes to cleared events, removes
  timed-out entries from pendingApprovals and notifies clearedListeners
  so ChatMessageList drops the stale card

* fix: surface denied inline approvals as errors in UI

- Detect error or denial payloads ("error" string or "ok: false")
  returned by tools when the user denies an execution
- Set isError: true on the tool-result message so the ToolCall UI
  renders it as a failure (red/rejected) instead of a success (green)
2026-03-20 22:02:21 +08:00
bincxz f4bbe62a1d fix: eliminate scroll bounce when switching tabs with AI chat open
StickToBottom was configured with initial="smooth", causing a visible
elastic scroll animation every time the chat panel remounted on tab
switch. Change to initial="instant" so the scroll position snaps
immediately without animation. Streaming and resize still use smooth.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:43:06 +08:00
陈大猫 4e26852482 feat: support multimodal attachments in AI chat (#397)
* feat: support multimodal attachments (images, PDFs, files) in AI chat

Previously uploaded images were displayed in the UI but never sent to
the AI model, and non-image files (PDF, text) were silently rejected.

- Rename useImageUpload → useFileUpload; accept image/*, PDF, and text/*
- Rename ChatMessageImage → ChatMessageAttachment with filePath support
- Build multimodal SDK messages (ImagePart/FilePart) for Catty Agent
- Fix ACP agent path: images inline, non-image files via local path hint
  so ACP agents (Claude Code, etc.) read them with native file access
- Use Electron webUtils.getPathForFile() for reliable file path capture
- Compact user message bubble padding

Closes #294 (AI file upload issues)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: show real tool names in AI chat instead of ACP wrapper names

- Unwrap ACP dynamic tool calls in serializeStreamChunk to extract
  real tool name, args, and toolCallId from chunk.input
- Simplify MCP tool name prefixes (mcp__server__tool → tool)
- Pass toolCallId from ACP tool-call events to match tool results
- Prevent onToolResult from overwriting correct names with wrapper name
- Build toolCallNames map in ChatMessageList for tool result display

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: backward-compatible fallback for legacy `images` field in chat messages

Persisted sessions may still have `images` instead of `attachments`.
Add `?? m.images` fallback in SDK message builder and renderer so
historical image attachments are not silently dropped after upgrade.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: broaden file type support and handle pasted files without path

- Accept all file types except video/audio (instead of allowlist)
  so .json, .yaml, .sh, etc. are not silently rejected
- For ACP agents, save pasted/virtual files (no filePath) to temp
  directory so the agent can still read them

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use managed temp dir for pasted ACP attachments

Use tempDirBridge.getTempFilePath() instead of manual os.tmpdir() path
so pasted file attachments are tracked by the app's cleanup system.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:45:50 +08:00
陈大猫 3d4fbf8763 fix: keep workspace MCP scope in sync (#385)
* fix: keep workspace MCP scope in sync

* fix: refresh catty workspace tool context

* fix: preserve AI stream state across tab switches

* fix: align ACP stop and resume with 1code semantics

* fix: harden ACP resume fallback for unsupported agents
2026-03-18 03:33:00 +08:00
bincxz f90aa81b2c feat: enforce safety settings, blocklist UI, provider UX improvements, and full i18n
Safety enforcement:
- Command Timeout: use user setting in MCP Server (execViaPty/execViaChannel)
  and Catty Agent path (aiBridge execViaPtyForCatty/execViaChannel fallback)
- Max Iterations: read user setting for Catty (stepCountIs) and ACP (via IPC)
- Permission Mode: observer mode hard-blocks write ops in MCP Server dispatch;
  Catty SDK tools wired to checkToolPermission(); all synced via IPC on change

Command Blocklist UI:
- Add editable regex pattern list in Settings AI Safety section
- Reset to defaults button, add/remove patterns
- IPC sync to MCP Server on change and on mount

Provider UX:
- ModelSelector rewritten as combobox: type-to-filter with suggestions dropdown
- All providers use unified ModelSelector (modelsEndpoint optional)
- API key passed as auth header for model fetching (Bearer / x-api-key)
- Skip model fetch when no API key (except Ollama)
- Provider toggle is now mutually exclusive (activating one disables others)
- New providers default to disabled (switch off)
- Custom provider supports editable display name
- No-provider error: friendly message shown in chat

i18n:
- Full i18n coverage for Settings AI tab (~55 keys)
- Full i18n for AI chat panel (placeholder, empty state, time, sessions)
- en.ts and zh-CN.ts locale files updated

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:05:41 +08:00
bincxz 8949394756 feat: add Claude Agent SDK streaming and Codex OAuth integration
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>
2026-03-14 21:27:41 +08:00
bincxz 7f3214e088 feat: integrate external AI agents via ACP protocol
- 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>
2026-03-14 17:21:28 +08:00
bincxz 72887c35b5 feat: add AI chat panel with Vercel AI SDK integration
Add a comprehensive AI assistant feature to netcatty, enabling AI-powered
terminal automation and multi-host orchestration.

Core features:
- AI chat side panel (Zed-style) with agent selector, session history,
  conversation export (Markdown/JSON/TXT), and streaming responses
- Catty Agent: built-in terminal assistant with 9 tools (terminal exec,
  SFTP read/write, multi-host orchestration) using zod schemas
- BYOK provider support: OpenAI, Anthropic, Google, Ollama, OpenRouter,
  and custom OpenAI-compatible endpoints
- Three-tier permission model: Observer / Confirm / Autonomous
- Command safety: blocklist patterns, doom loop detection, abort support
- External agent support: ACP protocol (JSON-RPC over stdio) for
  Claude Agent, Codex CLI, Gemini CLI, and custom agents

Tech stack:
- Vercel AI SDK (ai, @ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google)
  with streamText + fullStream for streaming tool-call loops
- AI Elements: adapted conversation (use-stick-to-bottom), message
  (streamdown markdown), prompt-input (InputGroup) components
- Custom bridge fetch adapter routing all API calls through Electron
  main process IPC to avoid CORS
- zod for tool parameter schemas

UI components:
- AIChatSidePanel, AgentSelector, ChatInput, ChatMessageList
- PermissionDialog, ExecutionPlan, ConversationExport
- AI Elements: conversation, message, tool-call, prompt-input
- New UI primitives: InputGroup, Spinner
- Settings AI tab for BYOK configuration and safety settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:22:28 +08:00