Commit graph

119 commits

Author SHA1 Message Date
Ramnique Singh
876bc35e9e refactor(x): one delivery path for turn events — chat, channels, deltas
Completes the turn-spine unification. Chat's SessionChatStore now
consumes turns:events like every other surface, joining live turns by
file offset (drop covered, append contiguous, refetch on gap) instead
of blind-appending session-bus events. Text/reasoning deltas cross IPC
only for turns a window subscribed to (turns:subscribe/unsubscribe, a
per-webContents registry that also survives window teardown), so
headless pipeline chatter never reaches windows that aren't watching.
The channels bridge settles turns off the turn event bus instead of
filtering the session broadcast. With no turn-event consumers left,
SessionsImpl stops forwarding entirely (it drains its execution streams
and keeps outcome/index handling) and sessions:events shrinks to
index-changed entries — one channel for turn events, one for session
metadata.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 15:47:21 +05:30
Ramnique Singh
12a7118445 feat(x): turn event spine — one bus for every turn's events
TurnRuntime now publishes every turn's events (durable ones tagged with
their 1-based file offset, deltas without) to an injected TurnEventHub,
regardless of who started the turn. Main forwards durable events to all
windows on one turns:events channel; the renderer's new useTurn(turnId)
hook joins live turns gap/duplicate-free via the offset protocol. The
sub-agent card drops its 1s polling for push updates, and the nine
per-channel window fan-out loops collapse into one broadcastToWindows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:19:53 +05:30
arkml
3261d64dd4
Merge pull request #691 from rowboatlabs/feat/caffeinate-toggle
feat(x): Caffeinate toggle to keep the system awake
2026-07-08 00:10:03 +05:30
Arjun
94727a238b feat(x): add Caffeinate toggle to keep the system awake
Adds a Caffeinate switch (Settings → Mobile channels) that uses
Electron's powerSaveBlocker ('prevent-app-suspension', equivalent to
caffeinate -i) to stop the machine from idle-sleeping so the
WhatsApp/Telegram bridge stays connected. While active, an amber
coffee icon in the titlebar shows the state and can be clicked to
turn it off; a power:caffeinateChanged push keeps the titlebar
indicator and the settings switch in sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:07:14 +05:30
Gagan
fd91f4ea22 feat(apps): star-ranked catalog, star from Rowboat, delete local apps
- catalog is ranked by GitHub star count (per-repo lookup with a 10-min
  cache; unauthenticated works, the publish token is used when present)
- star button on each catalog card stars/unstars the app's repo as the
  signed-in user (public_repo scope covers it), optimistic with revert
  and a sign-in hint when signed out
- the info panel now offers Delete for local apps (apps:delete already
  existed and cleans up app-owned agents; only installed apps had a
  removal button before)
2026-07-07 20:59:02 +05:30
Gagan
f2bc01ffa9 fix(apps): eliminate blank-app window on launch and add crash visibility
- start the apps server before createWindow instead of after the full
  service-init chain: the Apps view was reachable ~10s before port 3210
  was listening, so every app iframe opened to connection-refused
- retry EADDRINUSE binds (quick relaunch races left the server disabled
  for the whole session with no retry)
- app frame: auto-retry the iframe instead of a dead manual Retry, and
  surface when the apps server itself is down (new apps:serverStatus)
- log render-process-gone/child-process-gone reasons; renderer helper
  crashes were previously invisible in packaged builds
2026-07-06 20:52:31 +05:30
Gagan
35c781b8e4 feat(apps): M3 publisher — guided publish, updates, register-existing
- publisher (§11): resumable state machine persisted in .rowboat-publish.json
  (packaged → repo_created → source_pushed → release_created →
  assets_uploaded → registered → published); Git Data API single-commit
  source push with generated README/LICENSE/.gitignore when absent; both
  release assets uploaded (bundle + standalone manifest); fork+PR registry
  registration with rejected:<code> parsing and rename-retry surface
- publish update (§11.3): semver bump, package, push, release — no registry
- register existing release (§11.5) with client-side asset probe
- IPC: apps:publish/publishUpdate/registerExisting + apps:progress pushes
2026-07-06 15:08:36 +05:30
Gagan
6dea528fb4 feat(apps): M3 installer — catalog/URL installs, updates, rollback, uninstall
- installer (§12): streaming download with size cap, extraction guards
  (zip-slip, symlink entries, entry-count, uncompressed cap), bundle-identity
  check, D18 capability-mismatch check against the previewed manifest,
  defaults->data on first install, pinned per-file sha256, folder suffixing
- URL installs (§12.5): two-phase preview from the bundle's own manifest,
  10-min retained staging, GitHub-provenance detection for later updates
- update (§12.3): D18 scoped to the diff (new_capabilities), modified-file
  warning against pinned hashes, .previous/ swap with one-step rollback
- uninstall (§12.4) deletes app-owned bg-tasks; startup tmp cleanup
- IPC: apps:catalogIndex/Search/Detail, install, installFromUrl, uninstall,
  checkUpdate, update, rollback + analytics events
2026-07-06 15:02:00 +05:30
Gagan
d7af51c10b feat(apps): M3 groundwork — packager, GitHub device-flow auth, registry client
- env: GITHUB_OAUTH_CLIENT_ID (device flow enabled on the Rowboat OAuth app;
  overridable via ROWBOAT_GITHUB_CLIENT_ID)
- packager (§4.4): allowlist-only .rowboat-app ZIP (yazl), sorted entries,
  symlink skip, sha256
- github-auth (§10): device-code start/poll, identity fetch, token stored
  0600 with safeStorage encryption injected from main (core stays
  electron-free); githubAuth:* IPC + external open of the verification page
- registry client (§9.2): unauthenticated tarball index with 5-min cache and
  stale fallback, raw-record resolve, substring search, quota-free
  latestManifest via release-asset redirect with name-mismatch guard
- registry repo contents (docs/apps-registry): record JSON schema +
  validate-and-merge Action implementing §9.3 checks 1-7 with rejected:<code>
  comments and per-name concurrency
- fix: host-api copilot-run adapted to dev's ModelSelection {provider,model}
  (this is the same fix dev needs for Ramnique's packaging break)
- pnpm 11: blockExoticSubdeps=false (electron-forge has a git subdep)
2026-07-06 14:49:52 +05:30
gagan
b7d1019538
Merge pull request #665 from rowboatlabs/feature/apps-v1
Rowboat Apps V1 — M1 + M2 (spec implementation)
2026-07-06 14:18:11 +05:30
Ramnique Singh
1050614295
Merge pull request #674 from rowboatlabs/code-mode-fixes
Code mode fixes
2026-07-06 14:06:41 +05:30
Ramnique Singh
3bbed55735 feat(x): ephemeral CodeRunFeed for codex streaming + settle-time durable batch
The runs->turns migration broke codex live streaming in copilot chat: the
turns bridge's publish shim forwarded only tool-output-stream and silently
dropped code-run-event / code-run-permission-request (the latter would
deadlock a turn under policy 'ask'). An interim fix persisted every stream
event as durable tool_progress, but that wrote each text chunk to the turn
file — unsustainable.

Final architecture — live and durable paths split:

- Live (ephemeral, bypasses the turn runtime): code_agent_run broadcasts
  each ACP event on a new CodeRunFeed (core DI singleton), forwarded by
  main over a dedicated codeRun:events channel, buffered per toolCallId in
  a module-level renderer store and rendered by CodingRunBlock. The buffer
  survives session switches; nothing is persisted.

- Durable (one line per run): when the run settles (success, error, or
  cancel), code_agent_run publishes a single code-run-events-batch with
  the whole ordered timeline, consecutive same-role message chunks
  coalesced (display-lossless — the timeline concatenates them anyway).
  The turns bridge maps it to tool_progress {kind:'code-run-events'};
  turn-view derives the replay timeline from it, so reloads keep history.

- Permissions stay durable per-ask (request + resolved marker): the
  renderer overlay resets on session switch, so an ephemeral-only ask
  would strand a blocked turn with no card to answer. Pending = requests
  minus resolutions (handles concurrent asks), cleared on tool result.

The legacy code-section path (runs bus per-event) is untouched; its
per-event ctx.publish remains and is a no-op under the turns shim.

Also: repaired the two code_agent_run tests broken by the earlier cwd
existence check (they used a nonexistent /repo), and added coverage for
feed broadcast, batch coalescing, partial-batch-on-failure, bridge
durability routing, and pending-permission derivation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:04:33 +05:30
Arjun
fd49334c33 move local-model concurrency to a turn-level defer lock
Review feedback: drop the model-layer scheduler (per-call-site
interactive/classifier/background priorities, local-provider hostname
heuristic) in favor of app-level orchestration:

- deferBackgroundTasks flag in models.json, surfaced as a settings
  toggle; auto-enabled once (UI logic) when the user connects Ollama
- ChatActivity counter marked by both chat runtimes (sessions layer and
  legacy AgentRuntime.trigger)
- startWhenPossible/runWhenPossible wrappers around the headless agent
  runner; all background invocations (knowledge pipeline, live notes,
  background tasks, scheduled + prebuilt agents) go through them and
  wait for chat-idle when the flag is set

createLanguageModel keeps only the Ollama context-window middleware;
the LM Studio capability probe now keys off the provider flavor instead
of hostname sniffing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:38:39 +05:30
Arjun
c1fd4e6221 allow byok even when logged in 2026-07-06 13:32:13 +05:30
Arjun
799d7584b8 make local models work well 2026-07-06 13:31:13 +05:30
Arjun
adb8b16a3c Add in-call mic mute: pauses all input (mic audio + frame capture)
A mute button on both call surfaces (full-screen call and floating
popout) that pauses everything going to the assistant while keeping the
call alive — for stepping away to talk to someone in the room without
ending and restarting the call.

- Mic audio stops reaching Deepgram (user mute OR'd into the existing
  thinking/speaking setPaused; KeepAlives keep the socket warm so
  unmute is instant)
- Camera/screen frames stop being sampled (new setCapturePaused in
  useVideoMode); collectFrames() returns nothing while muted, so typed
  messages during a mute carry no frames either
- Devices stay acquired for instant resume; mute resets at call
  start/end; assistant output is unaffected (Stop handles that)
- Honest UI: "Muted" status chip replaces the green "Listening" pulse,
  muted badge on the user tile, pill's share badge flips to "Sharing
  paused"; new toggle-mic popout action + micMuted in popout state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 00:28:34 +05:30
arkml
6b2120d376
Merge pull request #666 from rowboatlabs/graph2
Improvements to graph and email labeling
2026-07-05 11:38:31 +05:30
Arjun
9d31d25046 learns email importance from preference actions 2026-07-05 02:36:01 +05:30
Gagan
dc28b0b868 merge dev into feature/apps-v1
Union resolutions — dev's changes kept, apps-v1 additions appended:
- use_case.ts: dev's meeting_prep + our app_llm_generate/app_copilot_run
- ipc.ts: dev's HttpAuthRequestSchema import + our AppSummarySchema
- shared/index.ts: dev's channels export + our rowboatApp export
- builtin-tools app-navigation: dev's read-view/open-item actions + our open-app
- chat-conversation: dev's read-view/open-item labels + our open-app labels
- sidebar: dev's nav-workspaces tour id + our Apps entry
- App.tsx: dev's open-item handler + our open-app handler
2026-07-05 01:08:35 +05:30
Gagan
7f15f67273 feat(apps): Rowboat Apps M1 — per-app origins, Host API core, apps skill
Implements M1 of the Apps V1 spec, replacing the mini-apps prototype:

- rowboat-app.json manifest + AppSummary/install/publish/registry schemas;
  sourceApp on background tasks
- apps server on 127.0.0.1:3210 (absorbs local-sites): Host-header routing to
  <slug>.apps.localhost origins, 421 rebinding guard, static from dist/ with
  entry/SPA fallback + realpath confinement, control host /health, chokidar
  watcher + per-app SSE with dist/data areas, embed-opt-in autosize bootstrap
  and cancelable rowboat:data-change event
- Host API: D17 anti-CSRF (X-Rowboat-App + strict Origin on non-GET),
  GET /_rowboat/app (manifest + theme), /_rowboat/events, data API under
  data/ with atomic writes and dataContracts enforcement
- indexer: list (ok/invalid surfaced) / create scaffold / delete
- IPC: apps:list/get/create/delete/setTheme; renderer reports theme live
- renderer: Apps home (My apps grid + catalog placeholder, server banner,
  invalid/kind badges) and full-height app view on the app origin
- apps copilot skill (replaces build-mini-app); app-set-data builtin replaces
  mini-app-install/set-data; deleted the app://miniapp protocol, postMessage
  bridge, mini-apps handler/channels, and local-sites
2026-07-04 17:12:54 +05:30
Gagan
2c038fe518 feat(mini-apps): live data refresh + install reliability
- watch ~/.rowboat/apps: data.json changes push into open apps (shim
  re-fetches, onData fires); installs/updates re-list the gallery live and
  reload an open app once the write settles
- atomic installs (temp->rename) so an app opened mid-install never reads a
  partial file
- serve app assets by direct disk read instead of net.fetch(file://) —
  Chromium's network service could stall and blank every open app at once
2026-07-04 15:59:13 +05:30
arkml
51c289ba36
Merge pull request #663 from rowboatlabs/video1
Video mode with screenshare and app navigation
2026-07-04 14:27:18 +05:30
Arjun
82abfe9a04 add better app navigation on call 2026-07-04 14:20:13 +05:30
Arjun
5b2ecc6720 added stop button in video call 2026-07-04 13:24:32 +05:30
Arjun
af24596591 phase 1 improvements to reduce latency: smart endpointing, streaming tts, early clause speech, ack 2026-07-04 12:51:06 +05:30
Arjun
e9ee8fd975 popout and full screen work as expected 2026-07-04 12:06:32 +05:30
Arjun
d2501c4f4d video mode popout over the app 2026-07-04 00:25:55 +05:30
Arjun
1edc1b17f1 screen share pop out when I switch to a different app 2026-07-04 00:09:46 +05:30
Arjun
6f901095ff initial commit of video mode 2026-07-03 22:29:10 +05:30
Arjun
fc9a76e1cb add whatsapp and telegram support 2026-07-03 19:25:06 +05:30
arkml
3ba94402d3
Merge pull request #650 from rowboatlabs/mail-enhancements
feat(email): drafts, search, read-state controls & Superhuman-style shortcuts
2026-07-03 01:12:40 +05:30
Arjun
080b8625c6 fix(email): draft lifecycle, keyboard nav, and composer layout fixes from review
- Remove the "Mark as read" toggle for Everything else end-to-end (UI, IPC,
  config, sync auto-read, markSectionRead) — it could bulk-mark backlog and
  archived mail read on the server during cache-recovery syncs
- Sending an edited draft now reuses the draft's stored In-Reply-To/References
  instead of self-referencing its own Message-ID (external clients thread
  correctly); standalone drafts send as fresh messages
- Keep draft autosaves out of the sync pipeline: skip DRAFT history messages
  (no more md/knowledge-event leaks, phantom "New email" notifications, or
  per-autosave LLM reclassification) and mirror the draft body onto the cached
  snapshot surgically instead of waking the sync loop
- Only recreate a Gmail draft when update fails with 404/410 — transient
  errors no longer pile up duplicates
- Emptying a draft and closing now deletes it from Gmail (matches Gmail)
- Forward keydown out of message iframes so j/k/e/r etc. keep working after
  clicking into an email body; restore expanded quotes across theme reloads
- Composer footer wraps on narrow panes: formatting toolbar + Discard move as
  one unit instead of overflowing into each other

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 01:08:55 +05:30
Arjun
7f3987094f fix browser issues 2026-07-02 19:39:21 +05:30
Ramnique Singh
49e1e99d16 fix(x/renderer): load headless transcripts via the turn runtime
Background-task and live-note history views called the legacy runs:fetch
for ids that are now turn ids (ENOENT after stage 6). New shared loader
src/lib/agent-transcript.ts fetches sessions:getTurn first and falls
back to runs:fetch so pre-migration histories stay readable (fallback
dies with the runs runtime in stage 7); turn transcripts render through
the same buildTurnConversation used by the chat views. Unit-tested
(turnToTranscript mapping + failure surfacing).

The chat-log download used runs:downloadLog with what is now a session
id. Added sessions:downloadLog (concatenates the session's turn logs
into one JSONL via the save dialog); the sidebar tries it first and
falls back to runs:downloadLog for legacy background tabs.

Remaining renderer runs:* callers are code-mode only (use-code-chat,
runs:events feed) — the deliberate stage-7 carve-out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 14:20:44 +05:30
Ramnique Singh
df607ea510 feat(x): sessions IPC + renderer data layer with testable stores (stage 5a)
The wire and state layer for the UI cutover; App.tsx integration follows
separately.

- shared/ipc.ts: eleven sessions:* invoke channels + the sessions:events
  push feed (SessionBusEvent via z.custom, like runs:events). The generic
  preload bridge needs no changes.
- main: sessions:* handlers as thin pass-throughs to the DI'd sessions
  service; startSessionsWatcher forwards the session bus to all windows;
  startup awaits the session-index scan before the renderer can list.
- renderer architecture per review guidance — all logic in framework-
  agnostic, dependency-injected modules; hooks are thin
  useSyncExternalStore subscriptions; components will consume pre-digested
  view models:
  - client.ts: narrow SessionsClient over window.ipc (fakeable).
  - feed.ts: one shared sessions:events consumer with fan-out (factory
    for tests).
  - turn-view.ts: pure derivations — live overlay (deltas accumulate,
    canonical events clear), TurnState -> ConversationItem[], and the
    session chat state (permission/ask-human maps re-manufactured in the
    runs-era shapes so existing components render unchanged;
    isProcessing/isThinking contract preserved).
  - store.ts: SessionChatStore (seed via getSession/getTurn, shared
    reduceTurn over live events, prior-turn freezing, unknown-turn
    reconciliation, stale-load guard, action routing) + SessionListStore.
  - hooks/useSessionChat + useSessions: thin wrappers, deps injectable.
- renderer test infra added from scratch (vitest + jsdom +
  testing-library); 29 tests across stores, pure views, feed, and hooks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 14:20:44 +05:30
Gagan
c424976926 fix(mini-apps): reliability fixes + self-contained data loading
From two rounds of copilot build feedback:

- runtime: null-check hallucinated tool names (was crashing the whole run)
- add window.rowboat.fetch + builtin fetch-url: CORS-safe HTTP for UI and
  bg-task agents (no shell needed for plain HTTP)
- mini-app-set-data/install: reject stringified JSON; require object
- manifest dataContract (requiredKeys/nonEmptyArrayKeys) enforced on writes so
  a stray/legacy task can't corrupt an app's data or wipe series
- self-contained data: data.json is a served sibling (dist/), apps fetch it via
  relative URL; removed host-side data injection
- build-mini-app skill: CORS/fetch-url/no-shell/capable-model/dataContract gotchas
2026-07-02 01:06:05 +05:30
gagan
08a4077d43
Meeting prep: attendee notes + proactive 6h-ahead prep briefs (#636)
* feat(meetings): inline meeting prep under next meeting

Resolve a meeting's attendees against the knowledge base and render each
attendee's existing person.md inline beneath the next upcoming meeting in
the Meetings view. Deterministic (email-exact match, then unambiguous
name/alias), no LLM in the hot path.

- core: resolveMeetingPrep() + meeting-prep:resolve IPC
- renderer: notes-first rows with expandable person.md; unmatched attendees
  collapse into a 'no notes yet' row with a Create note action that hands
  off to the Copilot
- prep re-resolves when a People note changes

* feat(meetings): per-meeting prep toggle

Add a Prep toggle to every eligible meeting row, not just the next one.
The next meeting still auto-expands; other meetings resolve their attendee
notes lazily when their toggle is opened. All-day and solo events get no
toggle.

* feat(meetings): org matching, cached index, and field-bleed fix

#2 Matching: resolve the meeting's external companies from attendee email
domains (and matched people's Organization field), excluding the user's own
domain, and surface those org notes in a Companies section. Normalize display
names (strip '(via ...)' suffixes) before name/alias matching.

#3 Perf: meeting prep reads a cached knowledge index instead of rescanning the
whole knowledge dir on every resolve; invalidated whenever a file under
knowledge/ changes (wired to the workspace watcher).

Fix: extractField in the knowledge index consumed newlines after the label, so
an empty field (e.g. **Role:**) bled the next line's value. Restrict it to
spaces/tabs so empty fields resolve to undefined. Also strip the folder prefix
from link targets for display (Organizations/Rowboat Labs -> Rowboat Labs).

* feat(meetings): proactive prep notes generated 6h ahead

Generate a meeting prep note ~6h before each meeting and surface its brief
in the prep card.

- Generator (meeting_prep_brief.ts): assembles roster + 'last time' recap
  (extracts the prior instance's Action items) + agenda, ordered by meeting
  type (recurring -> recap first, one-off -> agenda first), then one model
  call for a 'what matters' brief. Writes knowledge/Meetings/prep/<slug>-<date>.md
  with eventId/recurringEventId frontmatter. Reuses the summarizeMeeting LLM
  path (configured model, useCase: meeting_prep).
- Scheduler (meeting_prep_scheduler.ts): calendar-aware tick (5m), generates
  prep within the 6h lead window, state file dedupes, self-heals on changes.
- Card: shows the brief (bulleted, compact) above a People list whose rows
  link out to each person's note instead of rendering it inline. Generated
  prep notes are kept out of the past-notes table.

* chore(meetings): poll prep scheduler every 15m instead of 5m
2026-07-02 00:35:35 +05:30
hrsvrn
063f6892a8 feat(email): on-demand attachment download, search clear, per-category read prefs
- Download search-result attachments on demand before opening (fixes Linux
  where xdg-open reports success on a missing file so the old open-then-
  download fallback never fired)
- Add a clear button to the search box that dismisses search and returns to
  the inbox
- Remove the read toggle from the Important section
- Replace the Everything else "Read" toggle with a "Mark as read" preference:
  turning it on marks all current and future "Everything else" mail read;
  turning it off only stops auto-reading future mail, leaving current threads
  untouched
2026-07-01 23:51:08 +05:30
hrsvrn
ab9bce6203 feat(email): drafts, search, bulk read/unread, configurable backfill
Gmail client enhancements in apps/x, bundling four independent features:

- Drafts: save/autosave, update, delete, and list Gmail drafts. The
  composer autosaves to a real Gmail draft (debounced ~1.5s) while
  typing, reuses the thread's existing draft so edits update in place,
  flushes a final save on close, and deletes the draft on discard. Adds
  isDraft/draftId to the shared thread types. New core helpers:
  saveThreadDraft, deleteThreadDraft, listDraftThreads,
  buildDraftSnapshot, buildRawMimeMessage.

- Search: searchThreads(query, {limit}) backed by an on-disk snapshot
  cache (read/writeSearchSnapshot). Extracts parseThreadSnapshot as the
  shared parse core reused by both the cache-building sync and search.

- Read state: markThreadRead now takes a `read` flag so it toggles
  read/unread; new markSectionRead marks a whole section
  (important/other) read/unread and returns the affected count.

- Backfill: the onboarding/recovery sync is now bounded by a
  configurable thread COUNT instead of a fixed 7-day window. New
  gmail_sync_config.ts (getMaxEmails/setMaxEmails) backed by
  ~/.rowboat/config/gmail_sync.json, default 500, clamped to 1-5000;
  seeded on first run.

New IPC channels: gmail:saveDraft, gmail:deleteDraft, gmail:getDrafts,
gmail:search, gmail:markSectionRead (plus a `read` field on
gmail:markThreadRead).
2026-07-01 16:20:33 +05:30
Gagan
924a93fa4a feat(mini-apps): serve apps from ~/.rowboat/apps via app://miniapp
Apps are now user data on disk, not bundled in the repo.

- MiniAppManifest schema + mini-apps:list/get-data/seed IPC channels
- main: mini-apps-handler (list/get-data + seed install primitive) and an
  app://miniapp/<id>/ protocol host serving ~/.rowboat/apps/<id>/dist
- renderer lists installed apps from disk and loads each via app://miniapp
  (real origin: remote images/fetch work); data sourced from data.json
- removed the bundled sample apps from source (they live in ~/.rowboat/apps)
2026-07-01 00:27: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
Arjun
753e3448f0 Show previous chats related to the workspace in the workspace section. 2026-06-29 12:27:43 +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
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
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
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
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