a3s 0.10.3

a3s — A3S coding agent CLI; `a3s code` launches the interactive TUI
Documentation
#![cfg(all(windows, target_arch = "x86_64"))]

mod support;

#[path = "code_use_windows_e2e/fake_openai.rs"]
mod fake_openai;
#[path = "code_use_windows_e2e/fixture.rs"]
mod fixture;
#[path = "code_use_windows_e2e/plans.rs"]
mod plans;

use std::io::Read;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::time::{Duration, Instant};

use fake_openai::FakeOpenAi;
use fixture::{
    create_ocr_fixture, prepare_use_install, prepare_webview_probe, required_path, FixtureSite,
};
use plans::{browser_plan, ocr_plan, office_compat_plan, office_plan, PlannedToolCall};
use support::{a3s_bin, TempWorkspace};

struct WindowsE2e {
    workspace: TempWorkspace,
    project: PathBuf,
    use_install: PathBuf,
    webview_install: PathBuf,
    edge: PathBuf,
}

impl WindowsE2e {
    fn prepare() -> Self {
        let workspace = TempWorkspace::new("code-use-windows-all-tools");
        let project = workspace.path("project");
        let use_install = workspace.path("use-install");
        let webview_install = workspace.path("webview-install");
        std::fs::create_dir_all(project.join(".a3s")).expect("create Windows E2E project");

        let use_binary = required_path("A3S_USE_E2E_BIN");
        let source_root = required_path("A3S_USE_E2E_SOURCE_ROOT");
        prepare_use_install(&use_binary, &source_root, &use_install);
        prepare_webview_probe(&webview_install);

        let edge = PathBuf::from(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe");
        assert!(edge.is_file(), "Windows E2E requires Microsoft Edge");
        Self {
            workspace,
            project,
            use_install,
            webview_install,
            edge,
        }
    }

    fn run_phase(&self, label: &str, plan: Vec<PlannedToolCall>) {
        let model = FakeOpenAi::start(label, plan.clone());
        self.write_config(&model.base_url);
        let system_root = std::env::var_os("SystemRoot")
            .map(PathBuf::from)
            .unwrap_or_else(|| PathBuf::from(r"C:\Windows"));
        let isolated_path = std::env::join_paths([system_root.join("System32"), system_root])
            .expect("construct isolated Windows PATH");
        let prompt = format!(
            "Delegate the complete {label} Windows matrix to the dedicated use worker. Required tools: {}",
            plan.iter()
                .map(|call| call.name.as_str())
                .collect::<Vec<_>>()
                .join(", ")
        );

        let mut command = Command::new(a3s_bin());
        command
            .args(["code", "-C"])
            .arg(&self.project)
            .arg("--config")
            .arg(self.project.join(".a3s/config.acl"))
            .env("HOME", self.workspace.path("home"))
            .env("USERPROFILE", self.workspace.path("home"))
            .env("LOCALAPPDATA", self.workspace.path("local-app-data"))
            .env("APPDATA", self.workspace.path("app-data"))
            .env("A3S_DATA_HOME", self.workspace.path("data"))
            .env("A3S_STATE_HOME", self.workspace.path("state"))
            .env("A3S_CACHE_HOME", self.workspace.path("cache"))
            .env("A3S_RUNTIME_HOME", self.workspace.path("runtime"))
            .env("A3S_USE_INSTALL_DIR", &self.use_install)
            .env("A3S_WEBVIEW_INSTALL_DIR", &self.webview_install)
            .env("A3S_BROWSER_EXECUTABLE", &self.edge)
            .env("A3S_USE_BROWSER_EXECUTABLE_PATH", &self.edge)
            .env("A3S_USE_BROWSER_HEADED", "false")
            .env("A3S_CODE_TUI_SMOKE", "1")
            .env("A3S_CODE_TUI_PROMPT", prompt)
            .env("PATH", isolated_path)
            .env("NO_PROXY", "127.0.0.1,localhost")
            .env("no_proxy", "127.0.0.1,localhost")
            .env_remove("A3S_OFFLINE")
            .env_remove("A3S_NO_AUTO_INSTALL")
            .env_remove("A3S_OFFICECLI_EXECUTABLE");
        let output = run_with_live_output(command, label);
        let stdout = String::from_utf8_lossy(&output.stdout);
        let stderr = String::from_utf8_lossy(&output.stderr);
        assert!(
            output.status.success(),
            "{label}: stdout={stdout}\nstderr={stderr}"
        );
        assert!(
            stderr.contains("[tool start] task") && stderr.contains("[tool end] task (exit 0)"),
            "{label}: the primary TUI did not complete its delegated Use task\nstderr={stderr}"
        );
        model.assert_complete(&plan);
    }

    fn write_config(&self, base_url: &str) {
        std::fs::write(
            self.project.join(".a3s/config.acl"),
            format!(
                r#"default_model = "openai/fake"
providers "openai" {{
  apiKey = "test"
  baseUrl = "{base_url}"
  models "fake" {{
    name = "Windows Use E2E"
    toolCall = true
  }}
}}
memory {{ llmExtraction = false }}
"#
            ),
        )
        .expect("write Windows E2E model config");
    }

    fn path(&self, name: &str) -> PathBuf {
        self.project.join(name)
    }
}

fn run_with_live_output(mut command: Command, label: &str) -> Output {
    let phase_timeout = std::env::var("A3S_USE_WINDOWS_E2E_PHASE_TIMEOUT_SECS")
        .ok()
        .and_then(|seconds| seconds.parse::<u64>().ok())
        .map(Duration::from_secs)
        .unwrap_or_else(|| Duration::from_secs(30 * 60));
    let mut child = command
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .unwrap_or_else(|error| panic!("run {label} Code TUI smoke: {error}"));
    let stdout = capture_live_output(
        child.stdout.take().expect("capture Code TUI stdout"),
        "stdout",
    );
    let stderr = capture_live_output(
        child.stderr.take().expect("capture Code TUI stderr"),
        "stderr",
    );
    let deadline = Instant::now() + phase_timeout;
    let status = loop {
        if let Some(status) = child
            .try_wait()
            .unwrap_or_else(|error| panic!("poll {label} Code TUI smoke: {error}"))
        {
            break status;
        }
        if Instant::now() >= deadline {
            let _ = child.kill();
            let _ = child.wait();
            let stdout = stdout.join().expect("join Code TUI stdout capture");
            let stderr = stderr.join().expect("join Code TUI stderr capture");
            panic!(
                "{label} exceeded its {:?} phase timeout\nstdout={}\nstderr={}",
                phase_timeout,
                String::from_utf8_lossy(&stdout),
                String::from_utf8_lossy(&stderr)
            );
        }
        std::thread::sleep(Duration::from_millis(25));
    };
    Output {
        status,
        stdout: stdout.join().expect("join Code TUI stdout capture"),
        stderr: stderr.join().expect("join Code TUI stderr capture"),
    }
}

fn capture_live_output(
    mut stream: impl Read + Send + 'static,
    channel: &'static str,
) -> std::thread::JoinHandle<Vec<u8>> {
    std::thread::spawn(move || {
        let mut output = Vec::new();
        let mut buffer = [0_u8; 8 * 1024];
        loop {
            match stream.read(&mut buffer) {
                Ok(0) => break,
                Ok(read) => {
                    output.extend_from_slice(&buffer[..read]);
                    eprint!(
                        "[windows-use-e2e:{channel}] {}",
                        String::from_utf8_lossy(&buffer[..read])
                    );
                }
                Err(error) => panic!("read Code TUI {channel}: {error}"),
            }
        }
        output
    })
}

#[test]
#[ignore = "requires real Use artifacts, Edge, and network access for bounded providers"]
fn code_tui_exercises_every_projected_use_tool_and_optional_capability_on_windows() {
    let e2e = WindowsE2e::prepare();
    let site = FixtureSite::start();

    let browser_screenshot = e2e.path("browser.png");
    let browser_calls = browser_plan(&site.url, &browser_screenshot);
    e2e.run_phase("Browser core tools", browser_calls);
    assert_nonempty_file(&browser_screenshot);

    let document = e2e.path("native.docx");
    let merged = e2e.path("merged.docx");
    let office_screenshot = e2e.path("office.png");
    let office_calls = office_plan(&document, &merged, &office_screenshot);
    e2e.run_phase("native Office tools and views", office_calls);
    for artifact in [&document, &merged, &office_screenshot] {
        assert_nonempty_file(artifact);
    }

    let ocr_image = e2e.path("ocr.png");
    create_ocr_fixture(&ocr_image);
    let ocr_calls = ocr_plan(&ocr_image);
    e2e.run_phase("native OCR tools", ocr_calls);

    let compat_calls = office_compat_plan();
    e2e.run_phase("Office compatibility MCP", compat_calls);
}

fn assert_nonempty_file(path: &Path) {
    let metadata = std::fs::metadata(path)
        .unwrap_or_else(|error| panic!("missing artifact {}: {error}", path.display()));
    assert!(
        metadata.is_file() && metadata.len() > 0,
        "artifact is empty: {}",
        path.display()
    );
}