Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c4d81f0
feat(browser-use): embedded APP-053 host plane + Composer slash command
AruNi-01 Aug 4, 2026
851aeec
fix(browser-use): address CodeRabbit stability and security quick-wins
AruNi-01 Aug 4, 2026
ce0653e
fix(desktop): stop macOS Dock tile collapsing to zero width
AruNi-01 Aug 4, 2026
558ed4a
fix(browser): raise standalone traffic lights; fix element picker i18…
AruNi-01 Aug 4, 2026
1a851c1
merge(main): integrate APP-052 desktop-use with APP-053 browser-use
AruNi-01 Aug 4, 2026
a8d13da
feat(browser-use): show Desktop Use agent chrome on page actions
AruNi-01 Aug 4, 2026
562d77d
fix(desktop): harden browser-use errors and overlay workspace ownership
AruNi-01 Aug 4, 2026
d34cf2b
fix(ci): rustfmt browser-use chrome + generic control-plane 500s
AruNi-01 Aug 4, 2026
afafd1f
fix(desktop-use): fail fast on missing Screen Recording; auto-fill pi…
AruNi-01 Aug 5, 2026
8599443
fix(browser-use): use external/embedded backend names in public surfaces
AruNi-01 Aug 5, 2026
5e81886
style(desktop-use): rustfmt control and highlight
AruNi-01 Aug 5, 2026
59047d4
docs(specs): add APP-054 terminal TUI scroll stability
AruNi-01 Aug 5, 2026
cdaa248
feat(terminal): APP-054 mouse mode observe, restore, and detached watch
AruNi-01 Aug 5, 2026
9cc525a
feat(terminal): APP-054 proportional TUI wheel and reattach mouse UX
AruNi-01 Aug 5, 2026
2c7d604
docs(architecture): record client transport debt (WS vs desktop IPC)
AruNi-01 Aug 5, 2026
f068b2f
feat(desktop): improve macOS icon toolchain and packaging hooks
AruNi-01 Aug 5, 2026
101ee1e
feat(browser): harden embedded webview selection and guest runtime
AruNi-01 Aug 5, 2026
8595ca8
feat(web): desktop-use readiness dialog and settings polish
AruNi-01 Aug 5, 2026
6cfe7b8
fix(web): desktop fullscreen bus and chrome padding polish
AruNi-01 Aug 5, 2026
f8b462f
fix(terminal): stop reattach from stacking TUI frames into scrollback
AruNi-01 Aug 5, 2026
aed791c
fix(terminal): reduce Grok warm-switch flash of home underscore cursor
AruNi-01 Aug 5, 2026
9560482
fix(terminal): clear history when inline TUI disables mouse
AruNi-01 Aug 5, 2026
a6cad26
fix(terminal,web): keep pre-TUI history; polish composer menu UX
AruNi-01 Aug 5, 2026
60cceab
feat(web): regroup Desktop Use settings into collapsible cards
AruNi-01 Aug 5, 2026
940dd89
feat(web): add Desktop Use settings group i18n (en/zh)
AruNi-01 Aug 5, 2026
d97f8c5
test(web): cover Desktop Use settings groups and search keywords
AruNi-01 Aug 5, 2026
bdd1b24
fix(desktop-use): usable chrome, window coords, and session recovery
AruNi-01 Aug 5, 2026
8842c96
feat(desktop-use): native highlight binary and Electron grant overlay
AruNi-01 Aug 5, 2026
c3649a2
feat(agent): sticky attention latches and idle/dismiss hooks
AruNi-01 Aug 5, 2026
10c1582
feat(terminal): center-tab presentation and agent name titles
AruNi-01 Aug 5, 2026
e47016e
feat(web): configurable agent activity indicators
AruNi-01 Aug 5, 2026
96f1e59
refactor(ui): use border-beam package and dialog polish
AruNi-01 Aug 5, 2026
9f61c7f
fix(web): shell chrome, surface policies, and i18n polish
AruNi-01 Aug 5, 2026
d75b7ae
feat(shared/terminal): stabilize center-tab OSC session titles
AruNi-01 Aug 6, 2026
ccce0d5
feat(web): add terminal cursor appearance settings
AruNi-01 Aug 6, 2026
611232c
fix(terminal): zero scrollback only for inline mouse TUIs
AruNi-01 Aug 6, 2026
3696f10
fix(web): keep warm terminal chrome from painting over light surfaces
AruNi-01 Aug 6, 2026
b3490bd
feat(web): show workspace agent and PR status on sidebar rows
AruNi-01 Aug 6, 2026
8a4207e
feat(web): polish agent activity indicator picker
AruNi-01 Aug 6, 2026
a5ffb7e
feat(web): improve Appshots history popover UX
AruNi-01 Aug 6, 2026
c01090a
fix(web): polish Desktop Use readiness and permissions UX
AruNi-01 Aug 6, 2026
d906678
chore(desktop): unify host and notification icons with legacy regen
AruNi-01 Aug 6, 2026
c8ed993
fix(web): stabilize commit message field and refresh tab chrome
AruNi-01 Aug 6, 2026
963563e
docs: English UI casing rule and icon regen notes
AruNi-01 Aug 6, 2026
9bf93d5
Merge remote-tracking branch 'origin/main' into feat/app-053-browser-…
AruNi-01 Aug 6, 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
55 changes: 48 additions & 7 deletions .github/workflows/release-desktop-electron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ on:
type: choice
options:
- "all"
- "macos-14"
- "macos-15-intel"
- "macos-26"
- "macos-26-intel"
- "ubuntu-22.04"
- "windows-latest"
create_release:
Expand Down Expand Up @@ -126,13 +126,14 @@ jobs:
case "$1" in
"all" )
# electron-builder arch is selected via CLI on each runner OS.
printf '%s\n' '{"include":[{"platform":"macos-14","target":"aarch64-apple-darwin","builder_args":"--mac --arm64"},{"platform":"macos-15-intel","target":"x86_64-apple-darwin","builder_args":"--mac --x64"},{"platform":"ubuntu-22.04","target":"x86_64-unknown-linux-gnu","builder_args":"--linux --x64"},{"platform":"windows-latest","target":"x86_64-pc-windows-msvc","builder_args":"--win --x64"}]}'
# macOS 26 runners ship Xcode 26+ actool for Liquid Glass .icon → Assets.car.
printf '%s\n' '{"include":[{"platform":"macos-26","target":"aarch64-apple-darwin","builder_args":"--mac --arm64"},{"platform":"macos-26-intel","target":"x86_64-apple-darwin","builder_args":"--mac --x64"},{"platform":"ubuntu-22.04","target":"x86_64-unknown-linux-gnu","builder_args":"--linux --x64"},{"platform":"windows-latest","target":"x86_64-pc-windows-msvc","builder_args":"--win --x64"}]}'
;;
"macos-14" )
printf '%s\n' '{"include":[{"platform":"macos-14","target":"aarch64-apple-darwin","builder_args":"--mac --arm64"}]}'
"macos-26" )
printf '%s\n' '{"include":[{"platform":"macos-26","target":"aarch64-apple-darwin","builder_args":"--mac --arm64"}]}'
;;
"macos-15-intel" )
printf '%s\n' '{"include":[{"platform":"macos-15-intel","target":"x86_64-apple-darwin","builder_args":"--mac --x64"}]}'
"macos-26-intel" )
printf '%s\n' '{"include":[{"platform":"macos-26-intel","target":"x86_64-apple-darwin","builder_args":"--mac --x64"}]}'
;;
"ubuntu-22.04" )
printf '%s\n' '{"include":[{"platform":"ubuntu-22.04","target":"x86_64-unknown-linux-gnu","builder_args":"--linux --x64"}]}'
Expand Down Expand Up @@ -280,6 +281,46 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

# Liquid Glass icons: electron-builder compiles resources/icons/icon.icon via actool ≥ 26.
- name: Select Xcode 26+ and verify actool (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
set -euo pipefail
# Prefer newest Xcode_26* if multiple are installed on the image.
XCODE_APP=""
for candidate in \
/Applications/Xcode.app \
/Applications/Xcode_26.6.app \
/Applications/Xcode_26.6.0.app \
/Applications/Xcode_26.5.app \
/Applications/Xcode_26.4.1.app \
/Applications/Xcode_26.4.app \
/Applications/Xcode_26.3.app \
/Applications/Xcode_26.2.app \
/Applications/Xcode_26.1.app \
/Applications/Xcode_26.0.app; do
if [ -d "$candidate" ]; then
XCODE_APP="$candidate"
break
fi
done
if [ -z "$XCODE_APP" ]; then
# Fallback: first Xcode_26* directory
XCODE_APP="$(ls -d /Applications/Xcode_26*.app 2>/dev/null | sort -V | tail -1 || true)"
fi
if [ -z "${XCODE_APP:-}" ] || [ ! -d "$XCODE_APP" ]; then
echo "❌ No Xcode 26+ app found under /Applications (required for Liquid Glass .icon)"
ls /Applications/Xcode*.app 2>/dev/null || true
exit 1
fi
echo "Using Xcode at $XCODE_APP"
sudo xcode-select -s "$XCODE_APP"
xcodebuild -version
actool --version
# Fail early if actool is too old for Icon Composer (electron-builder check).
bun ./apps/desktop-electron/scripts/check-macos-icon-toolchain.ts

# electron-builder AppImage on Ubuntu 22.04 needs libfuse2 + packaging tools.
# Without these the Linux matrix fails during `appimagetool` / fpm steps.
- name: Install Linux packaging dependencies
Expand Down
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ Full conventions (zones, naming, the 4-file rule, optional spec logs, review che
- **Keep meaning aligned**: Locale variants should express the same product meaning and UI scope, even when the wording is not a literal translation.
- **Avoid hardcoded copy**: Prefer existing i18n lookup patterns for labels, titles, tooltips, empty states, and error text instead of hardcoded strings in components.

### English UI casing (strict)

**Do not set English UI words to ALL CAPS** (e.g. `OPEN`, `MERGED`, `DRAFT`, `SAVE ALL`). Agents default to this too often.

- **Use sentence case or leading capital only**: `Open`, `Merged`, `Draft`, `Closed` — first letter capital, rest lowercase (unless a multi-word product name needs standard title case, e.g. `Pull Request`).
- **Forbidden for labels, chips, badges, buttons, and status text**: CSS `uppercase` / `text-transform: uppercase` that forces full upcase, and source strings written as `OPEN` / `MERGED` solely for display.
- **Exceptions** (narrow): true acronyms that are conventionally all-caps in product copy (`API`, `PR` as a short form when space-constrained, `CI`, `TUI`), keyboard shortcuts display (`⌘K`), or third-party brand marks that require a specific casing.
- When in doubt, match nearby existing UI that is **not** all-caps.

---

## 🔌 Transport Rules
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

11 changes: 11 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,17 @@ not re-license those weights.

Full Apache-2.0 text: https://www.apache.org/licenses/LICENSE-2.0

-------------------------------------------------------------------------------
8. AIcss Orbs (agent activity indicators)
-------------------------------------------------------------------------------

Source: https://www.aicss.dev/components/orbs
Location: apps/web/src/features/agent/components/orbs/
Author: Kevin (@kvnkld) / AIcss
Notes: Free-tier copy-paste UI component (React + CSS Module), vendored
and lightly adapted for Atmos theme (html.dark) and sizing.
No external runtime dependency beyond the vendored source.

-------------------------------------------------------------------------------
End of NOTICE
-------------------------------------------------------------------------------
38 changes: 38 additions & 0 deletions apps/api/src/api/hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use axum::{
Json, Router,
};
use core_service::service::agent_hooks::AtmosContext;
use serde::Deserialize;
use serde_json::Value;

use crate::app_state::AppState;
Expand Down Expand Up @@ -66,6 +67,9 @@ pub fn routes() -> Router<AppState> {
post(force_session_idle),
)
.route("/sessions/{session_id}", delete(remove_hook_session))
// Attention routes before `/{tool}/…` so "attention" is not parsed as a tool name.
.route("/attention", get(list_attention))
.route("/attention/clear", post(clear_attention))
.route("/install", post(install_hooks))
.route("/uninstall", post(uninstall_hooks))
.route("/status", get(hooks_status))
Expand Down Expand Up @@ -240,6 +244,40 @@ async fn list_hook_sessions(State(state): State<AppState>) -> Json<Value> {
Json(serde_json::json!({ "sessions": sessions }))
}

async fn list_attention(State(state): State<AppState>) -> Json<Value> {
let attention = state.agent_hooks_service.get_all_attention();
Json(serde_json::json!({ "attention": attention }))
}

#[derive(Debug, Deserialize)]
struct ClearAttentionBody {
/// Prefer the stable pane id (`{context}:{tmux_window}`) the client focuses.
#[serde(default)]
stable_pane_id: Option<String>,
#[serde(default)]
stable_pane_ids: Option<Vec<String>>,
}

async fn clear_attention(
State(state): State<AppState>,
Json(body): Json<ClearAttentionBody>,
) -> Json<Value> {
let mut ids: Vec<String> = body.stable_pane_ids.unwrap_or_default();
if let Some(id) = body.stable_pane_id {
if !id.trim().is_empty() {
ids.push(id);
}
}
let cleared = if ids.len() == 1 {
state
.agent_hooks_service
.clear_attention_for_pane(ids[0].as_str())
} else {
state.agent_hooks_service.clear_attention_matching_ids(&ids)
};
Json(serde_json::json!({ "cleared": cleared }))
}

async fn clear_idle_sessions(State(state): State<AppState>) -> Json<Value> {
let cleared = state.agent_hooks_service.clear_idle_sessions();
Json(serde_json::json!({ "cleared": cleared }))
Expand Down
4 changes: 4 additions & 0 deletions apps/api/src/api/ws/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,10 @@ pub enum WsEvent {
AgentHookStateChanged,
/// Idle agent hook sessions were cleared; payload contains removed session IDs
AgentHookSessionsCleared,
/// Sticky need-attention latch raised (permission / task complete)
AgentAttentionRaised,
/// Sticky need-attention latch(es) cleared after user acknowledge
AgentAttentionCleared,
/// Agent notification (permission request, task complete, etc.)
AgentNotification,
/// Current branch PR status should be refreshed
Expand Down
7 changes: 7 additions & 0 deletions apps/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ fn spawn_agent_hook_forwarder(
WsEvent::AgentHookSessionsCleared,
json!({ "session_ids": session_ids }),
),
AgentHookEvent::AttentionRaised(latch) => {
(WsEvent::AgentAttentionRaised, json!(latch))
}
AgentHookEvent::AttentionCleared { stable_pane_ids } => (
WsEvent::AgentAttentionCleared,
json!({ "stable_pane_ids": stable_pane_ids }),
),
};
if let Err(error) = ws_manager
.broadcast(&WsMessage::notification(ws_event, data))
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

- **Dev**: `just dev-cli`
- **Build**: `just build-cli`
- **Install**: `just install-cli`
- **Install (cargo bin)**: `just install-cli` → `~/.cargo/bin/atmos`
- **Use local CLI**: `just use-local-cli` → release build + replace `~/.atmos/bin/atmos` (and `~/.cargo/bin` if present)
- **Test**: `cargo test -p atmos`

---
Expand Down
34 changes: 15 additions & 19 deletions apps/cli/src/commands/browser_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub async fn execute_cmd(command: BrowserUseCommand) -> Result<Value, String> {
target_id: Some(a.target_id),
tab_id: Some(a.tab_id),
text: Some(a.text),
element_ref: Some(a.element_ref),
element_ref: a.element_ref,
session: a.session,
..Default::default()
},
Expand All @@ -59,17 +59,13 @@ pub async fn execute_cmd(command: BrowserUseCommand) -> Result<Value, String> {
}

fn parse_backend(s: &str) -> Result<BrowserBackendKind, String> {
BrowserBackendKind::parse(s).ok_or_else(|| {
format!(
"invalid --backend {:?} (use cua|external|embedded|atmos)",
s
)
})
BrowserBackendKind::parse(s)
.ok_or_else(|| format!("invalid --backend {:?} (use external|embedded)", s))
}

#[derive(Debug, Subcommand)]
pub enum BrowserUseCommand {
/// Attach / prepare a browser CDP endpoint (CUA external Chromium).
/// Attach / prepare a system Chrome or Chromium page for Browser Use.
Prepare(PrepareArgs),
/// Read browser state / tabs / snapshot refs.
State(StateArgs),
Expand All @@ -83,12 +79,12 @@ pub enum BrowserUseCommand {

#[derive(Debug, Args)]
pub struct PrepareArgs {
/// Backend: cua (system Chromium) | embedded (Atmos webview stub until APP-053).
#[arg(long, default_value = "cua")]
/// Backend: `external` (system Chrome/Chromium) | `embedded` (Atmos in-app browser).
#[arg(long, default_value = "external")]
pub backend: String,
#[arg(long)]
pub pid: Option<i32>,
/// Required when --strategy existing_profile (engine 0.17).
/// Required when --strategy existing_profile.
#[arg(long)]
pub window_id: Option<i64>,
#[arg(long)]
Expand All @@ -100,7 +96,7 @@ pub struct PrepareArgs {

#[derive(Debug, Args)]
pub struct StateArgs {
#[arg(long, default_value = "cua")]
#[arg(long, default_value = "external")]
pub backend: String,
/// Bind mode: native browser pid (with --window-id).
#[arg(long)]
Expand All @@ -120,7 +116,7 @@ pub struct StateArgs {

#[derive(Debug, Args)]
pub struct ClickArgs {
#[arg(long, default_value = "cua")]
#[arg(long, default_value = "external")]
pub backend: String,
#[arg(long)]
pub target_id: String,
Expand All @@ -134,24 +130,24 @@ pub struct ClickArgs {

#[derive(Debug, Args)]
pub struct TypeArgs {
#[arg(long, default_value = "cua")]
#[arg(long, default_value = "external")]
pub backend: String,
#[arg(long)]
pub target_id: String,
#[arg(long)]
pub tab_id: String,
#[arg(long)]
pub text: String,
/// Required by engine 0.17 browser_type.
/// Element ref from state (required for external; optional for embedded active element).
#[arg(long = "ref")]
pub element_ref: String,
pub element_ref: Option<String>,
#[arg(long)]
pub session: Option<String>,
}

#[derive(Debug, Args)]
pub struct NavigateArgs {
#[arg(long, default_value = "cua")]
#[arg(long, default_value = "external")]
pub backend: String,
#[arg(long)]
pub target_id: String,
Expand All @@ -170,7 +166,7 @@ pub fn product_note() -> Value {
"product": "Browser Use",
"no_mcp": true,
"desktop_use": "separate — OS windows / keys / AX",
"backends": ["cua", "embedded"],
"embedded": "stub until APP-053 / PR #203",
"backends": ["external", "embedded"],
"embedded": "Atmos Desktop in-app browser via host control plane",
})
}
13 changes: 11 additions & 2 deletions apps/cli/src/commands/desktop_use/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,10 @@ pub struct ClickArgs {
/// Border highlight: auto (default) | desktop | clear | off
#[arg(long, default_value = "auto")]
pub highlight: String,
/// Under-arrow status text (what the agent is doing). Overrides auto copy.
/// Under-arrow operation text. Combined as "{agent} - {status}". Also used alone when already prefixed.
#[arg(long)]
pub status: Option<String>,
/// Agent display name for fallback "{name} Operating". Also reads ATMOS_DESKTOP_USE_AGENT_NAME.
/// Agent display name for "{name} - …". Also reads ATMOS_DESKTOP_USE_AGENT_NAME / AGENT_NAME.
#[arg(long)]
pub agent_name: Option<String>,
}
Expand All @@ -387,13 +387,22 @@ pub struct ClickArgs {
pub struct TypeArgs {
#[arg(long)]
pub text: String,
/// Optional pixel focus before type (engine 0.17). Use after a click fails to keep focus,
/// or for empty-AX apps: same coordinate rules as `drive click`.
#[arg(long)]
pub x: Option<i32>,
#[arg(long)]
pub y: Option<i32>,
#[arg(long)]
pub pid: Option<i32>,
#[arg(long)]
pub window_id: Option<i64>,
/// background (default) or foreground.
#[arg(long, default_value = "background")]
pub delivery_mode: String,
/// Desktop/window coord space for --x/--y: `png` (default) or `points` (screen/AX logical).
#[arg(long, default_value = "png")]
pub coord_space: String,
/// AX element token from `drive window-state` (preferred for focused fields).
#[arg(long)]
pub element_token: Option<String>,
Expand Down
5 changes: 5 additions & 0 deletions apps/cli/src/commands/desktop_use/drive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,17 @@ pub(super) fn drive_cmd(command: DriveCommand) -> Result<Value, String> {
a.highlight
)
})?;
let coord_space = CoordSpace::parse(&a.coord_space)
.ok_or_else(|| format!("invalid --coord-space {:?}", a.coord_space))?;
DriveRequest {
action: DriveAction::Type,
text: Some(a.text),
x: a.x,
y: a.y,
pid: a.pid,
window_id: a.window_id,
delivery_mode: Some(a.delivery_mode),
coord_space,
element_token: a.element_token,
element_index: a.element_index,
snapshot_id: a.snapshot_id,
Expand Down
Loading
Loading