Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ac9ab99
feat(windows): build native desktop and managed daemon
mrinaldi2 Oct 1, 2026
e74d9e0
fix(windows): use platform-neutral setup and folder labels
mrinaldi2 Oct 1, 2026
93e88f5
docs(windows): show verified native first-run setup
mrinaldi2 Oct 1, 2026
9076772
feat(runtime): choose Codex CLI or Claude Code per bot
mrinaldi2 Oct 1, 2026
f098695
fix(windows): stop native sessions and normalize trust paths
mrinaldi2 Oct 1, 2026
9704a69
fix(runtime): discover Windows Codex and surface startup failures
mrinaldi2 Oct 1, 2026
f085596
feat(runtime): embed the native Codex terminal
mrinaldi2 Oct 1, 2026
52ee60f
fix(mcp): expose bot runtime selection and verification
mrinaldi2 Oct 1, 2026
3bbfb78
fix(windows): install the daemon with the desktop installer
mrinaldi2 Oct 1, 2026
b464e02
fix(windows): clarify unavailable daemon ports
mrinaldi2 Oct 1, 2026
3bd59d9
fix(windows): tolerate stale daemon process IDs
mrinaldi2 Oct 1, 2026
bd633ad
fix(supervisor): start each bot's runtime only once at a time
mrinaldi2 Oct 1, 2026
205e4a4
docs: design peer bots across two daemons
mrinaldi2 Oct 1, 2026
b9da294
feat(peer): link bots across two daemons
mrinaldi2 Oct 1, 2026
c402609
docs: design the chat pane as the default bot surface
mrinaldi2 Oct 1, 2026
6ca3cea
feat(chat): serve bots' transcripts as chat turns
mrinaldi2 Oct 1, 2026
8538571
feat(desktop): open bots on a chat of their turns
mrinaldi2 Oct 1, 2026
8545803
feat(permissions): answer bots' permission prompts from the app
mrinaldi2 Oct 1, 2026
d61fac1
feat(tasks): add a Tasks tab to the bot's side panel
mrinaldi2 Oct 1, 2026
886af27
feat(codex): read Codex bots in the chat and answer their approvals
mrinaldi2 Oct 1, 2026
97fdd40
feat(peer): read a linked bot's real chat across the peer link
mrinaldi2 Oct 1, 2026
3d357da
feat(chat): answer decisions, attach files, run slash commands, search
mrinaldi2 Oct 1, 2026
f52d47d
fix(chat): find in the chat, read more on tasks, true image shapes
mrinaldi2 Oct 1, 2026
358afee
fix(tasks): show the whole task, not the list's preview
mrinaldi2 Oct 1, 2026
64e74eb
feat(desktop): dictate into the chat composer, on the device
mrinaldi2 Oct 1, 2026
1ed8f5a
fix(chat): show screenshots whole in a lightbox
mrinaldi2 Oct 1, 2026
fec6522
feat(desktop): dictate on Windows with the on-device SAPI recognizer
mrinaldi2 Oct 1, 2026
c40c7eb
fix(peer): free a revoked peer's name so the machines can pair again
mrinaldi2 Oct 1, 2026
41e9ecf
chore(notices): record the desktop manifest changes from Windows dict…
mrinaldi2 Oct 1, 2026
8770441
feat(peer): link projects across peer daemons
mrinaldi2 Oct 1, 2026
715f6bd
test(desktop): add visual baselines for the chat pane stories
mrinaldi2 Oct 1, 2026
f759438
docs: describe linked projects in the protocol and peer-bot docs
mrinaldi2 Oct 1, 2026
ffb8c46
feat(daemon): give each bot its own browser, and show bots' conversat…
mrinaldi2 Oct 2, 2026
c67f878
feat(daemon): advertise the bot browser and conversations capabilities
mrinaldi2 Oct 2, 2026
aa371dd
feat(desktop): watch each bot's browser, read its memory, follow bots…
mrinaldi2 Oct 2, 2026
de46d3f
docs: describe bots' own browsers and the conversations requests
mrinaldi2 Oct 2, 2026
dffd258
fix(daemon): read Chrome's DevTools replies to their Content-Length
mrinaldi2 Oct 2, 2026
1872840
feat(browser): one shared live stream per bot, watched from selection
mrinaldi2 Oct 2, 2026
fe323b6
feat(daemon): pick interrupted work back up after a restart
mrinaldi2 Oct 2, 2026
f9a3d57
feat(peer): watch and type into a linked bot's terminal from the othe…
mrinaldi2 Oct 2, 2026
3317e32
feat(peer): watch a linked bot's browser from the other machine
mrinaldi2 Oct 2, 2026
eee8e7d
feat: a Commands tab listing what each bot is running
mrinaldi2 Oct 2, 2026
3bbd423
feat: restart or clear a bot, and pick up its work on every restart
mrinaldi2 Oct 2, 2026
65a9741
feat(desktop): copy a bot's commands and their output
mrinaldi2 Oct 2, 2026
d07d72e
feat: say which bot made each file in the Files tab
mrinaldi2 Oct 2, 2026
9455656
fix(files): credit files bots copy in through a variable, and archive…
mrinaldi2 Oct 2, 2026
c99373f
fix(files): credit a task's generated files to whoever made its others
mrinaldi2 Oct 2, 2026
88a35da
fix(browser): start the browser server through cmd on Windows
mrinaldi2 Oct 2, 2026
254f652
feat(workers): let bots spawn temporary workers, queued past a cap
mrinaldi2 Oct 2, 2026
f9317eb
Merge remote-tracking branch 'origin/main' into codex/chat-pane
ahilles107 Oct 2, 2026
5db5e12
feat(workers): share work through the project's git repository
mrinaldi2 Oct 2, 2026
c595b52
docs(workers): describe temporary workers, the queue and the shared r…
mrinaldi2 Oct 2, 2026
974b173
feat(workers): show the queue in the app and save unpushed work
mrinaldi2 Oct 2, 2026
8141ed9
fix(workers): place workers at once and let them handle git themselves
mrinaldi2 Oct 2, 2026
0400242
fix(workers): tell clients which bots are workers, and drop stale waits
mrinaldi2 Oct 2, 2026
3e9ed2d
feat(desktop): answer every bot's permission prompts from the Control…
mrinaldi2 Oct 2, 2026
8ca995a
Merge remote-tracking branch 'origin/codex/chat-pane' into codex/link…
mrinaldi2 Oct 2, 2026
f7b2ebf
Merge branch 'codex/linked-projects' into codex/agent-browser-memory
mrinaldi2 Oct 2, 2026
b4edde5
Merge branch 'codex/agent-browser-memory' into claude/temporary-workers
mrinaldi2 Oct 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
94 changes: 94 additions & 0 deletions apps/desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,15 @@ serde_json = "1"
semver = "1"
ureq = { version = "2", features = ["json"] }
toml = "0.8"

# On-device dictation for the chat composer (Apple's Speech framework).
[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.6"
block2 = "0.6"
objc2-foundation = "0.3"
objc2-speech = "0.3"
objc2-avf-audio = "0.3"

# On-device dictation on Windows (SAPI's in-process recognizer).
[target.'cfg(windows)'.dependencies]
windows = { version = "0.61", features = ["Win32_Foundation", "Win32_Media_Speech", "Win32_System_Com"] }
10 changes: 10 additions & 0 deletions apps/desktop/src-tauri/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Signed releases run with the hardened runtime, which blocks the
microphone unless the app declares it. Used only for dictation. -->
<key>com.apple.security.device.audio-input</key>
<true/>
</dict>
</plist>
11 changes: 11 additions & 0 deletions apps/desktop/src-tauri/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Dictation in the chat composer. Merged into the app's Info.plist by the Tauri bundler. -->
<key>NSMicrophoneUsageDescription</key>
<string>Gravity listens while you dictate a message to a bot in the chat.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>Gravity turns your dictation into text on this Mac, for messages to your bots.</string>
</dict>
</plist>
82 changes: 82 additions & 0 deletions apps/desktop/src-tauri/src/dictation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! On-device dictation for the chat composer. The operating system's own
//! speech recognizer turns the microphone into text, so audio never goes to
//! Gravity or a third party: Apple's Speech framework on macOS, set to
//! recognize on the device whenever the Mac supports it, and SAPI's
//! in-process recognizer on Windows, which always runs on the PC.
//!
//! The composer starts and stops a session; partial and final transcripts
//! arrive as `dictation` events.

use serde::Serialize;
use tauri::{AppHandle, Emitter};

#[cfg(target_os = "macos")]
mod macos;
#[cfg(windows)]
mod sapi;

/// The event the composer listens to.
const EVENT: &str = "dictation";

#[derive(Clone, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub(crate) enum DictationEvent {
/// The transcript so far; it may still change.
Partial { text: String },
/// The settled transcript; the session has ended.
Final { text: String },
/// The session ended without a transcript, or failed.
Ended {
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
},
}

pub(crate) fn emit(app: &AppHandle, event: DictationEvent) {
let _ = app.emit(EVENT, event);
}

/// Whether this platform can dictate.
#[tauri::command]
pub fn dictation_available() -> bool {
#[cfg(windows)]
{
sapi::available()
}
#[cfg(not(windows))]
{
cfg!(target_os = "macos")
}
}

/// Starts listening, asking for speech and microphone permission the first
/// time. Async so the permission prompt never waits on the main thread.
#[tauri::command]
pub async fn start_dictation(app: AppHandle) -> Result<(), String> {
#[cfg(target_os = "macos")]
{
tauri::async_runtime::spawn_blocking(move || macos::start(app))
.await
.map_err(|e| e.to_string())?
}
#[cfg(windows)]
{
tauri::async_runtime::spawn_blocking(move || sapi::start(app))
.await
.map_err(|e| e.to_string())?
}
#[cfg(not(any(target_os = "macos", windows)))]
{
let _ = app;
Err("Dictation is not available on this platform yet.".to_string())
}
}

/// Stops listening; the final transcript follows as an event.
#[tauri::command]
pub fn stop_dictation() {
#[cfg(target_os = "macos")]
macos::stop();
#[cfg(windows)]
sapi::stop();
}
Loading