Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5ee4d1f
build: fast daily profiles + sccache groundwork
master5d Jun 12, 2026
5eb12a4
docs+scripts: fast-build workflow (BUILD.md section, build-fast.ps1)
master5d Jun 12, 2026
4bfc9d4
feat: cargo feature `diarization` (default on) — MKL out of dev builds
master5d Jun 12, 2026
013876c
fix: build-fast.ps1 + BUILD.md corrections from agy adversarial review
master5d Jun 12, 2026
ae7ac13
feat: TTS v0 — speech engine foundation (WinRT SpeechSynthesizer)
master5d Jun 12, 2026
11f8e24
style: cargo fmt on tts module
master5d Jun 12, 2026
d8c4c01
feat(agent-bridge): deps + module skeleton + dev-env helper
master5d Jun 12, 2026
e85fa1a
feat(agent-bridge): question journal storage (separate agent_bridge.d…
master5d Jun 12, 2026
95586f2
feat(agent-bridge): bearer token file
master5d Jun 12, 2026
5a12dbd
feat(agent-bridge): pending-question state machine
master5d Jun 12, 2026
279a799
feat(agent-bridge): localhost HTTP server (ask/notify/answers, bearer…
master5d Jun 12, 2026
458accf
fix(agent-bridge): harden state/storage/token per agy quality review
master5d Jun 12, 2026
02bc489
feat(agent-bridge): tauri wiring — settings, commands, TTS speak, col…
master5d Jun 12, 2026
3fae071
feat(agent-bridge): floating question panel (multi-entry vite page)
master5d Jun 12, 2026
8d592b7
feat(agent-bridge): CLI client echo --ask
master5d Jun 12, 2026
de6f3cd
docs(agent-bridge): skill + README
master5d Jun 12, 2026
49870fd
fix(agent-bridge): portable-mode token path + CLI error surfacing (ag…
master5d Jun 13, 2026
6985265
style: prettier --write on agent-bridge docs + panel (CI format gate)
master5d Jun 14, 2026
5d25447
style: rewrite echo-ask SKILL.md with fenced blocks (prettier-idempot…
master5d Jun 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,6 @@ docs/superpowers/
# Local coding-agent scaffolding (Gemini CLI config + plan/notes copies)
.gemini/
agent-work/

# Serena MCP project artifacts (created by delegate agents)
.serena/
50 changes: 50 additions & 0 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,56 @@ cargo tauri dev # or: cargo run --manifest-path src-tauri/Cargo.toml
If you switch toolchains, `cargo clean -p whisper-rs-sys` first so CMake
reconfigures from scratch (a stale cache keeps the old generator/instance).

## Fast builds (daily workflow)

For daily development, a `fast` cargo profile is available which provides
release-grade optimization (thin LTO, 16 codegen units) with much faster
compile times than the full `release` profile (fat LTO).

### Quick start with script

The easiest way to build on Windows is using the provided PowerShell script
which handles the LLVM environment and Vulkan SDK detection:

```powershell
# Default: fast build
./scripts/build-fast.ps1

# Even faster (skips heavy Intel MKL dependency)
./scripts/build-fast.ps1 -NoDiarization

# Build and bundle
./scripts/build-fast.ps1 -Bundle

# Release-grade build
./scripts/build-fast.ps1 -Profile release
```

### Manual workflow

1. **Cargo Profile**: Use `--profile fast` for daily builds.

```bash
cargo build --manifest-path src-tauri/Cargo.toml --profile fast
```

Note: `npm run tauri build` always uses the `release` profile — tauri-cli
appends `--release` itself and the bundler expects `target/release`
artifacts, so custom profiles apply to bare `cargo build` only.

2. **sccache**: Speeds up repeated builds by caching dependency crates across
clean builds.
- Prerequisite: `RUSTC_WRAPPER=sccache` must be set as an environment variable.
- Check status: `sccache --show-stats`.

3. **Skipping Diarization**: To significantly speed up compilation, skip the
heavy Intel MKL dependency by disabling default features:
```bash
npm run tauri dev -- -- --no-default-features
```
_Note: If built without default features, passing `--diarize` at runtime
will error by design._

## Linux Install (from source)

The raw binary (`src-tauri/target/release/handy`) cannot run standalone — it needs Tauri resource files (tray icons, sounds, VAD model) to be co-located at the expected path.
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,24 @@ echo --help # list all flags

Debug mode toggles with `Ctrl+Shift+D` (Windows/Linux) or `Cmd+Shift+D` (macOS).

## Agent Bridge

Echo exposes a localhost HTTP API (`127.0.0.1:4123`) that allows external agents and scripts to "speak" to the user via Echo's floating panel. It's protected by a bearer token generated on first run.

```bash
# Example: Ask the user a question from a script
TOKEN=$(cat "$APPDATA/com.sovern.echo/agent_bridge_token")
curl -X POST http://127.0.0.1:4123/v1/ask \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"question": "Deploy to production?",
"kind": "choice",
"options": ["Yes", "No"],
"source": "deploy-script"
}'
```

## Platform support

macOS (Intel + Apple Silicon), x64 Windows, x64 Linux (Ubuntu 22.04 / 24.04).
Expand Down
80 changes: 80 additions & 0 deletions scripts/build-fast.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
param(
[ValidateSet("fast", "release")]
[string]$Profile = "fast",
[switch]$NoDiarization,
[switch]$Bundle
)

$ErrorActionPreference = "Stop"

Write-Host "--- Echo: Fast Build Workflow ---" -ForegroundColor Cyan

# 1. Autodetect Vulkan SDK
$vulkanPath = "C:\VulkanSDK"
if (Test-Path $vulkanPath) {
$latestSdk = Get-ChildItem $vulkanPath -Directory | Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1
if ($null -ne $latestSdk) {
$env:VULKAN_SDK = $latestSdk.FullName
Write-Host "Using VULKAN_SDK: $($env:VULKAN_SDK)"
}
}

# 2. Find and import VsDevCmd.bat
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vswhere) {
$vsPath = & $vswhere -latest -property installationPath
if ($vsPath) {
$vsDevCmd = Join-Path $vsPath "Common7\Tools\VsDevCmd.bat"
if (Test-Path $vsDevCmd) {
Write-Host "Importing MSVC environment (x64)..."
$tempFile = [IO.Path]::GetTempFileName()
cmd.exe /c "call `"$vsDevCmd`" -arch=x64 > nul && set > `"$tempFile`""
Get-Content $tempFile | ForEach-Object {
# [^=]+ (not .*?): cmd emits hidden vars like "=C:=..." whose
# empty name would crash SetEnvironmentVariable under -Stop.
if ($_ -match "^([^=]+)=(.*)$") {
$name = $Matches[1]
$value = $Matches[2]
# Avoid overwriting critical PowerShell/System variables
if ($name -notmatch "^(ALLUSERSPROFILE|APPDATA|COMPUTERNAME|ComSpec|CommonProgramFiles|CommonProgramW64|ConfigSetRoot|DriverData|HOMEDRIVE|HOMEPATH|LOCALAPPDATA|LOGONSERVER|NUMBER_OF_PROCESSORS|OS|PATHEXT|PROCESSOR_ARCHITECTURE|PROCESSOR_IDENTIFIER|PROCESSOR_LEVEL|PROCESSOR_REVISION|ProgramData|ProgramFiles|ProgramW64|PSModulePath|PUBLIC|SystemDrive|SystemRoot|TEMP|TMP|USERDOMAIN|USERDOMAIN_ROAMING_PROFILE|USERNAME|USERPROFILE|windir)$") {
[Environment]::SetEnvironmentVariable($name, $value, "Process")
}
}
}
Remove-Item $tempFile
}
}
}

# 3. Setup LLVM/Ninja Environment
$env:CC = "clang-cl"
$env:CXX = "clang-cl"
$env:CMAKE_GENERATOR = "Ninja"
$env:CMAKE_LINKER_TYPE = "LLD"
$env:CMAKE_POLICY_VERSION_MINIMUM = "3.5"
$env:CXXFLAGS = "/EHsc"
$env:CL = "/EHsc"

# Clear VS instance vars that break Ninja
$vsVars = @("VSINSTALLDIR", "CMAKE_GENERATOR_INSTANCE", "CMAKE_GENERATOR_PLATFORM", "CMAKE_GENERATOR_TOOLSET")
foreach ($var in $vsVars) {
Remove-Item "env:$var" -ErrorAction SilentlyContinue
}

# 4. Construct Command
if ($Bundle) {
# tauri-cli appends --release itself; forwarding a custom --profile would
# conflict and the bundler looks for artifacts in target/release anyway.
# Bundles always build with the shipping `release` profile.
$tauriArgs = @()
if ($NoDiarization) { $tauriArgs = @("--", "--", "--no-default-features") }
Write-Host "Executing: npm run tauri build $($tauriArgs -join ' ')" -ForegroundColor Green
npm run tauri build @tauriArgs
} else {
$cargoArgs = @("--profile", $Profile)
if ($NoDiarization) { $cargoArgs += "--no-default-features" }
$manifest = Join-Path $PSScriptRoot "..\src-tauri\Cargo.toml"
Write-Host "Executing: cargo build --manifest-path $manifest $($cargoArgs -join ' ')" -ForegroundColor Green
cargo build --manifest-path $manifest @cargoArgs
}
exit $LASTEXITCODE
33 changes: 33 additions & 0 deletions scripts/dev-env.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dot-source before any cargo command: `. ./scripts/dev-env.ps1`
# Imports the VS x64 env + LLVM/Ninja toolchain vars (BUILD.md, "Windows release
# build"). Without this, whisper-rs-sys' CMake build breaks on this toolchain.
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vswhere) {
$vsPath = & $vswhere -latest -property installationPath
$vsDevCmd = Join-Path $vsPath "Common7\Tools\VsDevCmd.bat"
if (Test-Path $vsDevCmd) {
$tmp = [IO.Path]::GetTempFileName()
cmd.exe /c "call `"$vsDevCmd`" -arch=x64 > nul && set > `"$tmp`""
Get-Content $tmp | ForEach-Object {
# [^=]+ : cmd emits hidden "=C:=..." vars whose empty name would throw
if ($_ -match '^([^=]+)=(.*)$') {
[Environment]::SetEnvironmentVariable($Matches[1], $Matches[2], 'Process')
}
}
Remove-Item $tmp -Force
}
}

$env:CC = 'clang-cl'; $env:CXX = 'clang-cl'
$env:CMAKE_GENERATOR = 'Ninja'
$env:CMAKE_LINKER_TYPE = 'LLD'
$env:CMAKE_POLICY_VERSION_MINIMUM = '3.5'
$env:CXXFLAGS = '/EHsc'; $env:CL = '/EHsc'
if (Test-Path C:\VulkanSDK) {
$env:VULKAN_SDK = (Get-ChildItem C:\VulkanSDK -Directory |
Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1).FullName
}
foreach ($v in 'VSINSTALLDIR', 'CMAKE_GENERATOR_INSTANCE', 'CMAKE_GENERATOR_PLATFORM', 'CMAKE_GENERATOR_TOOLSET') {
Remove-Item "env:$v" -ErrorAction SilentlyContinue
}
Write-Host "echo dev env ready (VULKAN_SDK=$env:VULKAN_SDK)" -ForegroundColor DarkGray
44 changes: 44 additions & 0 deletions skills/echo-ask/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
name: echo-ask
description: Ask the human a question through Echo's floating panel (Windows). Use when a task needs user input, confirmation, or a periodic check-in, and you are running on the user's machine alongside Echo.
---

# Echo Ask

Echo (the dictation app) exposes a localhost Agent Bridge.

1. Read the token from `%APPDATA%\com.sovern.echo\agent_bridge_token`.
2. POST to `http://127.0.0.1:4123/v1/ask` with header `Authorization: Bearer <token>`. The call BLOCKS until the human answers, dismisses, or it times out.

Request body:

```json
{
"question": "...",
"kind": "text",
"options": [],
"timeout_s": 300,
"speak": false,
"source": "<your-name>"
}
```

`kind` is `text`, `choice`, or `confirm`. Response:

```json
{ "status": "answered", "answer": "..." }
```

`status` is `answered`, `dismissed`, or `timeout`.

3. Fire-and-forget messages: POST `/v1/notify` with `{ "message": "...", "speak": true }`.
4. Journal: GET `/v1/answers?since=<unix_ms>`.

Etiquette: one question at a time (the server enforces it); keep questions short;
set `speak: true` only for time-sensitive asks; always set `source`.

CLI alternative for cron loops:

```bash
echo --ask "..." --ask-options "a,b" --ask-timeout 60
```
35 changes: 34 additions & 1 deletion src-tauri/Cargo.lock

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

30 changes: 29 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ default-run = "echo"

[profile.dev]
incremental = true # Compile your binary in smaller steps.
debug = "line-tables-only" # Full debuginfo bloats link time; backtraces still work.

# Dependencies are compiled once and cached — optimize them so debug-run audio
# paths (resampler, FFT, VAD) are usable; the workspace crate stays at opt 0.
[profile.dev.package."*"]
opt-level = 2

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down Expand Up @@ -82,7 +88,13 @@ specta = "=2.0.0-rc.22"
specta-typescript = "0.0.9"
tauri-specta = { version = "=2.0.0-rc.21", features = ["derive", "typescript"] }
tauri-plugin-dialog = "2.6"
speakrs = "0.4.2"
speakrs = { version = "0.4.2", optional = true }
tiny_http = "0.12"
getrandom = "0.3"

[features]
default = ["diarization"]
diarization = ["dep:speakrs"]

[target.'cfg(unix)'.dependencies]
signal-hook = "0.3"
Expand All @@ -102,6 +114,12 @@ windows = { version = "0.61.3", features = [
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging",
"Win32_System_Console",
"Media",
"Media_Core",
"Media_SpeechSynthesis",
"Storage_Streams",
"Foundation",
"Foundation_Collections",
] }
winreg = "0.55"

Expand Down Expand Up @@ -129,9 +147,19 @@ speakrs = { git = "https://github.com/master5d/speakrs", branch = "progress" }

[dev-dependencies]
tempfile = "3"
ureq = { version = "2", features = ["json"] }

[profile.release]
lto = true
codegen-units = 1
strip = true
panic = "unwind"

# Daily release-grade builds: thin LTO + parallel codegen links minutes faster
# at ~equal runtime perf. Shipping artifacts keep using `release` (fat LTO).
# Usage: cargo build --profile fast (bare cargo only — tauri-cli bundling
# appends --release itself and expects target/release artifacts)
[profile.fast]
inherits = "release"
lto = "thin"
codegen-units = 16
Loading
Loading