* fix: prevent chat bar model selector from overflowing in narrow panel
* fix: contain chat bar left items so code pill clips instead of overflowing
* fix: compact icon-only mode for chat bar when panel is narrow
* fix: dynamic compact threshold based on visible toolbar items
* fix: use actual DOM overflow detection to eliminate toolbar overlap
* fix: progressive right-to-left icon collapse for chat toolbar
* fix: instant icon switch, remove search label transition
* fix: correct right-to-left collapse order (code→perm→search→workDir)
* fix: measure actual DOM overflow instead of estimating — eliminates half-text and disappearing icons
* refactor: replace JS overflow logic with CSS container queries
Drop the ResizeObserver/useLayoutEffect collapse machinery and the
estimated pixel thresholds in favor of declarative @container variants.
Each toolbar item swaps to icon-only at a fixed container-width
breakpoint (code 560, perm 460, search 410, workDir 370px), collapsing
right-to-left. Atomic swaps mean no half-clipped text and no
disappearing buttons.
* fix: move @container to card root so breakpoints track panel width
Putting container-type on the toolbar's own flex row made it stop
stretching to fill the card and hug its collapsed content instead, so
the query read a permanently-narrow width that never grew on widen.
The card root reliably spans the full panel width.
* fix: collapse toolbar by measuring real overflow, not fixed breakpoints
Fixed container-query breakpoints can't know the workdir name length or
model name width, so labels stayed full and overflowed into the model
selector. Replace with overflow measurement: a ResizeObserver resets to
full on any width/content change, then a pre-paint layout effect collapses
items right-to-left (code -> perm -> search -> workdir) until the row fits.
overflow-hidden on the group is a hard guarantee against any overlap.
* feat: overflow menu for toolbar items that don't fit even as icons
When the bar is too narrow to show every control as an icon, the
right-most items move into a '...' overflow dropdown (code -> perm ->
search -> workdir) instead of being clipped, so no icon is ever hidden.
Toggle items keep the menu open on click via onSelect preventDefault.
* fix: keep overflow menu open when toggling items inside it
Toggling an in-menu item (code mode, agent, search, perm) updated state
that was in the collapse-reset deps, resetting collapseLevel to 0 and
unmounting the '...' trigger mid-interaction. Drop the in-place toggles
from the reset deps so the menu stays open on click.
* fix: drop 'Options' label from toolbar overflow menu
---------
Co-authored-by: arkml <6592213+arkml@users.noreply.github.com>
* feat(code-mode): add ACP client engine (Layer 2 core)
Own the Agent Client Protocol client instead of shelling out to `acpx`, so code
mode can stream structured events (tool calls, diffs, plan) and surface live
permission requests. Headless acpx can't do live approvals (it only supports
--approve-all), which is why we drive the agent adapters ourselves.
- code-mode/acp/{agents,client,permission-broker,session-store,manager,types}.ts:
headless engine driving the Claude/Codex ACP adapters; one warm session per chat
with create-or-resume via session/load; approval policy (ask | auto-approve-reads
| yolo) in the broker.
- claude-exec.ts: cross-platform claude resolver (Windows .cmd EINVAL fix + macOS/Linux
GUI-PATH safety net) shared with the legacy acpx path in builtin-tools.ts.
- add @agentclientprotocol/sdk + claude/codex adapters to core.
* feat(code-mode): route code mode through code_agent_run tool + live approvals
Replace the acpx shell-out with a structured code_agent_run tool that drives the
ACP engine directly, streaming the agent's tool calls / diffs / plan into the chat
and surfacing permission requests inline.
- shared: code-mode.ts zod schemas; add code-run-event + code-run-permission-request
RunEvent variants (stream to the renderer over the existing runs:events channel);
codeRun:resolvePermission IPC channel.
- core: CodePermissionRegistry (promise-based mid-run approvals — the LLM tool-loop's
pre-call gate can't model a mid-execution wait); register codeModeManager +
codePermissionRegistry in awilix.
- core: code_agent_run builtin tool (streams via ctx.publish, asks via the registry,
cancels on ctx.signal, returns the agent summary). CodeModeConfig.approvalPolicy
(ask | auto-approve-reads | yolo; default ask). Exclude the tool from the headless
background-task / live-note / inline-task agents so they can't block on an approval.
- main: codeRun:resolvePermission handler -> registry.resolve.
- rewrite the code-with-agents skill and the runtime "Code Mode (Active)" block to call
code_agent_run instead of emitting npx acpx commands.
* feat(code-mode): render coding runs inline (live timeline + permission card)
Render a code_agent_run tool call as a live CodingRun block instead of generic
tool output: the agent's text, tool-call rows (kind icon + status + changed-file
names from diffs), a plan checklist, and resolved-permission lines — plus an
inline Allow / Always-allow / Deny card wired to codeRun:resolvePermission.
- chat-conversation.ts: ToolCall carries codeRunEvents + pendingCodePermission;
code_agent_run is excluded from tool-grouping so it renders standalone.
- App.tsx: handle code-run-event / code-run-permission-request, clear the pending
card on tool-result, handleCodePermissionResponse, render via CodingRunBlock.
* fix(code-mode): run the ACP adapter as Node under Electron + resolve it from main
Two runtime failures that only surfaced inside the packaged/bundled Electron app
(the headless harness used real node, so neither showed there):
- "ACP connection closed": the main process spawns the adapter via
process.execPath, which inside Electron is the Electron binary, not node — so
the child never ran as Node and its ACP stdio stream closed immediately. Set
ELECTRON_RUN_AS_NODE=1 on the adapter env (a no-op under real node).
- "Cannot find module '@agentclientprotocol/claude-agent-acp'": the adapters were
transitive (core) deps, unreachable from the esbuild-bundled main.cjs. Add them
as direct deps of the main app so require.resolve finds them at runtime (and so
they ship when packaged).
Also capture the adapter's stderr + exit code and enrich connection errors, so a
future failure reports the real cause instead of the opaque "ACP connection closed".
* chore(code-mode): remove dead acpx code paths and stale copy
Code mode now runs through the code_agent_run tool (owning the ACP client), so the
legacy acpx shell-out paths are dead. Remove them:
- core: envForCommand (acpx-only CLAUDE_CODE_EXECUTABLE injection) from
executeCommand; getCodeModeCommandLabel (acpx run-status label).
- renderer: the acpx-detection "switch agent / auto-flip the code-mode chip" flow —
App.tsx executeCommand detection, the permission-request onSwitchAgent button +
badge, and the composer's code-mode-detected listener.
- copy: Settings -> Code Mode and the code-with-agents skill summary no longer
mention acpx; tidy stale comments (claude-exec, command-executor).
No behavior change for code mode; the general executeCommand tool is unaffected.
* feat(code-mode): approval-policy selector in Settings
Surface the approval policy (Ask every time / Auto-approve reads / YOLO) in
Settings -> Code Mode, instead of being config-file only. The broker already
reads CodeModeConfig.approvalPolicy; this plumbs it through the
codeMode:getConfig / setConfig IPC + main handlers and adds the picker UI
(with a one-line explanation of each level). Defaults to "ask".
* fix(code-mode): harden ACP engine — turn-scoped connections, chip-authoritative agent, reliable stop
Three robustness fixes that co-modify manager.runPrompt and the code_agent_run
tool, so they land together:
- Lifecycle: scope each ACP adapter connection to the agent turn. Dispose it a
short grace (60s) after the turn ends instead of holding it for the app's life;
the next turn resumes via session/load (both agents support it). Wire
disposeAll() on app quit (was dead code). Fixes the unbounded per-chat leak of
booted agent processes.
- Agent selection: make the composer chip the source of truth. Thread codeMode
into ToolContext; code_agent_run uses it instead of the model's guessed `agent`
arg, which anchored on the thread's earlier agent and ignored a chip change.
Prompts updated to match; the run is labelled by the agent that actually ran.
- Stop/abort: guarantee a stopped turn unwinds. On abort the manager sends ACP
session/cancel, then force-kills the adapter after a 2s grace and resolves the
turn as cancelled — a wedged adapter can no longer hang the run and lock the
chat. code_agent_run returns a clean cancelled result.
* fix(code-mode): hide Codex's native console window on Windows
Codex's engine ships as a native console-subsystem binary (codex.exe). Launched
from our console-less Electron process tree, Windows allocated a fresh *visible*
console window for it; closing that window wedged the run in a pending state.
(Claude Code is a Node CLI, so it never triggers this.)
The window is created by @openai/codex's launcher (bin/codex.js), which spawns
codex.exe with no windowsHide. Patch it via pnpm to pass windowsHide: true
(CREATE_NO_WINDOW) so the console stays hidden — no window, nothing to close.
* refactor(code-mode): move ACP session files out of WorkDir/config
Per-run ACP session state is runtime state that accumulates one file per
chat run, not user/app config. Relocate it from WorkDir/config to a
dedicated WorkDir/code-mode/sessions/ so it can be listed, cleaned up, and
managed on its own without crowding config. Drop the now-redundant
codesession- filename prefix (the directory conveys it).
- add LLM-based auto permission classifier for permission-gated tool calls
- store run-level permission mode and auto permission decision events
- auto-approve low-risk calls, and bubble auto-denied calls to manual approval
- show auto-denied reasons in chat and auto-approved labels below tool cards
- add BYOK setting for the auto-permission decision model
Add a DocxFileViewer (via @eigenpal/docx-editor-react) wired into the file-type viewer switch, reading/saving bytes through the existing base64 workspace IPC with debounced autosave.
* feat: add in-chat code mode toggle with claude/codex swap
* feat: show agent and add swap-and-retry on acpx permission card
* style: reorder permission card buttons (approve, deny, swap)
* feat: add tooltips to composer plus and web search buttons
* feat: add code mode settings tab with agent install/auth checks
* feat: show sign-in command when agent installed but signed out
* style: refine code-mode permission and command block UX
- Render permission block before the command block
- Collapse permission details after a response; click header to expand
- Drop status icons/badge; use minimal green / bold red blocks
- Auto-collapse the running command block once it completes
* feat: rotating progress labels for code-mode commands; darker tool borders
- Code-mode (acpx) command block shows status-aware labels: rotating
'Working on the task…' phrases (5s each, holding on the last) while
running, then 'Completed the task' / "Couldn't complete the task"
- Darken outer border on all tool blocks in light and dark modes
* fix: detect Claude Code sign-in via macOS Keychain
On macOS, Claude Code stores OAuth credentials in the login Keychain
(service 'Claude Code-credentials'), not in ~/.claude/.credentials.json.
Read the Keychain as a fallback so signed-in Mac users are detected.
* feat: persistent per-chat sessions for code-mode coding agents
- Use a named acpx session (rowboat-<runId>) per chat so follow-up
coding requests resume the same agent and keep context
- Create the session once at chat start (sessions new --name), then
prompt with -s <name>; reuse on follow-ups (no re-create)
- Drop the redundant in-chat 'reply yes' confirmation (the executeCommand
permission card is the confirmation)
- Code-mode output uses plain-text paths (overrides global filepath rule)
- On not-installed/auth errors, point user to Settings -> Code Mode
* fix: code-mode session creation uses idempotent ensure, run sequentially
- Use 'sessions ensure --name' instead of 'sessions new' so reopening a
chat resumes the existing session instead of erroring on a name clash
- Create the session and run the prompt as separate sequential calls so
the permission/command blocks render one at a time (not all at once)
* fix: reliable Claude Code session resume on Windows (avoid claude.cmd EINVAL)
Resuming a code-mode chat after restarting the app spawns a fresh ACP
agent. On Windows + Node >=20.12 the bridge spawning claude.cmd throws
EINVAL, so the session queue owner fails to start. Rowboat injects
CLAUDE_CODE_EXECUTABLE=claude.exe to dodge this, but the override didn't
reliably reach the spawn. Windows-only; no-op on macOS/Linux.
- executeCommand now accepts an env override and the non-abortable
fallback path passes it through (was silently dropped)
- resolveClaudeExeOnWindows also scans known npm/pnpm/volta global bin
dirs, not just PATH (Electron's runtime PATH can omit them)
- add --timeout 600 to acpx prompt commands so a genuine stall fails
cleanly instead of hanging on 'Running' forever
* feat: roll web search sources in one-by-one with settle animation
* fix: keep web search toggle on for the rest of the chat session
* feat: redesign collapsed web search card with favicon stack and source summary
* style: tune web search card surface tints for light and dark mode
* feat: rounder web search card with subtle expand/collapse animation
* feat: apply web search card design to tool-call box with action summary
Shared --card-surface token, rounded card, hover, collapse animation, and a state-driven lead icon (spinner/check/cross). Single tools and the group now match. Completed group shows 'Ran N tools · <up to 2 actions>, more...' with the action summary in lighter gray.
* style: drop lead icon from tool group child rows and round them more
Replace workspace-scoped builtin file tools with general-purpose file-* tools that accept relative, absolute, and ~/ paths. Relative paths still resolve against the configured workdir.
File operations within the workdir are auto-approved. File operations outside the workdir now emit file permission metadata and require user approval, with support for once, session, and persistent grants.
Add a shared filesystem layer for text-focused read/write/edit/list/search operations, including binary-file safeguards for text reads. parseFile and LLMParse continue to read file buffers for document/image parsing.
Update copilot prompts, background/live-note agents, knowledge workflows, and renderer labels/UI to use the new file-* tool surface and permission details.
Add package-local Vitest setup for @x/core with colocated filesystem unit tests covering path resolution, canonical permission paths, binary detection, read/write/edit behavior, glob, and grep.
* added send, archive and delete
* fix scopes
* added replyall, cc, bcc etc
* - Added scope-aware Gmail status via gmail:getConnectionStatus, so the email empty state can
distinguish “not connected” from “connected but missing new Gmail scope.”
- Hardened Gmail send header construction against CR/LF header injection.
- Switched MIME parts from invalid UTF-8 7bit bodies to base64-encoded UTF-8 parts.
- Made forward send as a new message instead of attaching it to the original thread, and included
forwarded message content.
- Changed archive/delete UI behavior to remove the thread only after Gmail confirms success.
---------
Co-authored-by: Ramnique Singh <30795890+ramnique@users.noreply.github.com>
* fix discord users feedback
* fix: drop duplicate Link extension and scroll headings to viewport top
* fix: collapse ../ segments in note-relative file links
---------
Co-authored-by: Arjun <6592213+arkml@users.noreply.github.com>
Add the chat side-pane navigation with dock/close/open controls, refine
the Home and chat panes, show connect-account prompts in the Email and
Meetings views (Zoom/Teams/Meet), fix the dock-to-side arrow, and default
the app to Home with the chat docked on the right.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rebuild the sidebar, home, and chat surfaces per the navigation design:
Recents (capped at 5), single clickable previews, section separators, and
the chat page help items and discovery carousel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge Connected Accounts and Tools Library into a single Connections tab,
tighten sidebar spacing, move Help into Settings, and refine the Workspace
view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surface background tasks, upcoming meetings (with a live recording
indicator), and important unread email directly in the sidebar; add a
chat history page with chat icons and a home action row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move new chat to a top-bar icon with a Cmd+N shortcut, introduce the
Workspaces concept (workspace sidebar + default the working-directory
picker to it), and expand the knowledge browser (view more).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: fall back to next port when OAuth callback server can't bind 8080
On Windows with Hyper-V/WSL2/Docker, port 8080 is often reserved by the
OS (EACCES) or already in use (EADDRINUSE), making sign-in completely
impossible. The app now scans 8080–8089 and binds the first available
port. For DCR providers, a stale registration locked to a blocked port
is detected and cleared so the client re-registers on the new port.
Static-client providers (Google BYOK) keep fixed-port behaviour with a
clear error message instead of a raw Node.js exception.
* fix: keep createAuthServer fixed-port by default, opt-in fallback
Address review feedback:
- Flip createAuthServer default to fixed-port; fallback is now opt-in via
{ fallback: true }. Composio (composio-handler.ts) keeps exact-port
semantics with no code change — only the Rowboat sign-in call site,
which builds its redirect URI from the actual bound port, opts in.
- Wrap post-bind setup (DCR, PKCE, auth URL) in try/catch and close the
server on any failure so the port is released for retries.
* fix: clear stale DCR registration when bound port differs from start port
Add a read-only TipTap-backed RichMarkdownViewer and use it for Background Tasks output so rendered index.md files can display the same rich fenced blocks as notes, including email, calendar, chart, table, image, embed, transcript, and Mermaid blocks.
Keep the existing Source/Rendered toggle for raw markdown inspection, and hide editor-only delete controls in read-only output.
Move the rich block format examples out of the LiveNote-only prompt and into the shared knowledge note style guide. This gives both LiveNote and Background Task agents the same canonical renderer contract, including exact fenced-code schemas for rich Markdown blocks and the rule to avoid emitting task blocks as agent output.
Verified with:
- npm run build in apps/x/apps/renderer
- npm run build in apps/x/packages/core