English | 简体中文
A lightweight, embeddable LLM Agent runtime engine in pure Rust, designed specifically for mobile operating systems (iOS and Android).
PhoneBuddy SDK empowers mobile applications with autonomous Agent capabilities: dynamic task planning, pure-Rust virtual shell applets, embedded JavaScript execution & data analytics (boa_engine), in-memory subagents, scheduled tasks, human-in-the-loop interactions, and real-time streaming callbacks—all strictly adhering to Apple App Store and Google Play sandbox restrictions with zero child processes spawned.
PhoneBuddy SDK's core agent runtime is derived and adapted from xAI's open-source agent engine, xai-org/grok-build. Leading desktop AI agent harnesses—such as Claude Code, OpenAI's Codex, and xAI's grok-build—have established state-of-the-art standards for autonomous agent execution loops on desktop and server environments. PhoneBuddy SDK brings this exact tier of desktop-class harness maturity down to mobile platforms (iOS & Android).
- 🔒 100% Mobile Sandbox Compliant
- Completely eliminates
std::process::Command,tokio::process, andfork/execsyscalls. - Passes Apple App Store and Google Play app sandbox security reviews with zero permission escalations.
- Completely eliminates
- 🎯 Autonomous Planning & Dynamic ReAct Loop
- Dynamically decomposes complex user requests into structured, step-by-step execution plans (
plan). - Sequentially dispatches tools, inspects outputs, and automatically refines execution paths until task completion.
- Dynamically decomposes complex user requests into structured, step-by-step execution plans (
- 🧰 Pure-Rust In-Memory Virtual Shell (
busybox)- Built-in pure-Rust POSIX command applets executing entirely within memory:
cat,head,tail,ls,wc,sort,uniq,find,echo,touch,mkdir,rm,cp,mv,du,pwd,basename,dirname(with fast regex & glob text search provided by the dedicatedgreptool).
- Built-in pure-Rust POSIX command applets executing entirely within memory:
- ⚡ Embedded JavaScript Sandbox (
boa_engine)- Integrated pure-Rust ECMAScript engine (
run_scripttool) for safe in-app mathematical computations, algorithmic filtering, data manipulation, and JSON transformations.
- Integrated pure-Rust ECMAScript engine (
- 📊 Embedded Data Analytics & Scripting
- Fast in-memory data processing, aggregation, mathematical statistics, and structured JSON/CSV manipulation via the embedded JavaScript engine (
run_script) and sandboxed file tools without external runtimes.
- Fast in-memory data processing, aggregation, mathematical statistics, and structured JSON/CSV manipulation via the embedded JavaScript engine (
- 📁 Jailed File Sandbox & Deep Search
- Jails all file operations within a designated
root_dir(e.g. app's Documents directory) with strict path traversal prevention. - Comprehensive toolset:
read_file(with line slicing, offset navigation, confusable unicode mapping),write_file,edit_file(smart chunk replacement),list_dir(recursive directory scanning), andgrep(fast regex and glob search).
- Jails all file operations within a designated
- 🤖 In-Memory Subagents & Asynchronous Task Coordination
- Spawns concurrent background subagents (
task) managed by an in-memory Tokio async task manager. - Complete task lifecycle control: query task execution logs (
task_output,get_task_output), synchronize on multiple tasks (wait_tasks), cancel tasks (kill_task), and monitor running jobs (monitor).
- Spawns concurrent background subagents (
- ⏰ Scheduling, Notifications & Human-in-the-Loop
- In-memory cron and timer task scheduling (
scheduler). - System push notifications and UI alerts via host callbacks (
notification). - Interactive user prompts (
ask_user_question) supporting multi-choice options and freeform write-ins.
- In-memory cron and timer task scheduling (
- 🌐 Web Search & Fetch with SSRF Protection
- Live web search (
web_search) with multi-engine routing (headless DuckDuckGo Lite via host WebView, LLM search API fallback, or Grok hosted{type: web_search}on Responses whenenable_web_searchis on). - Clean HTML-to-Markdown page conversion via pure-Rust
htmd(web_fetch) with built-in SSRF protection blocking private network / loopback ranges.
- Live web search (
- 🛡️ Production-Grade Harness Robustness
- Doom-Loop Detection: Identifies action stationarity (
IdenticalToolCallRun, 8-turn nudge / 16-turn break) plus server-side check support (x-grok-doom-loop-check). - Context Compaction: Heuristic token estimation with automatic history compaction when surpassing
24,000tokens. - Fault Resilience: Exponential backoff retry with jitter for 429/5xx, plus optional multi-provider failover (fast-fail ~6s per endpoint, cooldown stickiness). See docs/retry-and-provider-fallback.md.
- Doom-Loop Detection: Identifies action stationarity (
- 🔌 Flexible Protocol Support & Extensible Host Bridge
- Supports multiple LLM backends:
responses(OpenAI Response API + SSE streaming),chat_completions, andmessages. - Pure-Rust TLS transport via
rustls-ringwith HTTP/2 streaming. - Host LLM mode (
LlmMode::Host) allowing inference delegation to on-device models (e.g.llama.cpp/llama.rn) or custom host gateways viaPbLlmRequestCallback. - Dynamic host tool injection (
PbHostToolCallback) from Swift, Kotlin, or C.
- Supports multiple LLM backends:
- 📱 Cross-Platform C ABI & Idiomatic Native Wrappers
- Clean C-ABI interface (
phone_buddy.h) with guaranteed unwind safety (catch_unwind). - First-class Swift SDK (
PhoneBuddy.swift) withasync/awaitand SwiftUI support. - First-class Android Kotlin SDK (
NativeAgent.kt+ JNI bridge) with Jetpack Compose support. - Local CLI development tool (
phone-buddy-cli) for testing, debugging, and self-testing tools offline.
- Clean C-ABI interface (
┌──────────────────────────────────────────────────────────────────────────────────┐
│ Host Application Layer │
│ iOS App (Swift / SwiftUI) Android App (Kotlin / Compose) │
│ C / C++ Native Applications Local Developer CLI (phone-buddy-cli) │
└─────────────────────────────────────────┬────────────────────────────────────────┘
│
┌─────────────────────────────────────────┴────────────────────────────────────────┐
│ Platform Integration & C FFI Layer │
│ • phone_buddy.h (C ABI) • PhoneBuddy.swift (Swift Async/Await) │
│ • phonebuddy_jni.c / NativeAgent.kt • Host Callbacks (LLM, Tool, WebView) │
└─────────────────────────────────────────┬────────────────────────────────────────┘
│
┌─────────────────────────────────────────┴────────────────────────────────────────┐
│ Core Agent Engine (phone-buddy) │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────────┐ │
│ │ Agent Turn Loop │ │ Subagent System │ │ LLM Transport Layer │ │
│ │ • Task Planner │ │ • In-Memory Manager │ │ • Responses (SSE stream) │ │
│ │ • Doom-Loop Detector │ │ • Task Concurrency │ │ • ChatCompletions │ │
│ │ • History Compactor │ │ • Scheduler (cron) │ │ • Messages │ │
│ │ • Session Store │ │ • Task Monitor │ │ • Host LLM / rustls-ring │ │
│ └──────────────────────┘ └──────────────────────┘ └──────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────────────┐ │
│ │ Pure-Rust Toolset & Sandboxes │ │
│ │ • File Sandbox: read_file, write_file, edit_file, list_dir, grep │ │
│ │ • Virtual POSIX Shell: Pure-Rust BusyBox Applets (cat, head, sort, uniq, …)│ │
│ │ • Embedded JS Runtime: boa_engine ECMAScript sandbox (run_script) │ │
│ │ • Data Analytics: In-memory JavaScript computations & CSV/JSON processing │ │
│ │ • Web & Network: web_search (WebView / DDG), web_fetch (htmd), SSRF guard │ │
│ │ • Interaction: ask_user_question (Human-in-the-loop), notification, Host │ │
│ └────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
Import libphone_buddy_ffi.a and phone_buddy.h into your Xcode project, or directly include PhoneBuddy.swift:
import Foundation
// 1. Initialize configuration
let config = PhoneBuddyConfig(
apiKey: "your-api-key-or-jwt-token",
baseUrl: "https://api.x.ai/v1",
model: "grok-4.6",
apiBackend: "responses", // OpenAI Response API + SSE streaming
rootDir: PhoneBuddyConfig.sandboxRoot(workspaceName: "workspace"),
maxTurns: 24,
enableWebSearch: true,
agentName: "Acme", // system-prompt identity; omit to keep "PhoneBuddy"
extraHeaders: [
"X-App-Version": "1.0.0",
"X-Client-Platform": "iOS"
]
)
// 2. Instantiate the engine
let engine = try PhoneBuddyEngine(config: config)
// engine.setAgentName("Acme") // optional runtime rename; nil/empty resets to PhoneBuddy
// 3. Run agent turn with real-time streaming events
let outcome = try await engine.chat(
sessionId: "session-001",
userInput: "Analyze the quarterly sales data in data/sales.csv and compute total profit",
onEvent: { eventJson in
print("Real-time event: \(eventJson)")
}
)
print("Final response: \(outcome.finalText)")
print("Turns used: \(outcome.turnsUsed)")See full iOS demo at examples/ios/
Place libphone_buddy_ffi.so into app/src/main/jniLibs/<abi>/ and include NativeAgent.kt:
import org.phonebuddy.NativeAgent
import org.phonebuddy.EventListener
import org.json.JSONObject
// 1. Prepare configuration JSON
val config = JSONObject().apply {
put("api_key", "your-api-key-or-jwt-token")
put("base_url", "https://api.x.ai/v1")
put("model", "grok-4.6")
put("api_backend", "responses")
put("root_dir", context.filesDir.resolve("workspace").absolutePath)
put("max_turns", 24)
put("enable_web_search", false)
put("agent_name", "Acme")
put("extra_headers", JSONObject().apply {
put("X-App-Version", "1.0.0")
put("X-Client-Platform", "Android")
})
}
// 2. Create agent instance
val agent = NativeAgent(config.toString(), context)
// agent.setAgentName("Acme") // optional runtime rename; null/blank resets to PhoneBuddy
// 3. Execute chat turn
val resultJson = agent.chat(
sessionId = "session-001",
userInput = "Analyze the quarterly sales data in data/sales.csv and compute total profit",
listener = object : EventListener {
override fun onEvent(eventJson: String) {
println("Real-time event: $eventJson")
}
}
)
val outcome = JSONObject(resultJson ?: "{}")
println("Final response: ${outcome.optString("final_text")}")See full Android demo at examples/android/
#include <stdio.h>
#include "phone_buddy.h"
void on_event(const char *event_json, void *user_data) {
printf("[Event] %s\n", event_json);
}
int main(void) {
const char *config_json = "{"
"\"api_key\":\"your-api-key\","
"\"base_url\":\"https://api.x.ai/v1\","
"\"model\":\"grok-4.6\","
"\"root_dir\":\"/tmp/phone-buddy-workspace\""
"}";
char *err = NULL;
PbEngine *engine = pb_engine_new(config_json, &err);
if (!engine) {
fprintf(stderr, "Engine init failed: %s\n", err);
pb_string_free(err);
return 1;
}
char *result = pb_engine_chat(engine, "session-001", "Hello Agent", on_event, NULL, &err);
if (result) {
printf("Result: %s\n", result);
pb_string_free(result);
} else {
fprintf(stderr, "Chat error: %s\n", err);
pb_string_free(err);
}
pb_engine_free(engine);
return 0;
}See C demo at examples/c_demo/
Use the built-in CLI for local debugging, automated self-tests, and live chat:
# 1. Run offline scripted demo (analyzes sample data without API key)
cargo run -p phone-buddy-cli -- mock
# 2. Run built-in tool self-test (exercises BusyBox, JS engine, and grep directly)
cargo run -p phone-buddy-cli -- self-test
# 3. Run real LLM interactive turn
PHONEBUDDY_API_KEY="your-api-key" cargo run -p phone-buddy-cli -- chat "Analyze the sales numbers"PhoneBuddy SDK supports 1:1 network request emulation of official desktop AI coding agent clients (xAI Grok Build, OpenAI Codex, and Anthropic Claude Code), replicating exact HTTP Headers (User-Agent, anthropic-version, anthropic-beta, x-grok-*, session-id), thinking signatures, and JSON wire schemas.
use phone_buddy::prelude::*;
// Fluent 1:1 Claude Code configuration:
let config = EngineConfig::for_claude_code("sk-ant-...", "claude-opus-5")
.url("https://api.anthropic.com/v1")
.build()?;For complete protocol matrices, headers, and multi-language usage, see docs/client_profiles.md.
During execution, PhoneBuddyEngine emits structured JSON event objects to the registered callback handler:
| Event Type | Field / Payload | Description |
|---|---|---|
TextDelta |
{"text": "..."} |
Streaming delta chunk of assistant text response |
ReasoningDelta |
{"text": "..."} |
Streaming delta chunk of LLM reasoning / thinking |
ToolCallStart |
{"call_id": "...", "name": "...", "arguments_json": "..."} |
Emitted immediately before a tool begins executing |
ToolCallResult |
{"call_id": "...", "name": "...", "ok": true, "output": "..."} |
Emitted when a tool finishes execution with its output |
PlanUpdated |
{"items_json": "..."} |
Emitted whenever the planning tool updates the task step list |
Completed |
{"final_text": "...", "usage": { ... }} |
Emitted when the turn finishes successfully |
Failed |
{"message": "..."} |
Emitted when an unrecoverable failure occurs during the turn |
Retrying |
{"provider": "host/model", "attempt": 2, "max_attempts": 3, "wait_ms": 2000, "reason": "..."} |
Waiting before retrying the current provider. provider is a desensitized host/model id |
ProviderSwitched |
{"from": "...", "to": "...", "reason": "...", "cooldown_ms": 120000} |
Current provider degraded; engine moved to the next fallback endpoint |
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
api_key |
String | Yes (in HTTP mode) | "" |
User API Key or Bearer Token |
base_url |
String | Yes (in HTTP mode) | https://api.x.ai/v1 |
OpenAI / xAI / Anthropic compatible endpoint URL |
model |
String | Yes | grok-4.6 |
Model identifier (e.g. grok-4.6, claude-opus-5, gpt-4o) |
client_profile |
String | No | default |
1:1 client emulation preset: grok_build, codex, claude_code, default. See docs/client_profiles.md |
client_version |
String | No | null |
Custom client version string to report in User-Agent |
client_session_id |
String | No | null |
Custom session UUID for vendor headers (x-claude-code-session-id, session-id) |
root_dir |
String | Yes | /tmp/phone-buddy |
Root path for jailed file sandbox and session storage |
api_backend |
String | No | chat_completions |
API protocol: responses (SSE stream), chat_completions, messages |
llm_mode |
String | No | http |
Transport mode: http (direct networking) or host (FFI callback bridge) |
locale |
String | No | zh |
UI locale instructing agent response language |
max_turns |
Integer | No | 24 |
Maximum tool loop turns per user turn |
temperature |
Float | No | 0.2 |
Model sampling temperature |
max_output_tokens |
Integer | No | 8192 |
Maximum output token generation limit |
enable_web_search |
Boolean | No | false |
Attach Grok hosted {type: web_search} on Responses (Grok Build supports_backend_search). Client DuckDuckGo/web_search function tool stays registered independently. Leave off for gateways that do not implement hosted search (e.g. PackyAPI). |
agent_name |
String | No | PhoneBuddy |
Identity used in the system prompt (You are {agent_name}…). Empty falls back to PhoneBuddy. |
system_prompt_extra |
String | No | null |
Custom persona or product instructions appended to system prompt |
stream_idle_timeout_secs |
Integer | No | 120 |
Streaming connection idle timeout in seconds |
max_retries |
Integer | No | 5 |
Maximum exponential backoff retry attempts in single-provider mode. Ignored for failover decisions when fallback_providers is set. See docs/retry-and-provider-fallback.md |
fallback_providers |
Array | No | [] |
Ordered backup HTTP endpoints. Non-empty enables chain mode (fast-fail ~6s per provider, then switch). Empty keeps historical single-provider behaviour |
failover_max_attempts |
Integer | No | 3 |
Chain mode only: total tries per provider per request (initial + 2 retries) |
provider_cooldown_secs |
Integer | No | 120 |
Chain mode: seconds a tripped provider sits out before a passive re-probe (doubles on consecutive trips, cap 600) |
provider_group |
String | No | client profile name | Compatibility group for encrypted reasoning. Same group + same model keeps rs_* ids and encrypted thinking across host failover. See docs/retry-and-provider-fallback.md |
extra_headers |
Object | No | {} |
Custom HTTP headers sent with LLM requests (X-App-Version, etc.) |
extra_body |
Object | No | {} |
Custom JSON fields merged into top level of LLM request payload |
enable_doom_loop_check |
Boolean | No | Auto | Server-side doom loop header (x-grok-doom-loop-check) |
web_fetch_allow_local |
Boolean | No | false |
Allow web_fetch to access local loopback addresses (for testing) |
http_dump |
Object | No | {"mode":"off"} |
Raw HTTP request/response dumper for debugging (e.g. {"mode":"on_error","max_files":30}) |
When troubleshooting network connectivity issues, authentication errors, API gateway failures, or unexpected HTTP response codes (e.g. 4xx / 5xx) on mobile clients, configure http_dump to record complete raw HTTP request and response exchanges to disk:
{
"api_key": "your-access-token",
"base_url": "https://api.example.com/v1",
"model": "grok-4",
"http_dump": {
"mode": "on_error",
"mask_sensitive": true,
"max_files": 30
}
}mode:"off"(default): Traffic dumping disabled."on_error": Dumps raw HTTP exchange only when a request fails (non-2xx HTTP status or network timeout/connection error)."all": Dumps all HTTP exchanges (both 200 OK handshakes and failures).
dump_dir: Custom output directory. Defaults to<root_dir>/.phonebuddy/http_dumps/.mask_sensitive: Mask sensitive headers (Authorization,x-api-key,cookie, etc.). Defaults totrue.max_files: Maximum number of dump JSON files retained before automatically rotating out the oldest files (FIFO rotation). Defaults to30.
When an error occurs, the SDK error message includes the absolute dump path:
Completion error: Error: LLM request failed: status=500 { "error": ... } [HTTP dump: /data/user/0/com.example.app/files/sandbox/.phonebuddy/http_dumps/dump_20260821_211530_500_req_a1b2c3.json]
Each dump file captures:
{
"schema_version": "1.0",
"request_id": "req_a1b2c3d4",
"timestamp": "2026-08-21T21:15:30.123+08:00",
"duration_ms": 352,
"request": {
"method": "POST",
"url": "https://api.example.com/v1/chat/completions",
"headers": {
"accept": "text/event-stream",
"authorization": "Bearer sk-p***3456",
"content-type": "application/json"
},
"body": { "model": "grok-4", "messages": [...], "stream": true }
},
"response": {
"status": 502,
"status_text": "Bad Gateway",
"headers": {
"content-type": "text/html; charset=UTF-8",
"server": "nginx/1.22.1",
"x-gateway-trace-id": "gw-987654321"
},
"body_text": "<html><head><title>502 Bad Gateway</title></head><body>...</body></html>"
},
"error": "status=502 <html><head><title>502 Bad Gateway..."
}PhoneBuddySDK/
├── Cargo.toml # Workspace configuration
├── crates/
│ ├── phone-buddy/ # Core Rust Agent engine (planning, tools, sandbox, subagents)
│ ├── phone-buddy-ffi/ # C ABI export layer (exports phone_buddy.h)
│ └── phone-buddy-cli/ # Local CLI developer & testing tool
├── dist/ # Compiled SDK release packages
│ ├── ios/ # iOS static libraries (.a) & header
│ └── android/ # Android shared libraries (.so) & header
├── examples/ # Native mobile app and C demos
│ ├── ios/ # Swift wrapper (PhoneBuddy.swift) & SwiftUI demo app
│ ├── android/ # Kotlin wrapper (NativeAgent.kt), JNI bridge & Compose demo app
│ └── c_demo/ # Standard C API usage sample (main.c)
└── scripts/ # Cross-compilation and distribution packaging scripts
├── build-ios-sdk.sh # iOS cross-compilation (aarch64 / simulator universal)
├── build-android-sdk.sh # Android cross-compilation (arm64-v8a / x86_64)
└── package-sdk.sh # One-shot distribution ZIP bundler
- Rust
1.94+with mobile target toolchains:rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios rustup target add aarch64-linux-android x86_64-linux-android
- Xcode 14+ (for iOS builds)
- Android NDK r25+ (for Android builds, export
ANDROID_NDK_HOME)
# Check Rust compilation
cargo check
# Run unit, integration, and E2E tests
cargo test
# Build iOS static libraries (device + simulator)
./scripts/build-ios-sdk.sh
# Build Android dynamic shared libraries
./scripts/build-android-sdk.sh
# One-click build and install demo on iOS device / simulator:
./examples/ios/build-and-install.sh [--simulator | --device]
# One-click build and install demo on Android phone:
./examples/android/build-and-install.sh [--logs]
# Package release distribution artifacts into dist/
./scripts/package-sdk.sh --build- Zero Subprocess Guarantee: All operations (BusyBox shell commands, JavaScript execution, file edits) run entirely in-process in pure Rust. No
fork,exec, or shell processes are ever invoked. - Strict File Jailing: The file sandbox prevents directory traversal attacks (
../) and enforces boundary checks againstroot_dir. - SSRF Guard: The
web_fetchtool blocks internal loopback and RFC 1918 private network address ranges by default. - Panic Safety: All Rust panics at the C FFI boundary are caught via
std::panic::catch_unwindto prevent undefined behavior across foreign language boundaries.
PhoneBuddy SDK is built upon the open-source foundations established by the AI agent community. We would like to express our gratitude to:
- xAI for open-sourcing grok-build, whose core agent harness architecture, task planning paradigms, and doom-loop detection algorithms serve as the upstream foundation for PhoneBuddy SDK.
- The Boa team for providing a robust pure-Rust ECMAScript engine that powers our safe mobile code execution sandbox.
- The Rustls project for pure-Rust memory-safe TLS transport.
This project is licensed under the Apache License 2.0.