Commit graph

1505 commits

Author SHA1 Message Date
arkml
470b75e1b6
code mode output sticks to bottom; mic input in direct drive; fix dark mode and minor margins (#649) 2026-06-30 23:05:39 +05:30
Arjun
e6ff631191 fix codex unresponsive 2026-06-30 22:28:15 +05:30
Arjun
f2b5c6b1ab (1) add running transcript to voice input (2) capture till end of speech when mic is used (3) notify only on important emails 2026-06-30 18:43:46 +05:30
Gagan
6cbf40b165 feat(mini-apps): refine card accent palette
- Assign card accents by grid position (adjacent cards differ)
- Add premium red + pink; drop blue/indigo/ocean purples
2026-06-30 01:28:00 +05:30
Gagan
30a46178d1 feat(mini-apps): real scoped Composio bridge + GitHub Dashboard
Phase 2 makes the Mini App bridge real instead of stubbed.

- New composio:execute-tool and composio:search-tools IPC channels
  (shared schema + main handlers reusing the agent's execute path)
- Bridge refactored to a scoped RPC: callAction/searchTools/isConnected/
  connect, enforced against each app's declared scope at the host
- Auth prompt: acting on a disconnected toolkit triggers Composio OAuth
- GitHub Dashboard sample: track repos, view their 20 most-recent open PRs
  with descriptions, plus the authenticated user's profile + contribution
  graph — all live via the bridge
- Twitter sample reverted to a local UI demo (X has no managed OAuth2)
2026-06-30 01:17:38 +05:30
Gagan
7acbf67cd4 feat(mini-apps): add Mini Apps section with sandboxed app runtime
Phase 1 of Mini Apps: a new sidebar section that renders user apps as
premium tiles and opens each in a sandboxed iframe.

- New first-class 'apps' view wired through App.tsx (mirrors bg-tasks)
- 'Mini Apps' sidebar entry
- MiniAppFrame: sandboxed iframe host + window.rowboat bridge
  (getData/onData, getState/setState, scoped callAction; actions stubbed)
- Self-contained vanilla apps (no CDN) so they render offline in the frame
- Sample apps: Twitter (X-style feed + topic filters), Newsletter Digest,
  Competitor Watch
- Premium card gallery: accent themes, decorative patterns, ambient glow,
  light/dark support
2026-06-30 00:30:29 +05:30
Arjun
753e3448f0 Show previous chats related to the workspace in the workspace section. 2026-06-29 12:27:43 +05:30
Arjun
0887ee7fc0 ability to add note to workspace 2026-06-27 13:28:56 +05:30
hrsvrn
4a26bc9d80 feat(email): faster inbox load, Ctrl+Tab tab cycling, square preset buttons
- Inbox load: add an mtime-keyed in-memory cache to listInboxPage so it no
  longer re-reads and JSON.parses every cached thread (bodies included) on
  every call — only files whose mtime changed are re-parsed. Speeds up the
  Important→Everything-else handoff, live reloads during sync, and re-opening
  the inbox tab.
- Add Ctrl+Tab / Ctrl+Shift+Tab to cycle to the next/previous tab (wraps
  around), alongside the existing Cmd+Shift+] / [ shortcuts.
- Improve bar: make the tone preset buttons square (rounded-md), left-aligned,
  with a bit of vertical gap so wrapped rows aren't cramped.
2026-06-26 15:51:04 +05:30
hrsvrn
824610f84f feat(email): restyle inbox, composer & reply to the Rowboat design system
- Inbox (gmail-shell): map the bespoke --gm-* palette onto the app design
  tokens (background/foreground/border/accent/primary), drop the unloaded
  "Inter" font override so it inherits the app font, and remove the redundant
  ".light .gmail-shell" block so theming follows :root/.dark.
- Composer: rebuilt on the shadcn Button/Input/Dialog primitives with app
  tokens; bring the AI describe-and-write bar and tone presets into reply mode.
- Auto-generated reply block (editor.css): retokenize the Gmail-blue pill
  buttons and compose surface onto app tokens; inherit the editor font.
- Add a Gmail-style "n" shortcut to open the composer from the inbox.
2026-06-26 15:51:04 +05:30
Harshvardhan Vatsa
40d684684c
feat(electron): add Cmd/Ctrl +/-/0 zoom shortcuts for the renderer (#646)
The app sets no application menu, so it relied on Electron's default menu
for the zoom accelerators — and on Linux that menu bar is suppressed by the
frameless `hiddenInset` title bar, leaving the shortcuts unhandled. Wire
them directly via `before-input-event`: Cmd/Ctrl + (+/=) zooms in,
(-/_) zooms out, and 0 resets, clamped to zoom levels -3..3.
`event.preventDefault()` stops the keystroke from leaking into the editor.
2026-06-26 14:40:34 +05:30
Harshvardhan Vatsa
41a6a8f519
Merge pull request #639 from hrsvrn/fix/compose-dark-mode
fix: respect dark mode in email compose/reply UI
2026-06-25 15:02:28 +05:30
gagan
6dbaa618e7
feat(onboarding): add Code Mode setup step (#643)
* feat(onboarding): add Code Mode setup step

Adds a new onboarding step (3, between Connect and Done; Completion moves
to 4) shown in both Rowboat and BYOK paths. It informs the user up front
that Code Mode needs Claude Code and/or Codex installed and signed in
locally (claude login / codex login), then offers a master 'Enable code
mode' toggle that reveals per-agent toggles.

Downloads do NOT block onboarding: on Continue, selected-but-not-yet-
installed engines are provisioned fire-and-forget in the main process,
and their live % surfaces in Settings -> Code Mode. Approval policy is
left at the default ('ask'); users adjust it later in Settings. The step
is skippable like Connect Accounts.

To make an onboarding-started download show its progress in Settings, the
module-level provisioning store (startProvisioning/useProvisioning) is
lifted out of settings-dialog.tsx into lib/code-mode-provisioning.ts and
shared by both.

* feat(onboarding): simplify Code Mode agent rows and refine copy

- Show just the agent name (Claude Code / Codex) with a toggle and a
  green check when ready; drop the per-row download/sign-in clutter
- Value-led description: use your existing Claude Code or Codex
  subscription inside Rowboat, signed in locally via the terminal
2026-06-24 17:30:34 +05:30
gagan
8b76c54295
fix(byok): decouple chat model picker from config.models (#642)
* fix(onboarding): BYOK saved entire model catalog as assistant models

testAndSaveActiveProvider persisted the provider's full fetched catalog
into config.models. That field is the user's curated assistant-model
list (rendered as one row per entry in Settings > Models), so every
available model showed up as a separate assistant-model dropdown. Seed
it with just the selected model; users add more from Settings.

* feat(byok): decouple chat model picker from config.models

BYOK config.models was doing double duty: it both seeded the in-chat
model picker AND was rendered as a per-entry dropdown stack in Settings.
That coupling forced a tradeoff between a clean Settings UI and full
model choice in chat, and baked a stale catalog snapshot into config.

Decouple them so BYOK matches the signed-in experience:
- Chat picker (signed-out) now lists the full live provider catalog from
  models:list, filtered to providers the user has a key/baseURL for, with
  the saved default model leading. No longer limited to config.models, so
  newly released models appear without re-saving.
- Settings assistant model collapses to a single default-model dropdown
  (removed the .map() stack + add/remove). config.models is now just the
  one default; save/load/delete logic is unchanged.
2026-06-24 16:33:40 +05:30
gagan
caa80f7c07
fix(onboarding): inline completion checkmark & widen indicator gap (#641)
- Remove the circular background and pulsing ring behind the completion
  checkmark; place the tick inline to the right of the "You're All Set!"
  heading
- Increase spacing between the step indicator and step content
2026-06-24 15:42:31 +05:30
gagan
aa347c9047
fix(onboarding): center step connector line & refresh welcome layout (#640)
* fix(onboarding): center step connector line with circles

The connector line was vertically centered against the circle+label
column, landing below the circle's center. Absolutely position the
label below the circle so the row height equals just the circle and
items-center aligns the line to the circle's center.

* feat(onboarding): refresh welcome layout & indicator spacing

- Place logo inline to the right of the Welcome heading
- Move the tagline badge below the heading with more breathing room
- Add space between the step indicator and step content
2026-06-24 15:35:22 +05:30
Arjun
7e1acedfcf remove recents from new chat 2026-06-24 12:01:10 +05:30
hrsvrn
0d069e8148 fix: respect dark mode in email compose/reply UI
The compose modal/card hardcoded light-mode CSS variables unconditionally
(introduced in 851c3be6 "gmail contacts should use light mode"), overriding
the theme variables inherited from .gmail-shell. As a result the reply/compose
box rendered as a white card with dark text even in dark mode.

Split the single hardcoded block into theme-aware blocks following the existing
.gmail-shell convention: dark values as the unprefixed default, light values
under `.light`. Light mode is unchanged; dark mode now gets a proper dark card.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 01:51:48 +05:30
PRAKHAR PANDEY
1bfda94f48
fix: suppress notification spam on app re-open (#623)
* fix: suppress notification spam on app re-open

- Add background_task notification category (default ON) so users can
  toggle off background-task pings via Settings → Notifications
- Route notify-user builtin through notifyIfEnabled gate so the
  category toggle takes effect
- Add 60s startup grace period: background-task notifications fired
  within 60s of launch are suppressed, killing the reopen flood where
  all queued agents complete at once
- Suppress new_email notifications for emails older than 5 min so
  Gmail's startup backlog replay doesn't surface day-old mail

Fixes both issues reported by Ramnique.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: skip automatic chat_completion ping for background task agents

Background task runs were triggering "Response ready / Your agent
finished responding" on every completion. Skip the automatic
chat_completion notification when finalState.runUseCase ===
'background_task_agent' — background tasks notify explicitly via
notify-user when they have something worth surfacing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: correct notification deeplinks and skip internal agent pings

- runtime.ts: also skip chat_completion ping for knowledge_sync
  useCase (agent_notes_agent was opening notes view on click)
- sync_gmail.ts: new_email notification now links to specific
  email thread (rowboat://open?type=email&threadId=...)
- App.tsx: add email case to parseDeepLink, parse threadId param,
  wire threadId through applyViewState, update viewStatesEqual

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: distinguish background-agent notifications from auto knowledge-sync

Per team clarification, two background types are handled differently:
- knowledge_sync (auto knowledge-graph generation): never notifies;
  skips the generic chat_completion ping entirely.
- background_task_agent (user-configured agents): notifies via its
  own notify-user path, gated behind the toggleable "Background
  agents" category, deep-linking to the background-tasks page.

- runtime.ts: skip the generic chat_completion completion ping for
  both knowledge_sync and background_task_agent (the latter notifies
  via notify-user, so the generic ping would duplicate it).
- builtin-tools.ts: notify-user branches on getCurrentUseCase() —
  background agents route through notifyIfEnabled('background_task')
  with a bg-tasks deeplink default; chat agents notify directly.
- App.tsx: add the bg-tasks deeplink target (ViewState, parseDeepLink,
  applyViewState, currentViewState).
- settings-dialog.tsx: rename the category label to "Background agents".
- runner.ts: wrap the background-task run in withUseCase so tools see
  the correct use-case context.

* fix: route coding-session notifications through notifyIfEnabled

Code-mode status-tracker was calling notificationService.notify() directly,
bypassing the user's notification-category toggles. Routed both calls
through notifyIfEnabled() with correct categories:
- needs-you state → agent_permission
- idle after 30s  → chat_completion

Removed now-redundant container/INotificationService plumbing.

* fix: fall back to persisted run useCase when ALS context is missing in notify-user

AsyncLocalStorage does not propagate across the background-task agent's
async generator — getCurrentUseCase() returns undefined inside notify-user,
causing the background_task_agent branch to be skipped and the Background
agents toggle to be ignored.

Fix: load persisted useCase from run record via fetchRun(ctx.runId) when
ALS is falsy. Lazy dynamic import() avoids the known module-init cycle.
Background-agent notifications now correctly respect the toggle and only
fire when the app is in the background, with deep link to the bg-tasks page.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 01:36:06 +05:30
Harshvardhan Vatsa
c0b38d46d3
Fix/GitHub workflow (#638)
* fixed workflow

* fix(forge): hoist staged ACP node_modules to stay under Windows MAX_PATH

The Squirrel/nuget Windows maker failed with 'path too long': stageAcpAdapters
rebuilt the ACP dependency closure by always nesting each dep under its parent,
producing 5-level chains (codex-acp -> open -> wsl-utils -> is-wsl ->
is-inside-container -> is-docker) whose paths hit ~260 chars on the CI runner and
blew past Windows' MAX_PATH (nuget.exe ignores long-path settings).

Hoist every package to the top-level node_modules npm-style, nesting only on a
genuine version conflict. Deepest staged path drops 177 -> 114 chars (worst-case
CI absolute ~197, well under 260). Add verifyAcpStaging() which asserts every
dependency edge resolves, in the staged tree, to the same version as the source
pnpm tree, failing the build loudly instead of shipping a broken installer.
2026-06-23 23:25:19 +05:30
PRAKHAR PANDEY
812ed9222c
Merge pull request #633 from prakhar1605/fix/filter-chat-models
Filter non-chat models from BYOK model picker
2026-06-23 18:18:00 +05:30
Prakhar Pandey
e1fae252bf feat: add out-of-credits warning to sidebar plan badge
Show a warning state + popover on the sidebar plan badge when the user is
out of credits, prompting them to upgrade. Detection comes from two existing
signals: the /v1/me billing payload (daily or monthly bucket exhausted, the
same data the Settings usage bars use) and the existing 'not enough credits'
API-error path. The warning clears on a successful cost-incurring call (LLM
finish-step or a successful voice synth) and re-fetches /me once, only when
the warning was actually showing -- no polling. Popover auto-opens on first
detection and can be dismissed while the indicator persists.
2026-06-23 18:11:42 +05:30
gagan
6ba6f494c1
fix(google-docs): move Add Google Doc into Knowledge Quick actions (#635)
Place the action in the files-view "Quick actions" row (next to New note)
instead of the header, where it sat after the dev↔drive merge. Widen
QuickAction's icon type to accept the custom GoogleDriveIcon component.
2026-06-23 03:20:17 +05:30
arkml
be81ffb27b
Drive (#583)
* add drive sync up and down

* add drive button

* google doc icon

* icon changes

* show error state with retry in google doc picker

* feat(google-docs): import and sync down as Markdown, record remote revision

* feat(google-docs): structure-preserving sync up with remote-conflict guard

* feat(google-docs): overwrite-confirm on sync conflict and last-synced indicator

* feat(google-docs): store linked docs as .docx, edit in docx editor, sync via Drive

* feat(google-docs): offer BYOK connect in picker so signed-in users can grant Drive/Docs scopes

* fix(google-docs): request full drive scope so .docx sync-up can write back

* fix(google-docs): search all drives in doc picker, log result count

* feat(google-docs): import native Docs AND uploaded .docx files from Drive

* chore(google-docs): drop dev-only test file

* feat(google-docs): use Google Picker + drive.file scope instead of full-drive listing

* fix(google-oauth): request offline access so BYOK tokens refresh

BYOK never requested access_type=offline/prompt=consent so no refresh token was issued and tokens died after ~1h; also stop handing back expired tokens and extend the connect timeout to 10m.

* feat(google-docs): pick docs via system-browser Google Picker

Runs the Picker in the user's real browser (it 403s inside Electron), sets appId so the drive.file grant attaches to the picked file, and downloads + opens the selected doc.

* feat(google-docs): managed OAuth-redirect Picker (no API key, no BYOK) (#620)

* feat(google-docs): managed OAuth-redirect Picker (no API key, no BYOK)

Adds the managed (rowboat-mode) Google Docs picker via Google's trigger_onepick
flow. The Rowboat backend runs a standalone drive.file OAuth with the company
client, renders the Picker inside the browser consent screen, and deep-links the
selection back; the desktop downloads the picked doc with the fresh drive.file
token the backend returns. No Picker API key, appId, or BYOK credentials on the
desktop.

- core: importGoogleDocWithToken downloads a picked doc with an explicit token;
  fetch/metadata helpers take an optional Drive client and share writeDocxAndLink.
  claimPickedFilesViaBackend claims the parked file ids + token from the api.
- main: google-picker-managed.ts opens the backend start URL and resolves on the
  rowboat://oauth/google/picker/done deep link; deeplink.ts routes that completion.
- ipc: google-docs:pickViaManaged.
- renderer: the picker dialog gates on Rowboat sign-in (the picker grants
  drive.file per-file, so no pre-existing connection or scope is required).

Backend contract: rowboatlabs/rowboatx-backend#7
(GET /oauth/google/picker/{start,callback}, POST /v1/google-oauth/claim-picked).

* chore(google-docs): remove the dead API-key/system-browser Picker

The managed picker replaced the only consumer (the picker dialog), so the
experimental API-key Picker is now unused. Removes:
- main: google-docs:openPicker handler (system-browser loopback Picker)
- shared: google-docs:openPicker + google-docs:getAccessToken IPC schemas
- core: getGoogleAccessToken (token plumbing for the client-side Picker)
- renderer: lib/google-picker.ts (Picker JS SDK loader)

Kept GoogleClientIdModal / google-credentials-store — still used by the
general BYOK Google connect in onboarding, connectors, and settings.

---------

Co-authored-by: Gagancreates <gaganp000999@gmail.com>
2026-06-23 03:13:58 +05:30
gagan
a12bf4837b
feat(voice): audio-reactive waveform while recording (no live transcript) (#634)
* feat(voice): show audio-reactive waveform instead of live transcript

When recording, the chat input now displays only a live waveform that
accumulates from the left and grows to full width, with bar heights
driven by real mic amplitude. The transcribed words are still captured
and submitted, just not shown while recording. New bars animate in and
flow smoothly at ~16 updates/sec.

* feat(voice): auto-gain waveform bar heights to track voice dynamics

Normalize each frame's amplitude against a running peak (instant attack,
slow release) at capture time and map it with a near-linear curve, so bar
heights accurately reflect how loud/soft the voice is regardless of mic
gain — replacing the old fixed-gain sqrt curve that saturated near max.
2026-06-23 02:40:56 +05:30
Arjun
de7d6b7a10 assistant notes below each note 2026-06-22 22:35:54 +05:30
Prakhar Pandey
1368d84bac fix(onboarding): filter non-chat models from provider list using models.dev 2026-06-22 12:49:48 +05:30
Prakhar Pandey
30373765e7 Merge remote-tracking branch 'upstream/dev' into feat/byok-multiple-keys 2026-06-22 12:03:40 +05:30
PRAKHAR PANDEY
51ca8778b5
Merge pull request #629 from prakhar1605/feat/byok-onboarding-simplify
Simplify BYOK onboarding to provider + key (tasks 4 & 6)
2026-06-22 11:59:55 +05:30
gagan
45188e7c1c
feat(code-mode): per-session model + effort, and keep output on nav (#632)
Two improvements to the Code section:

- Fix: leaving the Code section and returning no longer drops the open
  session's output. The selected session id is persisted to localStorage
  (mirroring the terminal-height pattern) and restored on remount, so the
  right-hand chat pane re-binds instead of falling back to the empty state.

- Feature: choose the coding agent's model and reasoning effort per session.
  Choices are discovered live from the engine (the same list `/model` shows)
  via a new `codeMode:listModelOptions` IPC, cached per agent — never
  hardcoded, so they track whatever the provider currently offers. Claude
  exposes model + effort as separate axes (with explicit Opus/Sonnet/Haiku
  alias rows surfaced for clarity); Codex folds effort into the model id and
  reports no separate effort. Selections persist on the CodeSession and are
  re-applied to the ACP session each turn (best-effort), editable from both
  the new-session dialog and the session header.
2026-06-21 21:08:49 +05:30
Arjun
c8d801a123 add toolkits to the new chat for ease of discovery 2026-06-20 03:02:41 +05:30
Arjun
a0429fc037 email reply all and first name signoff 2026-06-20 02:32:08 +05:30
Arjun
f848d235d8 fix file diffs 2026-06-20 00:02:29 +05:30
Ramnique Singh
f65f7e8fc8 feat(billing): consume plan variant catalog
Fetch the public billing catalog during account config load and use durable plan IDs from /v1/me for display, analytics, and upgrade/manage labels.

Renderer billing surfaces now resolve plan display data from the catalog and show Unknown when the backend returns an unmapped plan ID.
2026-06-19 16:52:22 +05:30
Arjun
aa8dfb74ad minor ui fixes and default model to claude 2026-06-19 16:24:29 +05:30
Arjun
dfd4075e0e filter out random contacts 2026-06-19 14:59:45 +05:30
Arjun
851c3be69f gmail contacts should use light mode 2026-06-19 14:45:21 +05:30
Prakhar Pandey
2fb0e7402a feat(onboarding): allow adding multiple provider keys in BYOK 2026-06-19 12:47:35 +05:30
Arjun
811ae22bb1 knowledge to brain 2026-06-19 11:59:56 +05:30
gagan
1f8ac2cf34
feat(bg-tasks): coding-from-meetings — auto-implement coding action items (#630)
* feat(bg-tasks): coding-from-meetings — auto-implement coding action items

A background-task flavor that watches for meeting notes, scans them for
actionable coding items, and autonomously implements them in isolated git
worktrees, summarizing results in the task's index.md.

- Emit `meeting.notes_ready` when Fireflies/Granola first write a meeting note
- Add optional `projectId` to BackgroundTask (pins a coding task to a repo)
- New `launch-code-task` builtin tool: per group of items, create a
  worktree-isolated, yolo, direct code session, wrap the prompt in an
  autonomous scaffold, run async, and finalize a per-session row in index.md
- Group code sessions under their meeting heading in index.md
- Summary from the code agent's `## Summary` section; file counts from
  `git diff` vs the worktree fork point (counts committed work, not just dirty)
- Guardrails: self-heal projectId across runs, cap launches per run, and bar
  the bg-task agent from managing/spawning tasks
- UI: "View available templates" -> Coding-from-meetings preset (repo picker,
  prefilled trigger + instructions)

See plan.md for the full design.

* let the copilot able to configure a coding background agent

* Delete plan.md

---------

Co-authored-by: Arjun <6592213+arkml@users.noreply.github.com>
2026-06-19 11:26:43 +05:30
Prakhar Pandey
36da053b8d Merge remote-tracking branch 'upstream/dev' into feat/byok-onboarding-simplify
# Conflicts:
#	apps/x/apps/main/src/ipc.ts
#	apps/x/packages/core/src/models/models.ts
#	apps/x/packages/shared/src/ipc.ts
2026-06-19 01:37:25 +05:30
Prakhar Pandey
92b95f659e feat(onboarding): simplify BYOK to provider + key, fetch models from key 2026-06-19 01:12:53 +05:30
gagan
2f926f8dc0
fix(copilot): make Copilot aware of native Slack and query selected channels (#627)
When Slack is connected natively (desktop/cURL auth, not Composio), the Copilot now routes Slack requests to the native slack skill instead of composio-integration, surfaces the user's selected channels in the system prompt, and steers catch-up queries to 'agent-slack message list --oldest' rather than unreads/search (which return empty under desktop-imported auth). Instruction cache is invalidated on slack:setConfig and knowledgeSources:upsert so changes take effect.
2026-06-18 11:53:56 -07:00
Harshvardhan Vatsa
c38ddef93f
feat: compose new email with contact autocomplete and AI drafting (#616)
* feat: compose new email with contact autocomplete and AI drafting

- Add a compose-new-email box to the email view with a recipient field
  that autocompletes from Gmail contacts (keyboard navigation, match
  highlighting, avatar chips)
- Build contact indices in core: gmail_sent_contacts syncs the SENT
  label via the Gmail API for full coverage of people you've emailed,
  with gmail_contacts as an instant local-snapshot fallback; both are
  pre-warmed at startup so the first keystroke is instant
- Add generateOneShot() one-shot text generation for the composer's
  "write with AI", resolving to the active default model/provider
- Add getAccountName() (parsed from a recent SENT message's From
  header, no extra OAuth scope) so AI drafts sign off with the real name
- New IPC channels: gmail:searchContacts, gmail:getAccountName,
  llm:generate, llm:getDefaultModel

* feat: attachments, undo/redo, and unified compose for new emails

- Merge ComposeNewBox into ComposeBox via a new 'new' mode, memoizing the
  component so inbox sync ticks no longer jank the open composer.
- Add file attachments: stage files in the renderer (25MB cap), pass raw
  base64 over IPC, and build a multipart/mixed MIME on send.
- Add undo/redo buttons to the compose toolbar.
- Single Write/Edit AI bar that generates a draft, then iteratively rewrites
  it; drop the hardcoded Gemini Flash model and use the default Copilot model.
- Suppress inbox reloads while the compose-new modal is open.
- Log llm:generate provider/model/output for debugging.

* fix: remove redundant Subject placeholder in composer

The subject row already has a 'Subject' gutter label, so the input's
placeholder repeated the word — an empty field read 'Subject' twice.
Drop the placeholder to match the To/Cc/Bcc fields.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 00:14:00 +05:30
gagan
3fe7f307ab
fix(code-mode): use System32 bsdtar to extract engines on Windows (#626)
Enabling Claude/Codex in Settings -> Code Mode failed on Windows with
"tar (child): Cannot connect to C: resolve failed / gzip: stdin:
unexpected end of file". PATH `tar` resolves to a GNU tar (Git/MSYS2)
that misreads the absolute archive path "C:\...\engine.tgz" as a remote
host:path spec.

Pin extraction to the bsdtar shipped in System32, which handles
drive-letter paths natively, with a relative-path fallback if it is
absent. macOS/Linux behavior is unchanged.
2026-06-18 02:03:21 +05:30
arkml
79162ebc69
feat: ship Slack as a knowledge source, hardened for production (#596)
* index slack and add to home page

* filter only useful slack messages in homr

* feat: bundle agent-slack CLI and route all calls through shared executor

Pins agent-slack@0.9.3, bundles it next to main.cjs (replaces the startup npm install -g), adds a structured-result executor with bundled/global/PATH resolution, a slack:cliStatus IPC probe, and a PATH shim so the Copilot skill keeps working.

* feat: surface Slack failures and add cross-OS auth fallbacks

Classify agent-slack errors (not_authed/rate_limited/network/bad_channel), persist per-source sync status with rate-limit backoff, and expose it via slack:knowledgeStatus. Fix the Settings Enable bounce-back with actionable copy, a browser-paste (parse-curl) fallback, and a Windows quit-Slack-and-import button; add home-feed empty/error states.

* feat: rank Slack home feed deterministically by recency

Drop the per-load LLM ranker (cost/latency/model dependency) in favor of a stronger deterministic filter + recency ordering. The filter now removes system messages, emoji/reaction-only posts, bare greetings/acks, and empty bodies, with a durable-signal escape hatch. Expand tests to one describe per noise class plus ordering/cap/volume coverage.

* fix: hide Slack knowledge Save button once saved

Only show the Save button when the channel list or enabled toggle differs from the last-persisted config, so it disappears after a successful save and reappears when a new channel is entered.

---------

Co-authored-by: Gagancreates <gaganp000999@gmail.com>
2026-06-17 12:52:27 -07:00
gagan
2ddec07712
Code mode: make packaged builds work via managed engine provisioning (#625)
* fix(code-mode): make packaged code mode work via on-demand engine provisioning

Packaged builds could never run code mode: the Claude/Codex ACP adapters are
spawned as separate `node <entry>` processes resolved at runtime, but esbuild
can't inline a dynamic spawn target and Forge strips the workspace node_modules,
so every release threw `Cannot find module '@agentclientprotocol/...'`. Dev
worked only because of the pnpm symlink.

Rather than bundle the ~400 MB of native engines (one claude + one codex binary
per OS), provision them on demand:

- forge.config.cjs: stage the two ACP adapters + their JS dependency closure into
  .package/acp/node_modules (npm-style nested layout, native engines skipped),
  exempt .package from the node_modules ignore rule, and only sign/notarize when
  APPLE_ID is set so unsigned local/CI builds can package.
- agents.ts: resolve the adapter from the staged location first (node_modules
  fallback in dev); provision the pinned engine and point the adapter at it via
  CLAUDE_CODE_EXECUTABLE / CODEX_PATH. No dependence on a user's global install.
- engine-provisioner.ts: ensureEngine() downloads the per-platform engine package
  from npm AT THE EXACT VERSION THE ADAPTER WAS BUILT AGAINST, verifies its sha512
  integrity, extracts atomically into ~/.rowboat/engines/<agent>/<version>/, and
  caches it. Version-pinning keeps the ACP handshake compatible.
- engine-manifest.ts + scripts/gen-engine-manifest.mjs: committed manifest of
  tarball URLs + integrity for all platforms, regenerated from the adapters'
  pinned versions on a bump.

Verified on macOS arm64: both engines provision and run, and both adapters
complete the ACP initialize handshake from the packaged .app against the
provisioned engines. Installer drops from ~790 MB to 390 MB.

* feat(code-mode): explicit per-agent Enable in Settings; no silent chat download

Code mode now requires the user to explicitly enable an agent before use, instead
of silently downloading a ~200 MB engine on the first chat message.

- Settings → Code Mode: each agent shows "Not enabled" + an Enable button that
  downloads its engine with a live progress indicator (download % → verify →
  install), then flips to "Engine ready". Driven by a new codeMode:provisionEngine
  IPC call + a codeMode:engineProgress push channel. The section now states the
  prerequisite explicitly: the agent must be installed (Enable) and logged in
  (claude login / codex login — code mode reuses that saved credential).
- Chat path no longer auto-downloads: getProvisionedEnginePath() returns the
  enabled engine or throws a clear "enable it in Settings → Code Mode" error, so
  there's never a surprise mid-conversation download. getAgentLaunchSpec is sync
  again.
- Agent status: `installed` now means "engine provisioned" (downloaded), driving
  the Enable/Ready state; the new-session dialog shows "Enable in Settings" and
  disables un-enabled agents. Dropped the dead PATH-probing for a global CLI.

Verified: empty cache -> status installed=false and the chat path throws the
enable-in-Settings error (no download); core, renderer, and main typecheck/build;
no new lint errors.

* fix(code-mode): show only percentage during engine download in Settings

* feat(code-mode): prune superseded engine versions after install

After a successful provision, remove any other version dirs (and their .meta) for
that agent so old ~200 MB engines don't accumulate across version bumps. Best-effort;
never fails a good install. Verified: a planted stale version dir + meta are both
removed after provisioning the current version.

* fix(code-mode): keep showing engine download % after reopening Settings

Provisioning state lived in the row component, which unmounts when the Settings
dialog closes — so reopening mid-download showed the Enable button again even though
the download was still running in the main process. Move provisioning state to a
module-level store with one persistent listener on codeMode:engineProgress, so a row
remounting (dialog reopened) reflects the live % and resolves to Ready on completion.

* fix(code-mode): flip Enable row straight to Ready after install (no Enable flash)

On successful provision the in-flight flag was cleared before the async status
refresh completed, so the row briefly (or until reopen) showed the Enable button
again. Await the status refresh before clearing the flag so it transitions directly
to Ready.

* fix(code-mode): optimistically show Ready right after Enable completes

Awaiting the status refresh wasn't enough — setStatus re-renders the parent
separately from the row, leaving a window where the in-flight flag was cleared but
the status prop was stale, so the row flashed/stuck on the Enable button until
reopen. Track just-enabled agents in a module-level set and treat them as installed
immediately; loadStatus still syncs the real status in the background.

* fix(code-mode): graft login-shell PATH + add startup deadline

#1 (the gh/git "command not found" in packaged builds): GUI/Finder launches inherit
launchd's stripped PATH (/usr/bin:/bin:...), so tools the engine spawns — gh, git,
rg, bash — fail even though they work from a terminal (e.g. Homebrew's
/opt/homebrew/bin/gh). Probe the user's login-shell PATH and graft it onto the
engine's env before spawn (shell-env.ts; no-op on Windows / probe failure).

#2: add a 60s startup deadline (initialize / session create+load) so a wedged engine
fails with a clear, stderr-enriched error instead of an infinite "(pending...)".
Overridable via ROWBOAT_ACP_STARTUP_TIMEOUT_MS. Manager now disposes the client on
startup failure so the spawned adapter doesn't leak.

Verified: getAgentLaunchSpec's env.PATH now includes /opt/homebrew/bin (where gh
lives); core builds; no new lint errors.

* chore(code-mode): comment out signing/notarization for local builds

Revert to the explicit comment-out approach for osxSign/osxNotarize: uncomment them
(with APPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID) for a signed release build.

* chore(code-mode): keep signing/notarization active in committed config

The repo's forge.config ships with osxSign/osxNotarize enabled (release-ready).
Developers comment them out locally for unsigned test builds and don't commit that.

* chore: approve workspace build scripts so packaging runs non-interactively

The allowBuilds entries were left as "set this to true or false" placeholders, so
`pnpm install` / the pre-build deps check aborted with ERR_PNPM_IGNORED_BUILDS and
`npm run package` failed. Set them to true (and add node-pty, used by the code-mode
embedded terminal) so build scripts are approved and packaging works without a manual
`pnpm approve-builds`.
2026-06-17 21:53:15 +05:30
Gagan
8ce24ebb33 Revert "fix(code-mode): make packaged code mode work and drop ~460MB bundled engines (#614)"
This reverts commit 33c15cfbd9.
2026-06-16 23:29:46 -07:00
arkml
ed8f6f7246
Last modified (#624)
* order chats by recency
2026-06-17 10:21:24 +05:30
gagan
33c15cfbd9
fix(code-mode): make packaged code mode work and drop ~460MB bundled engines (#614)
* fix(code-mode): ship ACP adapters in packaged builds

Code mode spawns the Claude/Codex ACP adapters as separate `node <entry>`
processes resolved at runtime, so each must exist as a real file on disk.
esbuild can't inline them (dynamic require.resolve + spawn target), and Forge's
`ignore: /node_modules/` rule strips the workspace node_modules — so packaged
builds threw `Cannot find module '@agentclientprotocol/claude-agent-acp'` and
code mode was broken in every release. Dev worked only because the pnpm symlink
was present.

Stage the two adapters and their full production dependency closure into
.package/acp/node_modules during generateAssets, reconstructing an npm-style
nested layout: nest on version conflict (claude and codex keep their own
@agentclientprotocol/sdk; the @openai/codex launcher keeps its platform binary)
and skip platform-optional deps not installed for the build OS, so each OS ships
its own native binary. Exempt .package from the node_modules ignore rule, and
make the adapter resolver check the staged location first, falling back to
node_modules in dev.

Resolve dependency directories by walking node_modules directly rather than
require.resolve(`${pkg}/package.json`): the latter throws for packages whose
`exports` map doesn't expose package.json (e.g. @anthropic-ai/claude-agent-sdk),
which would silently drop them and their subtrees from the staged closure.

* fix(code-mode): drive agents from local install, drop bundled engines

Skip the platform-native engine packages (~230 MB each) when staging the ACP adapters and point each adapter at the user's local claude/codex via CLAUDE_CODE_EXECUTABLE / CODEX_PATH, erroring clearly when neither is installed. The codex resolver mirrors claude's login-shell probe so nvm/fnm installs resolve on macOS/Linux. Shrinks each installer ~460 MB.

* fix(code-mode): surface agent startup failures instead of hanging forever

- 60s deadline on adapter initialize / session create+load: an engine that
  launches but never completes the SDK handshake (e.g. an outdated local
  CLI) now fails with the adapter's stderr attached instead of leaving the
  turn (pending...) indefinitely; prompts themselves stay un-timed
- dispose the adapter client when startup fails so the spawned process
  does not leak
- set DEBUG_CLAUDE_AGENT_SDK=1 so the SDK logs the exact spawn command and
  claude's stderr to ~/.claude/debug/sdk-*.txt and startup errors point at
  that file (engine stderr is otherwise discarded entirely)
- graft the user's login-shell PATH onto the adapter env on macOS/Linux:
  GUI launches inherit launchd's stripped PATH, which breaks node-shebang
  claude launchers (nvm/npm installs) and the engines' own subprocess spawns

* ci(code-mode): cross-platform smoke matrix + one-shot diagnose script

- x-code-mode-smoke.yml: on apps/x PRs, package the app on mac/linux/windows
  and run acp-smoke.mjs, which asserts (1) adapters staged + native engines
  stripped, (2) each staged adapter boots from the packaged app via the
  packaged Electron binary and answers ACP initialize, (3) a fake engine that
  launches but never responds is converted into a clear startup-timeout error
  instead of hanging forever (the silent-hang class)
- diagnose-code-mode.sh: colleagues run one command and send one blob
  (engine versions/paths/types, login-shell vs GUI PATH, auth presence,
  stream-json probe, newest SDK debug log) — one round trip instead of five
- forge.config.cjs: only sign/notarize when APPLE_ID is set, so unsigned
  local mac builds and the CI smoke matrix can package deterministically
- client.ts: startup timeout overridable via ROWBOAT_ACP_STARTUP_TIMEOUT_MS
  (CI uses 10s; also an escape hatch for slow MCP-heavy setups)

Verified on Windows: all smoke checks pass, including the end-to-end
fake-hanging-engine timeout (fails in 10.0s with the stderr-enriched error).

* fix(ci): make smoke fake engines executable — codex-acp spawns CODEX_PATH directly on unix

A bare .js with no shebang/exec bit fails with EACCES and crashes the adapter
on mac/linux. Split into an exec-bit exit-0 fake for the handshake check and
the hanging fake for the timeout check.

* fix(code-mode): resolve claude/codex installed via version managers

commonInstallPaths only checked ~/.nvm/versions/node/<binary>, never
descending into nvm's versioned vX.Y.Z/bin subdirs, so an nvm-installed
codex/claude showed "not installed" in Settings and failed code-mode
chat with "CLI not found" on GUI launches (where the login-shell PATH
isn't inherited).

Enumerate each installed Node version's bin dir for nvm/fnm/asdf, and
add pnpm global (PNPM_HOME / platform default) and Claude Code's legacy
~/.claude/local install location. Shared by both the Settings status
check and the chat/tab binary resolvers, so it covers all code-mode
entry points.

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

* chore(code-mode): remove code-mode smoke test workflow and script

Only needed during cross-platform verification of the packaged code-mode
fix; drop the CI workflow and its acp-smoke.mjs helper now that it's done.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:16:00 -07:00