Skip to content

About

Embeddable agent runtime (agent harness) for Android and the JVM: agent loop, sandboxed workspace, MCP, skills, subagents, hooks. Bring your own model.

Topics

Resources

Contributing

Security policy

Stars

13 stars

Watchers

1 watching

Forks

Latest commit

 

History

141 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Android Agent Runtime

CI Maven Central License: MIT Kotlin Android API

English | 中文

An embeddable agent runtime (an agent harness) for Android and the JVM. It runs a full agent loop inside your app's process: a sandboxed workspace the agent reads and writes, tools from your code and from MCP servers, skills and subagents, hooks and policy around every tool call, and durable sessions. Bring any OpenAI- or Anthropic-compatible model.

What you get

  • The agent loop, on the device. Native tool calling with streaming, thinking, prompt caching and server-side web search where the provider has them; retries, budgets and conversation compaction that summarizes old turns.
  • A sandboxed workspace. A virtual file system the agent works in; mount app directories, assets, in-memory sources or a folder the user picks (SAF). Nothing outside what you mount is reachable.
  • Tools. Plain Kotlin functions (functionTools), any ToolProvider, and remote MCP servers over Streamable HTTP (McpHttpClient, built in). With many tools the agent searches for the right ones instead of seeing them all.
  • Skills, AGENTS.md and subagents. Skills load on demand; the agent can delegate to child agents and wait for them.
  • Control. Hooks before and after tool calls, a policy engine, confirmation for network and side-effecting actions, and a per-provider concurrency limit.
  • Durable sessions. An append-only event log per session, resumable runs, SQLite session storage on Android.
  • Optional modules. Operate other apps' screens through accessibility (Android Use), sandboxed JavaScript that calls tools, calendar, notification and media tools, and A2A client and server.

Install

dependencies {
    implementation(platform("cc.noharry:agent-bom:0.5.0"))
    implementation("cc.noharry:agent-runtime-android") // an Android app
    // implementation("cc.noharry:agent-runtime")      // the JVM: a server, a desktop tool, tests
}

Kotlin 2.0 or later (the artifacts are built at language level 2.1), JDK 17, Android minSdk 23. The workspace and the event log use java.nio.file, which Android has from API 26: below that, enable core library desugaring with com.android.tools:desugar_jdk_libs_nio 2.1.5 or later (as samples/quickstart-android does).

Quickstart (Android)

val tools = functionTools("device") {
    tool("device_info", "The phone or tablet this app runs on.", readOnly = true) {
        "${Build.MANUFACTURER} ${Build.MODEL}, Android ${Build.VERSION.RELEASE}"
    }
}

val runtime = AndroidAgentRuntime.builder(context) // workspace, SQLite sessions, event log, history
    .model(OpenAiModelClient({ apiKey }, model = "gpt-4o-mini", stream = true))
    .tools(tools)
    .build()

scope.launch {
    runtime.startThread().runStreamed("What device am I on?")
        .collect { event -> if (event is ThreadEvent.ItemDelta) answer.append(event.text) }
}

Use thread.run(text) for a complete TurnResult, or runStreamed for eight event types. Each item has a stable id, and ItemCompleted includes its full content. An Approval item waits in the same stream until approve() or deny(); ask finishes with WAITING_FOR_ANSWER, and the next turn supplies the answer. thread.steer(text) adds input while a turn runs; thread.interrupt() cancels it. Configure .storage(directory) on the JVM to restore model history and thread.items() after restarting; Android configures private storage automatically. Live tool payloads are original; stored item snapshots use the configured trace redactor.

Keep API keys off the device in a real app: point baseUrl at your own backend.

Quickstart (JVM, with an MCP server)

val deepWiki = McpToolProvider(
    McpHttpClient("https://mcp.deepwiki.com/mcp", headers = { emptyMap() }, http = OkHttpHttpTransport(),
        clientName = "my-app", clientVersion = "1.0"),
    serverId = "deepwiki",
)

val runtime = AgentRuntime.builder()
    // Any OpenAI-compatible endpoint: pass its baseUrl. AnthropicModelClient speaks the Messages API.
    .model(OpenAiModelClient({ System.getenv("API_KEY") }, model = "gpt-4o-mini", stream = true))
    .tools(deepWiki)
    .workspaceRoot(File("agent-workspace"))
    .build()

The samples run as they are: AGENT_API_KEY=… ./gradlew :samples:quickstart-jvm:run --args="…", and samples/quickstart-android is a one-screen app. ./gradlew :samples:extensions:run needs no key or network: a model of your own (a scripted one standing in for an on-device model), tools from your code, and a hook that refuses a call.

Modules

Artifact What it is
agent-runtime The runtime and host facade (plain JVM): AgentRuntimeBuilder, OpenAI- and Anthropic-compatible model clients, MCP, functionTools, skills, subagents, sessions and memory, web search and fetch, hooks and policy, and the workspace's virtual file system
agent-runtime-android The Android layer: AndroidAgentRuntime.builder(context), SQLite sessions, the app-private event log, and mounting a folder the user picks through the Storage Access Framework
agent-api Android-neutral Thread / Turn / Item contracts and stable model, tool, hook and event-log extension interfaces
agent-exec-js Optional: exec_js, sandboxed JavaScript (QuickJS through Zipline) that can call tools; the app server's scripts (plugin packages' tools written in JavaScript) need it too
agent-device-capabilities Optional: calendar, notification and media tools; with the app server, also their plugin (device-tools), loaded by default
agent-android-use Optional: observe and operate the screen through an accessibility service; with the app server, also its plugin (android-use), loaded by default
agent-a2a Optional (plain JVM): delegate tasks to remote A2A agents, and expose the agent as a local A2A endpoint. Two clients: a direct JSON-RPC/SSE one speaking A2A 0.3, and one over the official A2A Java SDK speaking A2A 1.0, whose SDK the app adds (below)
agent-acp Optional (plain JVM): serve the agent's threads over the Agent Client Protocol (ACP) to editors and other ACP clients, on stdio or any stream
agent-bom One version for all of the above and the app server's three artifacts below

An Android app depends on agent-runtime-android; a server, desktop tool or test uses agent-runtime. Optional modules depend at most on agent-api. The loop implementation is internal to agent-runtime; samples/minimal shows a host with only a model.

The app server (experimental) makes an Android app an agent: one runtime behind a JSON-RPC protocol of threads, turns and items, which every front end reaches as a client, and a plugin kernel the rest comes through. Its guide for hosts is agent-app-server/README.md; samples/agent-app-sample* are four apps on it.

Artifact What it is
agent-app-protocol The protocol (plain JVM): requests, notifications and approvals over threads, turns and items, the state a front end renders, and its client (ServerClient)
agent-app-server The server (Android): sessions, models, MCP, skills, subagents and usage behind the protocol, the plugin kernel (AppServerPlugin, PluginHost) and plugin packages a person installs; a host installs it with an AppServerConfig
agent-app-models Optional (Android): the model services' plugin, loaded by default once on the classpath: DeepSeek (with its balance, search and relay check), OpenAI, Anthropic, Google Gemini, xAI, OpenRouter, Groq, Moonshot (Kimi), Zhipu (GLM), Alibaba Cloud Model Studio (Qwen), Volcengine Ark (Doubao), MiniMax and SiliconFlow: where each is reached, its models' facts and prices, how its failures read

The core speaks the wire protocols (OpenAI Chat Completions and Responses, the Anthropic Messages API) and knows no service: with it alone a profile reaches any compatible service, with conservative defaults. Three published modules bring a plugin that the server loads once the app depends on them, unlisted: agent-app-models, and agent-android-use and agent-device-capabilities for operating the screen and the device's tools (those two only compile against the server, so an app with the runtime alone gets their tools without it). A host's own plugins are its code (AppServerPlugin). The other first-party ones (cards, drawn pages, memory, pictures, plans, A2A) are source modules of the Bothy app, built into it rather than published as Maven artifacts, so they change without a release of this one; their source is published with Bothy's next release. The core names no plugin's package and no model service, and carries none of the runtime modules only a plugin brings (agent-a2a, agent-android-use, agent-device-capabilities, agent-app-models; checkAppServerLayering, part of ciCheck).

The official A2A client

agent-a2a is compiled against the official A2A Java SDK 1.1.0.Final without carrying it, so the direct client (DirectA2aClientModule) needs nothing more. An app that uses OfficialA2aClientModule adds the SDK, without its a2a-java-sdk-common (agent-a2a has its own copy, which Android accepts); on Android the SDK's jakarta jars carry the same notice files, of which the app keeps one:

dependencies {
    implementation("cc.noharry:agent-a2a")
    implementation("org.a2aproject.sdk:a2a-java-sdk-client:1.1.0.Final") {
        exclude(group = "org.a2aproject.sdk", module = "a2a-java-sdk-common")
    }
    implementation("org.a2aproject.sdk:a2a-java-sdk-http-client-android:1.1.0.Final") {
        exclude(group = "org.a2aproject.sdk", module = "a2a-java-sdk-common")
    }
}

android {
    packaging { resources.pickFirsts += setOf("META-INF/LICENSE.md", "META-INF/NOTICE.md", "META-INF/beans.xml") }
}

Without the SDK, OfficialA2aClientModule() throws an IllegalStateException naming these artifacts, and a release shrunk by R8 that names it does not build. The SDK's R8 rules come in agent-a2a's jar. Below Android 7.0 the SDK needs core library desugaring, as the runtime does below 8.0.

API stability

Starting with 0.5.0, the Thread / Turn / Item API and the main extension interfaces in cc.noharry.agent.spi are stable. Advanced diagnostic and tuning APIs remain experimental. The changelog records migrations.

  • Declarations marked @ExperimentalAgentRuntimeApi or @ExperimentalA2aClientApi are the likeliest to change, and ask for an opt-in (without one, the runtime's are a compile error and the A2A client's a warning).
  • The app server's artifacts (agent-app-protocol, agent-app-server, agent-app-models) are experimental as a whole; their entry points (ServerClient, AppServer, AppServerConfig, AppServerPlugin) carry @ExperimentalAppServerApi, whose opt-in, without one, is a warning.
  • Declarations marked @InternalAgentRuntimeApi are not API: they are there for the runtime's own modules and tests, and may change or go in any release.
  • The public API of every module is recorded in its api/*.api, which CI compares with the code, so no change to it goes unnoticed.

Security reports: see SECURITY.md.

Building from source

To try changes not yet released, clone this repository next to your project and let Gradle build it from source; the same coordinates resolve to it:

// settings.gradle.kts of your project
includeBuild("../android-agent-runtime")

Showcase

Bothy is a tablet-first demo built on this runtime: a floating voice assistant, public MCP servers, plans that watch a server's state, generative UI, and a simulated car head unit to drive.

Building

JDK 17 and the Android SDK. ./gradlew ciCheck runs what CI runs: the source rules, the API compatibility check, the Android API level checks and lint, every unit test and the sample builds. See CONTRIBUTING.md.

License

MIT. Third-party code and native libraries keep their own licenses; see THIRD_PARTY_NOTICES.md.

About

Embeddable agent runtime (agent harness) for Android and the JVM: agent loop, sandboxed workspace, MCP, skills, subagents, hooks. Bring your own model.

Topics

Resources

Contributing

Security policy

Stars

13 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages