Skip to content

resolve-kit/resolvekit-android-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

ResolveKit Android SDK

CI Maven Central License Min SDK

Android SDK for embedding ResolveKit agent chat, tool calling, and host UI surfaces into Android apps.

Support is moving into the product. ResolveKit is where it lands.

📱 Preview

Chat View Tool Call Approval Activity Integration
Chat View Tool Approval Activity
Live agent chat with streaming responses User approval for destructive tool calls Full-screen and embedded host surfaces

Note: Screenshots are placeholders. Replace docs/assets/screenshots/ with actual captures from the sample app.

Demo GIF

ResolveKit Demo

End-to-end flow: SDK init → chat session → tool call → approval → result


🏷️ Featured In

Android Arsenal Kotlin Weekly Android Libs


🏗️ Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Your Android App                      │
├─────────────────────────────────────────────────────────────┤
│                                                               │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐   │
│  │  Compose UI  │    │   Activity   │    │   Fragment   │   │
│  │ChatView      │    │  ChatActivity│    │ ChatFragment │   │
│  └──────┬───────┘    └──────┬───────┘    └──────┬───────┘   │
│         │                   │                   │            │
│         └───────────────────┼───────────────────┘            │
│                             ▼                                │
│                  ┌─────────────────────┐                     │
│                  │    ResolveKitRuntime │                     │
│                  │  (Session lifecycle, │                     │
│                  │   event handling,    │                     │
│                  │   tool call mgmt)    │                     │
│                  └─────────┬───────────┘                     │
│                            │                                 │
│          ┌─────────────────┼─────────────────┐              │
│          ▼                 ▼                 ▼              │
│  ┌───────────────┐ ┌───────────────┐ ┌───────────────┐     │
│  │    core/      │ │  networking/  │ │   authoring/  │     │
│  │  Types,       │ │  API client,  │ │  @ResolveKit  │     │
│  │  Registry,    │ │  Event stream │ │  annotation,  │     │
│  │  Functions    │ │  (WebSocket)  │ │  KSP gen      │     │
│  └───────────────┘ └───────────────┘ └───────┬───────┘     │
│                                               │              │
│                                    ┌──────────▼──────────┐  │
│                                    │        ksp/          │  │
│                                    │  Code generation     │  │
│                                    │  for tool adapters   │  │
│                                    └─────────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│                         sdk/ (Facade)                        │
│  Single dependency that bundles ui + core + networking       │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
                  ┌───────────────────────┐
                  │   ResolveKit Backend   │
                  │  (Session management,  │
                  │   LLM routing, agent   │
                  │   orchestration)       │
                  └───────────────────────┘

The repository contains:

  • a Kotlin-first runtime for ResolveKit sessions
  • a Jetpack Compose chat UI
  • Activity and Fragment host surfaces for non-Compose apps
  • a KSP-based authoring path for defining tools with @ResolveKit
  • a sample Android app

Modules

Module Purpose
sdk Public facade artifact that pulls in the default ResolveKit runtime and chat UI stack
core Shared JSON types, tool definitions, registry, and errors
networking ResolveKit API and event stream clients
ui Runtime, Compose chat view, ResolveKitChatActivity, and ResolveKitChatFragment
authoring @ResolveKit annotation and authoring interfaces
ksp KSP processor that generates tool adapters
sample Reference Android application

Requirements

Requirement Value
Min SDK 26 (Android 8.0)
Target SDK 36
JDK 17
Android Studio Ladybug+
Gradle 8.9+ (wrapper included)
Compose BOM 2024.02.00
Kotlin 1.9.22

Installation

ResolveKit is set up to publish these coordinates:

app.resolvekit:sdk:<version>
app.resolvekit:authoring:<version>
app.resolvekit:ksp:<version>

core, networking, and ui remain published for advanced/internal use, but sdk is the default dependency for app consumers.

Maven Central

For most apps, depend on sdk. It brings in the default ResolveKit runtime and UI stack.

dependencies {
    implementation("app.resolvekit:sdk:1.0.1")
}

If you want the annotation-based tool authoring path:

plugins {
    id("com.google.devtools.ksp")
}

dependencies {
    implementation("app.resolvekit:sdk:1.0.1")
    implementation("app.resolvekit:authoring:1.0.1")
    ksp("app.resolvekit:ksp:1.0.1")
}

GitHub Packages

Use this when you need preview artifacts before a Maven Central release.

repositories {
    google()
    mavenCentral()
    maven {
        url = uri("https://maven.pkg.github.com/resolve-kit/resolvekit-android-sdk")
        credentials {
            username = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
            password = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN")
        }
    }
}

Then use the same coordinates shown above.

Local Maven for Android Studio testing

If you are working on the SDK locally and want to consume it from another app without publishing externally:

./gradlew publishToMavenLocal

Then add mavenLocal() to the consuming app:

repositories {
    mavenLocal()
    google()
    mavenCentral()
}

Composite build from another app

For active SDK development, composite builds are usually the best Android Studio workflow.

In the host app settings.gradle.kts:

includeBuild("../resolvekit-android-sdk")

Then depend on the project coordinates normally:

dependencies {
    implementation("app.resolvekit:sdk:1.0.1")
    implementation("app.resolvekit:authoring:1.0.1")
    ksp("app.resolvekit:ksp:1.0.1")
}

Gradle will substitute the local modules automatically.

Quick Start

Compose-first integration

val runtime = ResolveKitRuntime(
    configuration = ResolveKitConfiguration(
        apiKeyProvider = { "rk_your_key_here" },
        functions = listOf(GetCurrentTime)
    ),
    context = applicationContext
)

setContent {
    MaterialTheme {
        ResolveKitChatView(runtime = runtime)
    }
}

Launch as a screen from a View-based app

startActivity(
    ResolveKitChatActivity.createIntent(
        context = this,
        configuration = ResolveKitConfiguration(
            apiKeyProvider = { "rk_your_key_here" },
            functions = listOf(GetCurrentTime)
        )
    )
)

Embed as a Fragment

supportFragmentManager.beginTransaction()
    .replace(
        R.id.container,
        ResolveKitChatFragment.newInstance(
            ResolveKitConfiguration(
                apiKeyProvider = { "rk_your_key_here" },
                functions = listOf(GetCurrentTime)
            )
        )
    )
    .commit()

Configuration Reference

ResolveKitConfiguration is passed to ResolveKitRuntime at initialization and is immutable after that point.

ResolveKitConfiguration(
    baseUrl: String = "https://agent.example.com",
    apiKeyProvider: () -> String?,
    deviceIdProvider: () -> String? = { null },
    llmContextProvider: () -> JSONObject = { emptyMap() },
    availableFunctionNamesProvider: (() -> List<String>)? = null,
    localeProvider: (() -> String)? = null,
    preferredLocalesProvider: (() -> List<String>)? = null,
    functions: List<AnyResolveKitFunction> = emptyList(),
    functionPacks: List<ResolveKitFunctionPack> = emptyList(),
    context: Context
)

baseUrl

Type: String | Required: No | Default: https://agent.example.com

Base URL of the ResolveKit backend. Override only when self-hosting:

baseUrl = "https://your-backend.example.com"

apiKeyProvider

Type: () -> String? | Required: Yes

Called at the start of each session. Return null or an empty string to block connection.

apiKeyProvider = { SecureConfig.getApiKey() }

deviceIdProvider

Type: () -> String? | Required: No | Default: { null }

Stable device or user identifier used to correlate sessions across app launches. If null is returned, the SDK generates and persists a UUID automatically.

deviceIdProvider = {
    val prefs = PreferenceManager.getDefaultSharedPreferences(context)
    prefs.getString("device_id", null) ?: run {
        val id = UUID.randomUUID().toString()
        prefs.edit().putString("device_id", id).apply()
        id
    }
}

llmContextProvider

Type: () -> JSONObject | Required: No | Default: { emptyMap() }

Custom JSON context sent as llm_context during session creation. Use it to pass user preferences, location, app state, or any signal the agent needs at routing time.

localeProvider

Type: (() -> String)? | Required: No | Default: null

Provides the preferred locale for the chat session as a BCP 47 language tag (e.g. "en", "lt", "fr-CA"). If null, the SDK resolves locale from system settings.

functions

Type: List<AnyResolveKitFunction> | Required: No | Default: emptyList()

List of tool functions available to the agent.

Runtime API

ResolveKitRuntime exposes the following state via StateFlow:

Property Type Description
messages StateFlow<List<ResolveKitChatMessage>> Chat transcript in chronological order
connectionState StateFlow<ResolveKitConnectionState> Current session-stream connection phase
isTurnInProgress StateFlow<Boolean> True while the agent is processing a turn
toolCallChecklist StateFlow<List<ToolCallChecklistItem>> Live checklist of tool calls in current batch
toolCallBatchState StateFlow<ResolveKitToolCallBatchState> Aggregate approval state of current batch
toolCallBatches StateFlow<List<ToolCallChecklistBatch>> Historical tool call batches
lastError StateFlow<String?> Last error message
chatTitle StateFlow<String> Chat window title
messagePlaceholder StateFlow<String> Input placeholder text
appearanceMode StateFlow<ResolveKitAppearanceMode> Appearance mode
currentLocale StateFlow<String> Current session locale
chatTheme StateFlow<ChatTheme?> Active chat theme
executionLog StateFlow<List<String>> Debug log of runtime lifecycle events

Connection States

State Description
IDLE Not connected (initial state)
REGISTERING Registering device with backend
CONNECTING Establishing WebSocket connection
ACTIVE Session established, ready for chat
RECONNECTING Connection lost, attempting reconnect
RECONNECTED Successfully reconnected after loss
FAILED Connection failed with error
BLOCKED Connection blocked (e.g., invalid API key)

Defining Tools

Manual tool registration

object GetCurrentTime : AnyResolveKitFunction {
    override val resolveKitName = "get_current_time"
    override val resolveKitDescription = "Returns the current local time"
    override val resolveKitParametersSchema = mapOf(
        "type" to JSONValue.String("object"),
        "properties" to JSONValue.Object(emptyMap())
    )
    override val resolveKitTimeoutSeconds = 5
    override val resolveKitRequiresApproval = false

    override suspend fun invoke(
        arguments: JSONObject,
        context: ResolveKitFunctionContext
    ): JSONValue = JSONValue.String("12:00:00 UTC")
}

KSP authoring with @ResolveKit

@ResolveKit(
    name = "echo_message",
    description = "Echoes back the provided message",
    requiresApproval = false
)
class EchoMessage(private val message: String) : ResolveKitFunction {
    override suspend fun perform(): Any? = message
}

The processor generates EchoMessageResolveKitAdapter, which can be passed into ResolveKitConfiguration.functions.

Supported Parameter Types

Kotlin type JSON Schema type LLM coercion
String "string" Tolerates numbers/bools
Boolean "boolean" Tolerates 1/0/"true"/"false"
Int, Long "integer" Truncates 3.0 → 3
Float, Double "number"
T? (any of above) Same as T, not in required null if key absent
List<T> "array" with "items" schema
Map<String, V> "object"
Nested data class "object"

ProGuard / R8 Rules

Add these rules to your app's proguard-rules.pro to prevent obfuscation of ResolveKit classes:

# ResolveKit
-keep class app.resolvekit.** { *; }
-keep class app.resolvekit.core.** { *; }
-keep class app.resolvekit.ui.** { *; }
-keep class app.resolvekit.networking.** { *; }

# KSP-generated adapters
-keep class **ResolveKitAdapter { *; }

# JSON serialization
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
}

# Keep function names for tool dispatch
-keepnames class app.resolvekit.authoring.** { *; }

Android Studio Workflows

Work on the SDK itself

  1. Open this repository in Android Studio.
  2. Ensure local.properties points at your Android SDK:
sdk.dir=/path/to/Android/sdk
  1. If you want to run the sample app, also add:
resolvekit.apiKey=rk_your_key_here
resolvekit.baseUrl=https://agent.resolvekit.app

You can start from local.properties.example and copy it to local.properties.

  1. Select the sample run configuration or run:
./gradlew :sample:installDebug

The sample app now has a clear two-step test flow:

  • Step 1: configuration screen (host + API key are required before continue)
  • Step 2: capabilities screen with supported tool calls, prompt examples, and one Open Chat CTA

After tool calls run in chat, return to the capabilities screen to see updated app state (vibe, mascot, confetti counter, laser state). This demonstrates SDK tool calls driving visible host-app behavior.

Use the SDK source from another Android Studio project

Recommended options:

  1. includeBuild("../resolvekit-android-sdk") for day-to-day local development.
  2. publishToMavenLocal when you want artifact-style consumption without an external registry.

Avoid manually copying AARs between projects unless you specifically need a binary handoff.

Building and Testing

./gradlew test
./gradlew :sample:assembleDebug
./gradlew publishToMavenLocal

Publishing

Shared publishing logic lives in gradle/publish.gradle.kts.

Configured targets:

  • Maven Central
  • GitHub Packages
  • local Maven via publishToMavenLocal

Expected release credentials are read from Gradle properties or environment variables:

  • MAVEN_CENTRAL_USERNAME / MAVEN_CENTRAL_PASSWORD
  • GITHUB_PACKAGES_USERNAME / GITHUB_PACKAGES_PASSWORD
  • SIGNING_KEY_BASE64 / SIGNING_PASSWORD

Legacy OSSRH_USERNAME and OSSRH_PASSWORD are also accepted for compatibility.

For local setup, copy .env.example to .env and fill in the values. .env is loaded by the publishing script and is gitignored.

For Maven Central releases with Gradle's built-in maven-publish, the artifact upload and the Portal release handoff are separate steps. The GitHub Actions workflow performs both automatically. For a local release, run:

./gradlew publishAllPublicationsToMavenCentralRepository

auth="$(printf '%s:%s' "$MAVEN_CENTRAL_USERNAME" "$MAVEN_CENTRAL_PASSWORD" | base64 | tr -d '\n')"
curl --fail --silent --show-error \
  -X POST \
  -H "Authorization: Bearer $auth" \
  "https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/app.resolvekit?publishing_type=automatic"

After the handoff call succeeds, Sonatype may keep the deployment in PUBLISHING state for several minutes before the modules appear in Maven Central search.

GitHub Actions publishing is defined in .github/workflows/publish.yml. GitHub Packages uses GITHUB_TOKEN; Maven Central publishing runs only when these repository secrets are configured:

  • MAVEN_CENTRAL_USERNAME
  • MAVEN_CENTRAL_PASSWORD
  • SIGNING_KEY_BASE64
  • SIGNING_PASSWORD

Troubleshooting

Connection fails with 401

  • Verify your API key starts with rk_
  • Check that baseUrl points to a running ResolveKit backend
  • Ensure the backend has an app configured for your API key

Tool calls not appearing in chat

  • Verify functions are registered in ResolveKitConfiguration.functions
  • Check that the function name matches exactly (snake_case)
  • Ensure the KSP processor ran: ./gradlew build should generate *ResolveKitAdapter classes

Compose UI not rendering

  • Ensure ResolveKitRuntime is created with a valid Context
  • Check that MaterialTheme wraps ResolveKitChatView
  • Verify Compose BOM version compatibility

ProGuard crashes in release builds

  • Add the ProGuard rules from the ProGuard / R8 Rules section above
  • Run ./gradlew :sample:assembleRelease to test with minification enabled

Repository Hygiene

This repo includes:

  • LICENSE (MIT)
  • CONTRIBUTING.md
  • SECURITY.md
  • GitHub issue templates
  • CI workflow for build, test, and local publication validation

Development Notes

  • local.properties is intentionally ignored.
  • Do not commit API keys or publishing credentials.
  • Public API changes should be reflected in this README.
  • RESOLVEKIT_BASE_URL (or resolvekit.baseUrl in local.properties) controls the backend URL used by the sample app.

Packages

 
 
 

Contributors