feat(android): linearize Android agent app branch - #30
Conversation
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_e30b6832-ad0a-4ef8-bd61-b6c6dd645844) |
There was a problem hiding this comment.
Pull request overview
Packages a new isolated mobile-agent-android/ workspace into a linear review surface, adding a buildable Kotlin/Android demo app plus a dedicated GitHub Actions workflow to assemble and upload APK artifacts.
Changes:
- Added an Android Gradle project (wrapper/scripts, app module, resources, and Kotlin sources) with an offline AgentLoop + EditGate demo UI.
- Introduced minimal core libraries: agent loop state machine, naive edit gate snapshot/diff/apply, OpenAI-compatible model router, and a GitHub REST client.
- Added CI workflow to build debug/release APKs on PRs/pushes affecting
mobile-agent-android/**.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| mobile-agent-android/README.md | Documents workspace scope/isolation, milestones, and current status. |
| mobile-agent-android/docs/DEVELOPMENT.md | Defines branch/scope rules and milestone checkpoint notes. |
| mobile-agent-android/docs/ARCHITECTURE.md | Draft architecture/principles and planned module layout. |
| mobile-agent-android/.gitignore | Ignores Gradle/IDE/build outputs for the new workspace. |
| mobile-agent-android/app-android/settings.gradle.kts | Sets up repositories and includes the :app module. |
| mobile-agent-android/app-android/build.gradle.kts | Pins AGP/Kotlin plugin versions for the workspace build. |
| mobile-agent-android/app-android/gradle.properties | AndroidX + Gradle JVM args settings. |
| mobile-agent-android/app-android/gradlew | Adds POSIX Gradle wrapper script for local/CI builds. |
| mobile-agent-android/app-android/gradlew.bat | Adds Windows Gradle wrapper script. |
| mobile-agent-android/app-android/gradle/wrapper/gradle-wrapper.properties | Pins Gradle distribution for the wrapper. |
| mobile-agent-android/app-android/gradle/wrapper/gradle-wrapper.jar | Gradle wrapper jar required for wrapper execution. |
| mobile-agent-android/app-android/app/build.gradle.kts | Android app module config (SDK levels, dependencies, build types). |
| mobile-agent-android/app-android/app/proguard-rules.pro | Placeholder ProGuard rules file. |
| mobile-agent-android/app-android/app/src/main/AndroidManifest.xml | Declares app, INTERNET permission, and launcher activity. |
| mobile-agent-android/app-android/app/src/main/res/values/themes.xml | Defines MaterialComponents-based app theme. |
| mobile-agent-android/app-android/app/src/main/res/values/strings.xml | UI strings for status/actions/settings and log text. |
| mobile-agent-android/app-android/app/src/main/res/layout/activity_main.xml | Main screen layout (status, config summary, log, controls). |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/App.kt | Application class stub. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/AppSettingsStore.kt | SharedPreferences-backed settings store for provider/GitHub config. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/MainActivity.kt | Control panel UI wiring: start/approve/reject loop, settings dialog, log rendering. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/core/agent/AgentState.kt | Agent loop state enum. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/core/agent/AgentLoop.kt | Offline demo loop: plan, human approval gate, edit gate preview/apply, trace logs. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/core/editgate/EditGate.kt | Snapshot/diff/apply primitives for the edit-gate demo. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/core/modelrouter/ModelRouter.kt | Provider interface, OpenAI-compatible chat-completions provider, ordered fallback routing. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/core/github/GitHubClient.kt | Minimal GitHub REST client helpers for branches/commits/PRs. |
| mobile-agent-android/app-android/app/src/main/kotlin/ai/openchat/mobile/agent/core/tools/Tool.kt | Tool interface and ToolResult model. |
| .github/workflows/mobile-agent-android.yml | CI workflow that builds debug/release APKs and uploads artifacts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| class AppSettingsStore(context: Context) { | ||
|
|
||
| private val prefs = context.getSharedPreferences("openchat_agent_settings", Context.MODE_PRIVATE) | ||
|
|
||
| fun load(): AppSettings = AppSettings( |
| if (loopJob?.isActive == true) { | ||
| loopJob?.cancel() | ||
| } else { | ||
| loopJob = lifecycleScope.launch { agentLoop.run() } |
| lifecycleScope.launch { | ||
| agentLoop.log.collect { entry -> | ||
| tvLog.append("$entry\n") | ||
| } | ||
| } |
| // === invariants === | ||
| // - token must be non-blank; all ops throw IllegalStateException if blank | ||
| // - branch names must not contain spaces | ||
| // - commitFiles() is atomic: either all files are committed or none |
| fun apply(snapshot: Snapshot, proposed: String): Result<String> { | ||
| if (hashOf(snapshot.original) != snapshot.hash) { | ||
| return Result.failure(IllegalStateException("HASH_STALE: ${snapshot.path}")) | ||
| } | ||
| return Result.success(proposed) |
| val orig = snapshot.original.lines() | ||
| val next = proposed.lines() | ||
| return buildString { | ||
| appendLine("--- ${snapshot.path}") | ||
| appendLine("+++ ${snapshot.path}") | ||
| val removed = orig.filterNot { it in next } | ||
| val added = next.filterNot { it in orig } | ||
| removed.forEach { appendLine("-$it") } | ||
| added.forEach { appendLine("+$it") } | ||
| }.trimEnd() |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_431280c9-22db-44a3-9875-0119ea9c305a) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_ce6db236-b113-40d4-ac7a-542d3c5a70b7) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_1338f2dc-7434-474c-bbd4-6c5ab4c6623b) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f09a6669-e844-4d7d-bc7f-d4d930ff50ff) |
…ive) with tab-bar UI
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_918474a7-705b-46db-80f6-a8d61b60bbb5) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_6230f900-e953-4cc3-883b-ec3a629de1e2) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f62e168e-ba5b-40a5-9cce-72440e2ce088) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f49baac7-006c-4b8a-befd-05bf9681208c) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_1947e755-b0a1-4b9d-aa4f-91ea75ee00c7) |
… experiment definition)
| role = if (turn.role == "You") "user" else "assistant", | ||
| content = turn.content, | ||
| ) | ||
| } + ModelMessage(role = "user", content = prompt) |
There was a problem hiding this comment.
Ask sends duplicate user turn
Medium Severity
The askModel function constructs the ModelRequest messages by including the askHistory and then explicitly appending the current user prompt. Since the AskStarted action already adds this prompt to askHistory, the model receives the user's latest input twice, which can skew multi-turn context and waste tokens.
Reviewed by Cursor Bugbot for commit 374a978. Configure here.
| Log.d(TAG, "[C0] getBranchHeadSha $branch") | ||
| return runCatching { | ||
| validateBranch(branch) | ||
| val refJson = request("GET", "/git/ref/heads/${branch.encodePath()}") |
There was a problem hiding this comment.
GitHub ref paths encode slashes
High Severity
Agent publish branches are always named like mobile-agent/..., but ref lookups and updates use encodePath(), which turns / into %2F. GitHub expects slash-separated path segments (e.g. .../git/ref/heads/mobile-agent/foo), so head SHA, commit, and ref updates fail for generated branch names.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 72c9f32. Configure here.
| ArtifactKind.KotlinCode -> "package ai.openchat.mobile.agent.generated\n\n" | ||
| } | ||
| val snapshot = editGate.snapshot(path = artifact.path, content = original) | ||
| val diff = editGate.diff(snapshot, artifact.content) |
There was a problem hiding this comment.
EditGate uses fake originals
Medium Severity
Preview compares generated artifacts against hardcoded placeholder file contents, not the target path on GitHub. Approval diffs misrepresent overwrites of existing repo files, so users may approve changes without seeing the real delta.
Reviewed by Cursor Bugbot for commit 72c9f32. Configure here.


Summary
Packages the Android Agent app work into one clean linear branch for review, and now includes the shell stabilization work needed to build the app, publish APK artifacts in CI, and provide usable Ask/Agent mobile MVP flows.
What Changed
mobile-agent-androidchanges and uploads them as artifactsScope
mobile-agent-androidsubtree plus one dedicated workflow fileAGENTS.mddriftValidation
./gradlew :app:assembleDebugpassed locally after shell stabilization work./gradlew :app:assembleReleasepassed locally./gradlew :app:assembleDebugpassed again after Ask mode, history restore, and streaming changes./gradlew :app:assembleDebugpassed again after Agent GitHub draft workflow changesFollow-ups
Note
High Risk
Large new surface area (foreground agent, GitHub/API keys in encrypted prefs, release signing) plus doc routing changes that can mislead agents; credential handling and PR-from-phone flows need careful review.
Overview
Introduces
mobile-agent-androidas a native Kotlin companion (v0.1.0-alpha): single-screen ASK (streaming OpenAI-compatible chat, history/tabs, encrypted provider settings) and PLAN/AGENT/ADAPTIVE modes that run a foregroundAgentServicewith multi-roleAgentLoop, human approval, EditGate preview, and GitHub branch/commit/PR publish via REST tools.Adds
.github/workflows/mobile-agent-android.ymlas the canonical gate: JDK 17, unit tests, debug + release APK builds (release minify/signing when keystore props exist), and artifact upload.Repo hygiene:
.gitignorefor keystores/keystore.properties;AGENTS.md/MEMORY.mdreplaced with token-light routing (full text moved todocs/agents/*);bridge/src/api/routes/experiments-api.spec.mddocuments experiment/agent HTTP contracts.Reviewed by Cursor Bugbot for commit 1272bcb. Bugbot is set up for automated code reviews on this repo. Configure here.