Skip to content

feat(android): linearize Android agent app branch - #30

Open
openchat-ai wants to merge 85 commits into
mainfrom
mobile/android-agent-app-linear
Open

feat(android): linearize Android agent app branch#30
openchat-ai wants to merge 85 commits into
mainfrom
mobile/android-agent-app-linear

Conversation

@openchat-ai

@openchat-ai openchat-ai commented Jul 4, 2026

Copy link
Copy Markdown
Owner

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

  • repaired the Android Gradle project root and added the wrapper scripts/jar
  • replaced the duplicate launcher skeleton with a single Kotlin MainActivity and config-backed settings flow
  • added Ask / Agent mode switching in a single-screen mobile shell
  • upgraded Ask mode from a single request box into a more usable flow with:
    • OpenAI-compatible model calls
    • multi-turn conversation context
    • local Ask history restore after restart
    • streaming response rendering for providers that support SSE chat completions
  • upgraded Agent mode from a pure demo loop into a minimal executable GitHub draft workflow:
    • enter a goal in the app
    • generate a markdown draft
    • preview it through the EditGate
    • create a branch, commit the draft, and open a GitHub PR after approval
  • wired an OpenAI-compatible provider client and a minimal GitHub REST client
  • added a GitHub Actions workflow that builds debug and release APKs for mobile-agent-android changes and uploads them as artifacts
  • updated project docs to reflect the new checkpoint

Scope

  • includes only the mobile-agent-android subtree plus one dedicated workflow file
  • excludes unrelated history noise and merge commits
  • excludes unrelated AGENTS.md drift

Validation

  • ./gradlew :app:assembleDebug passed locally after shell stabilization work
  • ./gradlew :app:assembleRelease passed locally
  • ./gradlew :app:assembleDebug passed again after Ask mode, history restore, and streaming changes
  • ./gradlew :app:assembleDebug passed again after Agent GitHub draft workflow changes
  • Mobile Agent Android GitHub Actions workflow passed for PR feat(android): linearize Android agent app branch #30

Follow-ups

  • runtime provider token / GitHub token integration still needs real credential testing
  • device install/run verification is still pending outside this container
  • Agent mode can now complete one real GitHub draft workflow, but is not yet a general-purpose Cursor-class engineering agent

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-android as 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 foreground AgentService with multi-role AgentLoop, human approval, EditGate preview, and GitHub branch/commit/PR publish via REST tools.

Adds .github/workflows/mobile-agent-android.yml as the canonical gate: JDK 17, unit tests, debug + release APK builds (release minify/signing when keystore props exist), and artifact upload.

Repo hygiene: .gitignore for keystores/keystore.properties; AGENTS.md / MEMORY.md replaced with token-light routing (full text moved to docs/agents/*); bridge/src/api/routes/experiments-api.spec.md documents experiment/agent HTTP contracts.

Reviewed by Cursor Bugbot for commit 1272bcb. Bugbot is set up for automated code reviews on this repo. Configure here.

@openchat-ai
openchat-ai marked this pull request as ready for review July 4, 2026 10:33
Copilot AI review requested due to automatic review settings July 4, 2026 10:33
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread mobile-agent-android/app-android/app/src/main/AndroidManifest.xml Outdated
Comment on lines +29 to +33
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() }
Comment on lines +75 to +79
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
Comment on lines +33 to +37
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)
Comment on lines +21 to +30
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()
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 11, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 11, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor

cursor Bot commented Jul 11, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

role = if (turn.role == "You") "user" else "assistant",
content = turn.content,
)
} + ModelMessage(role = "user", content = prompt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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()}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 72c9f32. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants