Rowboat code sessions (copilot orchestrating the coding agent) now run on the
new sessions/turn runtime instead of the retired LLM run path.
Core:
- TurnEvent gains code-run-event + code-run-permission-request variants;
ComposeContext + SendMessageOptions gain codeCwd + codePolicy.
- The agent loop threads codeCwd/codePolicy from the turn's composeContext into
ToolRunContext; RealToolRunner passes them to code_agent_run and stops
deferring the code-run events — it forwards them onto the turn's event stream.
- turnToChatState/applyOverlay accumulate per-tool code-run events + the pending
code permission and attach them to the code_agent_run tool call (live-only;
a completed turn collapses to the tool result).
Code-mode:
- CodeSessionService.create makes a rowboat session a real sessions row (id
shared) so sessions.sendMessage drives it; delete() also removes that row.
- sessions:sendMessage IPC handler pins the coding agent's agent/cwd/policy from
the code session meta (server-side source of truth), mirroring the old
createMessage override.
Rowboat renders in the main chat (App.tsx binds the tab's runId to the code
session id — preserved from dev); the code_agent_run tool card shows the agent's
plan/diffs/permission via the existing CodingRunBlock. Direct mode is unchanged
(its own runtime from step 1).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebased new-runtime onto dev (which added code-mode on the old runs-based
runtime). Reconciliation: kept the generic event-log + bus (runs/repo.ts,
runs/bus.ts, a trimmed runs.ts = createRun + fetchRun only) decoupled from the
retired LLM runtime; restored the runsRepo DI registration and the
bus -> runs:events forwarder.
Step 1 of the code-mode migration — direct mode on its own dedicated runtime:
- new SQLite-backed CodeEventStore (migration 0008 + code_session_events) replaces
the runs JSONL log for code sessions
- dedicated codeEventBus (InMemoryBus) + codeSession:events feed +
codeSession:getEvents history channel, replacing the shared bus / runs:events /
runs:fetch for code
- CodeSessionService mints its own id (drops createRun) and writes to
codeEventStore / codeEventBus; status-tracker subscribes to codeEventBus
- renderer (use-code-chat) loads history via codeSession:getEvents and streams via
codeSession:events
Rowboat mode is temporarily disabled in the UI (its old copilot-LLM path is
retired); it moves onto the new sessions runtime in step 2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
shared/ipc.ts: add sessions:* channels (create / get / list / sendMessage /
getHistory / listTurns / getTurn / respondToPermission / setToolResult /
resumeTurn / stopTurn / delete) and the sessions:events feed; remove the runs:*
channels.
main:
- register the sessions handlers and forward the turn event bus to renderer
windows; getAgentRuntime() at startup
- stop in-flight headless runs via stopTurn (live-note / bg-task)
- drop the runs watcher, runs:* handlers, and the dev test-agent script
renderer:
- single global session-feed consumer; useSessionChat(sessionId) hook; pure
turn -> chat-state mappers (agent-turn-view, session-chat-state); shared
ChatConversation component
- chat (main view + sidebar) renders from the session feed; per-turn model /
permission mode; bg-task and live-note detail views load transcripts via
sessions:getTurn; chat delete via sessions:delete
- remove the dormant run-event path (handleRunEvent + runs:events) and its
orphaned state
- vitest + jsdom test setup
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the old run-based agent runtime with an append-only, fact-log turn model.
agent-loop:
- turn = append-only fact logs; advance() reducer IS the resume function
- SystemComposer (loop-injected system prompt) + TurnObserver (live deltas +
state snapshots), per-turn ComposeContext (voice / search / code-mode) and
use-case attribution; per-step model usage; transcript prefix dedup at rest
sessions:
- ordered turn chains with copy-forward transcript; deleteSession support
agent-runtime bridges:
- RealToolRunner, RealPermissionGate (per-turn use-case), Copilot system /
user-message-context composers, TurnEventBus
- getAgentRuntime() process singleton; runHeadlessAgent() runs one-shot headless
work as a standalone turn (sessionId null) — no throwaway sessions, no growth
shared contracts (@x/shared): agent-turn.ts, sessions.ts (browser-safe)
headless consumers ported to standalone turns with use-case tagging: live-note,
background-task, knowledge pipelines (agent_notes / tag_notes / label_emails /
build_graph / inline_tasks), pre-built, agent-schedule.
retire the old runtime: delete runs/{runs,bus,repo}.ts and message-queue.ts; gut
agents/runtime.ts to loadAgent + convertFromMessages; trim agents/utils.ts; drop
the DI registrations for the old runtime / message queue / runs repo.
SQLite migrations: compose_context, model_usage, transcript dedup, use_case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three follow-ups from the runtime design review:
Transcript prefix dedup (storage-level, transparent):
- A session turn's input starts with the previous turn's closed transcript
(copy-forward); storing it again made session storage quadratic and every
fact append rewrote the whole transcript. Stores now keep only the suffix
past prefix_length and recompute the prefix on read from the immutable
previous turn (closedTranscript moved to agent-loop/types.ts; shared
split/join helpers in prefix-dedup.ts with loud tripwires).
- Opportunistic: input that doesn't extend the previous transcript (e.g.
future compaction summaries) falls back to whole-row storage. Nothing
above the stores changed; InMemoryTurnStore mirrors SQLite exactly.
- Migration 0005 adds prefix_length (default 0 = stored whole).
Per-model-call usage recording:
- New modelUsage fact log on the turn: one entry per model call, committed
in the same write as the assistant message it paid for. ModelAdapter
results now carry {message, usage}; VercelModelAdapter treats usage
reporting failures as null rather than failing a completed step.
- totalUsage() derives turn aggregates; null means never reported.
- Migration 0004 adds the model_usage column.
EventStream: replay removed, bus-style live delivery:
- Events go to consumers attached at push time and are dropped otherwise,
matching the runtime's IBus philosophy (facts persisted, deltas cosmetic,
renderer reconciles from snapshots). No buffering without consumers;
per-iterator queues bounded by consumer lag. Iterators attach
synchronously at creation so the loop's own for-await never misses events.
Also: dispatched-call closures get honest wording ("may have completed
externally") distinct from interrupted and never-ran; SqliteTurnStore.update
keeps the no-op write guard alongside the prefix_length read.
8 new tests (87 total).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A session is a grouping label (id, agentId, title) over an ordered chain
of self-contained turns; the loop itself never learns sessions exist.
- sendMessage builds each turn's input by copying the previous turn's
full transcript forward (copy-forward history) and only ever chains on
TERMINAL turns: anything running, waiting, or crashed-idle must be
resolved or stopped first. Terminal turns are immutable, so the copied
snapshot can never go stale or be re-activated.
- stopTurn now persists the stop as a terminal turn error (code
"stopped") instead of leaving the turn idle/resumable; resumeTurn,
setToolResult, and respondToPermission all reject terminal turns.
Unresolved tool calls on a stopped turn are closed out with synthetic
ToolMessages when history is copied forward — never re-executed.
- Turn rows gain sessionId/sessionSeq (opaque to the reducer) with a
UNIQUE(session_id, session_seq) index as a fork tripwire; createTurn
is now async and persists the row before returning so a claimed seq is
visible the moment the caller holds the handle.
- New sessions/ module: Session types, SessionStore (in-memory +
SQLite), SessionsImpl facade with a per-session KeyedMutex (extracted
from the loop's TurnMutex). getHistory returns the same closed-out
transcript the next turn will actually send.
- Migration 2026-06-12_0003_sessions: sessions table + additive turn
columns. Provider/model/permissionMode deliberately flow per
sendMessage call and land on the turn row, not the session.
- 21 new vitest tests (79 total): copy-forward, supersede-after-stop,
crashed-turn blocking, terminal-mutation guards, concurrent-send
serialization, SQLite round-trips and unique-seq enforcement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New @x/core module (src/agent-loop) — groundwork for replacing the JSONL
event runtime. Development-only; not wired into the app yet.
- Durable unit is a turn: append-only fact logs (messages, permission
requests/decisions, started/dispatched tools) + set-once error/completedAt;
status is derived from facts, never stored
- advance() reducer drives the loop purely from persisted state; crash
recovery = resumeTurn() re-entering the reducer
- Permission gating as data: batched requests, user/classifier decisions
with reasons, denials materialized as ToolMessages
- TurnHandle API per entry point: stream events or await the rest state
- Kysely migration (agent_loop_turns) + SqliteTurnStore with zod-parsed
reads; InMemoryTurnStore for tests
- VercelModelAdapter over the existing createProvider()/streamText
- 33 vitest tests: reducer, permissions, classifier, crash recovery,
abort, concurrency, SQLite round-trips
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Introduce a core-owned Kysely SQLite storage layer backed by
$WorkDir/db/rowboat.sqlite, with startup initialization, shutdown
cleanup, in-code migrations, and initial storage metadata schema.
Ignore database files in the workspace watcher, add focused
storage/watcher tests, and update Electron packaging to stage and rebuild
better-sqlite3 against Electron's native module ABI.
On macOS, the first getUserMedia({audio:true}) call hits TCC permission
status 'not-determined' — the OS prompt appears but the in-flight call
rejects, is silently swallowed, and the UI snaps back to idle. Second
click works because permission is already granted.
Fix: add voice:ensureMicAccess IPC channel (mirroring the existing
meeting:checkScreenPermission pattern) that calls
systemPreferences.askForMediaAccess('microphone') before getUserMedia,
so the same first click proceeds once the user grants access.
Also fixes a secondary bug: on the failure path, the code only called
setState('idle'), leaking the WebSocket that connectWs() had already
opened. Now calls stopAudioCapture() for proper cleanup.
* fix: add track-state polling for macOS meeting auto-stop
On macOS (ScreenCaptureKit/Electron 39) the system-audio track does not
fire 'ended' or 'mute' events when the meeting ends. Add a 3-second
polling interval that checks track.readyState and track.muted directly.
Auto-stops after the track is muted for 3 consecutive polls (~9 seconds),
or immediately if readyState becomes 'ended'. Windows behavior unchanged
(existing 'ended' event listener kept intact).
* fix: gate macOS meeting muted-poll auto-stop on scheduled calendar end
On macOS the system-audio track reports muted=true both when the meeting
ends and during any silent stretch of a still-live meeting, so the
unconditional ~9s muted hard-stop could cut a quiet-but-live meeting short
with no warning.
Only hard-stop on sustained mute once we're past the linked calendar
event's scheduled end (a strong "it's really over" signal); otherwise let
the existing silence nudge + backstop handle it. Gate the whole poll to
macOS — Windows already auto-stops via the track "ended" event — and drop
the noisy per-poll debug log.
---------
Co-authored-by: Gagancreates <gaganp000999@gmail.com>
* feat: render background-task index.html output in a sandboxed iframe
The task output pane now prefers bg-tasks/<slug>/index.html when present and
non-empty, rendering it full-bleed via HtmlFileViewer (app://workspace
protocol) so CSS, layout, and scripts render faithfully. Falls back to the
markdown index.md note when there is no HTML artifact. The viewer remounts on
refreshKey so a re-run's updated HTML reloads. The Source/Rendered toggle works
for both formats.
The runner agent is instructed to choose index.md (default, notes) vs a
self-contained index.html (visual/styled output) per run, written via the
existing file-writeText tool. The Copilot background-task skill notes the HTML
option so visual asks are steered toward it.
* fix: open links from HTML report iframes in the system browser
Links inside the sandboxed iframe that renders a background-task/workspace
index.html did nothing on click, unlike the markdown viewer which opens links
in the browser.
Two causes: target="_blank" links were blocked by the sandbox before reaching
the window-open handler, and plain links fire will-frame-navigate (subframe),
which the app did not handle (will-navigate only covers the main frame).
- Add allow-popups to the HtmlFileViewer iframe sandbox so target="_blank"
reaches setWindowOpenHandler, which routes to shell.openExternal.
- Handle will-frame-navigate in main, routing external subframe navigations to
the system browser. Scoped to app://workspace frames so third-party note
embeds (YouTube/Figma/Twitter) keep their internal navigation.
Adds a gmail:searchContacts IPC channel backed by two indices: a
SENT-label API-backed index (gmail_sent_contacts) for full historical
coverage of people you've actually emailed, and a local-snapshot
fallback (gmail_contacts) used until the SENT sync finishes on first
launch. Both indices warm at startup so the first keystroke in the
recipient box is instant. Renderer wires the suggestions into the
to/cc/bcc fields in email-view with styled chips.
Co-authored-by: arkml <6592213+arkml@users.noreply.github.com>
Custom Electron Forge maker that wraps makepkg to produce a
.pkg.tar.zst with /opt/<app>, /usr/bin wrapper, .desktop entry,
and hicolor icon. Only activates on Linux when makepkg is present,
so other platforms are unaffected.
Detect silence from raw mic+system audio armed at recording start, add a quiet-meeting stop nudge, shorten the window once past the calendar end time, and stop instantly when the shared call window closes.
* 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.
Pin Electron release builds to Node 24.15.0, the last known-good runner version for Windows/Linux packaging, and fail artifact upload when out/make is empty so successful jobs cannot hide missing release assets.
Move volatile current time and middle-pane data out of the system prompt and into a hidden userMessageContext stored on each user message. Reconstruct the LLM-facing message from this persisted context so older conversation turns remain stable across later requests while UI-facing content stays unchanged.
Keep finite branch instructions such as voice, search, code mode, agent notes, and workdir behavior in the system prompt so each conversation can still benefit from reusable prompt-cache prefixes.
* 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