When dispatched with opencode_url set (e.g. Tailscale URL), the workflow
skips starting a local opencode server and the model probe, and points the
emulator directly at the external server.
This allows running the full CUA test against a live dev server without
depending on the flaky CI-hosted opencode serve setup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The probe step computed a SCENARIOS output, but the emulator step runs
--showcase hardcoded and never consumes steps.probe.outputs.SCENARIOS, so
the selection logic was dead. Drop it and keep MODEL_CAPABLE as an
informational signal. Showcase mode handles model-unavailability gracefully
(typescript/verify phases are informational-only).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sessionScopeDirectory() always returned null and ignored its home arg, so
the scope plumbing in loadSessions/createSession was dead: scopeDir was
always null, listClient always the default client, and the lazy path.get()
fetch fed only that dead branch. Collapse both call sites to use the
connection's default client directly and delete the now-orphaned
sessionScope.ts helper and its test.
serverHome is intentionally KEPT in connections.ts: it is still consumed by
the directory switcher UI (DirectorySwitcher.tsx, app/(tabs)/index.tsx) for
~ path expansion, so it is not dead code.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace stale opencode.vibebrowser.app / www.vibebrowser.app domain refs
with the current agentlabs.cc/opencode branding, and update the privacy
policy package id ai.opencode.mobile -> cc.agentlabs.opencode.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Screenshots from CUA smoke test showing:
- 01: Sessions list loading real sessions from server on connect
- 02: Streaming AI response in a coding session
- 03: Sessions tab after navigating back (sessions_reload regression guard)
Demo: 10x speed video (138s → 14s) of full onboarding flow.
Website now shows <video> with mp4 source + gif fallback.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- full_description.txt: replace fluffy copy with concrete HOW IT WORKS steps,
TAILSCALE recommendation, explicit WHAT THIS APP IS NOT section, updated
support email to support@agentlabs.cc
- short_description.txt: 77-char tagline focused on opencode serve remote control
- docs-site/index.html: update meta description, og:description, twitter:description,
ld+json description, hero tagline, and 'What is OpenCode Mobile?' paragraphs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace all @vibebrowser.app email addresses with @agentlabs.cc across
22 files including privacy policy, Play/App Store listings, fastlane
metadata, docs, README, CONTRIBUTING, eas.json, and in-app mailto links.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The CUA script runs on the CI runner (host), not inside the emulator.
10.0.2.2 is the emulator's special address for the host — it is only
reachable FROM INSIDE the emulator. Calling it from the runner always
fails, so _precreate_test_session returned None, and the session_list
phase fell back to the weak 'screen visible' assertion instead of the
strong 'pre-created session must appear' check.
Fix: replace 10.0.2.2 → 127.0.0.1 before making the pre-create call.
Localhost URLs (100.x.x.x, custom dev server) pass through unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sessions_reload regression phase now runs BEFORE the TypeScript task.
Previously it was gated behind typescript which fails in CI when the model
is unavailable — meaning the actual sessions regression check never ran.
Phase order is now:
connect → session_list (pre-created session required) → new_session
→ sessions_reload (navigate back, list must be non-empty) [CRITICAL]
→ typescript (informational) → verify (informational) → settings
Critical phases: connect, session_list, new_session, sessions_reload.
TypeScript/verify/settings are informational (model availability varies).
CI emulator fixes:
- api-level: 30 → 28 (more stable, boots reliably on ubuntu-latest)
- target: google_apis → default (lighter, no Play Services needed for
sessions regression test, avoids known boot issues with google_apis)
- disable-animations: true (reduces boot overhead)
- emulator-boot-timeout: 600 (explicit, matches action default)
- Switch from --scenarios to --showcase (runs the new structured flow
with _precreate_test_session + sessions_reload phase)
- Clear app state before install (pm clear) for deterministic first-run
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously the session_list phase goal said 'The session list may be empty
(no sessions yet) — that is fine' and 'Report done when you can see the
session list screen (even if empty)'. This means an empty sessions list
was treated as a test PASS, so every previous 'fix' was validated against
a test that cannot detect the regression.
Two changes:
1. _precreate_test_session(): calls POST /session via HTTP before the CUA
starts. The session_list phase goal now explicitly names this session and
requires it to be visible — if the app fails to load server sessions,
the phase fails (not passes silently with an empty list).
Falls back gracefully if the server is unreachable at pre-create time.
2. sessions_reload phase (new, critical): after completing the TypeScript
task the test navigates back to the Sessions tab and asserts the list is
non-empty. This catches the other variant of the regression — sessions
vanishing after navigating away from a session and back.
Both phases are now in the critical list, so a failure in either causes the
overall test to report partial/fail instead of success.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ADB input text with %s escaping didn't trigger React Native onChangeText.
New approach: write text to /sdcard/ file on device, then use shell
command-substitution "$(cat ...)" to pass raw text to input text.
This preserves spaces without %s conversion and avoids quote-escaping
issues that prevented React Native from detecting text changes.
Fixes#35. The app was selecting claude-sonnet-4-6 (agent default) instead of gpt-5.4 (provider default), causing send_message failures in CI where only Azure provider is available.
Run 31 collided with a prior manual upload at versionCode 31. Add a
+100 offset so the next workflow run uploads at versionCode 132 and
all subsequent runs continue monotonically past historical conflicts.
Refs #32
Recent sessions disappeared from the list when opencode serve was launched
outside $HOME (e.g. ~/workspace/opencode). The list path scoped to
server.home, but opencode-server resolves x-opencode-directory to a project
id by exact-match, not subtree prefix. $HOME mapped to the synthetic
'global' project, which never contained the user's workspace sessions.
Change the single-source-of-truth rule to return null (no scope header) when
the connection has no explicit directory. The server then uses its own CWD
project — the same project sessions are actually created in. Both list and
create paths still derive from sessionScopeDirectory, so #10's drift cure is
preserved.
Verified against 100.108.64.76:4096: no header returns the recent workspace
sessions (Opencode npm install, autopilot_exit, ...); header=$HOME returns
only the empty global project.
Closes#32
- App in review as of 2026-06-21 (versionCode 32)
- Website live at opencode.agentlabs.cc
- CUA test rewritten with full onboarding showcase
- Next actions: wait for review, keep demo server alive, announce on approval
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01No3k1AEioE4PNUZg12TxQo
Rewrites android-cua-smoke.py to demonstrate the complete first-run journey
instead of the previous "ping" smoke test. The new structured multi-phase
flow covers: server connection setup, session list, new session creation,
TypeScript hello-world task submission (watching tool calls/file writes),
output verification, and Settings/model-selection screenshot.
Key changes:
- run_onboarding_showcase() orchestrates 6 sequential CUA phases with
per-phase goals, step budgets, and PASS/FAIL phase tracking
- run_cua_step() replaces run_cua() — accepts step_label, action_delay,
saves labeled screenshots (/tmp/cua_<phase>_<step>.png) for debugging
- Global --speed-multiplier flag scales all _sleep() calls (0.5 = 2x faster)
- Showcase is now the default mode; legacy --goal / --scenarios flags retained
for backwards compat and CI regression scenarios
- Tighter action_delay (0.7s) and trimmed history window (14 turns) vs
previous 1.0s / 12 turns
- Phase banner log lines ("STEP N: ...") narrate the video in real time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01No3k1AEioE4PNUZg12TxQo
Structured store listing copy for cc.agentlabs.opencode:
- Optimized title, short description, and full description
- Keyword strategy for AI coding agent / developer tools niche
- Screenshot captions for 5 key screens
- Notes for Play Console entry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01No3k1AEioE4PNUZg12TxQo
* fix(ci): add emulator to PATH and increase CUA smoke timeout to 60min
- Add 'Add emulator to PATH' step after setup-android so emulator binary
is found (was: command not found, causing adb wait-for-device to hang
until the 30min job timeout)
- Increase timeout-minutes from 30 to 60 to accommodate full build
- Add npm cache and Gradle cache (same as build.yml) to speed up rebuild
* fix: address code review findings for cua-emulator-path
- Fix stale Gradle cache causing build failure after package rename
(ai.opencode.mobile → cc.agentlabs.opencode): add purge step matching
the one already present in publish-play-store.yml
- Fix adb launch command using old package name ai.opencode.mobile;
updated to cc.agentlabs.opencode/.MainActivity
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JKGMRpgihA4io2frodqLjt
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): bump opencode Azure apiVersion to support /responses endpoint (#22)
The CUA smoke probe was returning MODEL_CAPABLE=false because opencode's
@ai-sdk/azure provider got 'API version not supported' from Azure on
/openai/v1/responses with apiVersion=2024-08-01-preview. Split into two
envs: keep the CUA driver on 2024-08-01-preview (chat-completions only)
and bump the opencode-side provider config to 2025-04-01-preview, which
supports the new responses API.
Effect: send_message/multi_turn scenarios get included again in CUA smoke
when the probe succeeds.
* ci(cua): bound curl timeouts + diag dump on server-start hang (#22)
Step 11 'Start opencode server' has hung past the 45-min job timeout in
two consecutive runs (27195348071, 27198238465). Local boot of
opencode-ai 1.16.2 with the same heredoc config is healthy in 3s, so
something is wrong specifically on the GH-hosted runner — likely curl
post-loop waiting indefinitely on an unresponsive server.
Adds:
- set -x for command tracing
- --connect-timeout 2 -m 5 on every curl so hangs cannot exceed 5s
- HEALTHY flag + explicit exit 1 (drops the unbounded post-loop curl)
- Periodic dump every 10s: server log tail, ss listening sockets,
process liveness — so we can see WHY the server isn't replying
Pure diagnostics; no behaviour change for the green path.
* fix(ci): use api-version=preview for /openai/v1/responses (#22)
Reproduced the probe failure locally against the same Azure resource:
all date-based api-versions (2024-08-01-preview, 2024-12-01-preview,
2025-01-01-preview, 2025-03-01-preview, 2025-04-01-preview) return:
{"error":{"code":"BadRequest","message":"API version not supported"}}
Only api-version=preview and api-version=v1 succeed (200). This is the
new Azure OpenAI v1 responses-API style; date strings are reserved for
the legacy /openai/deployments/{model}/chat/completions endpoint.
@ai-sdk/azure 3.x already defaults apiVersion to "preview" (per the
type definition: "Custom api version to use. Defaults to `preview`."),
so this aligns the workflow with the SDK default. Probe should now
return MODEL_CAPABLE=true and the send_message scenario will run.
* test(cua): extend send_message and multi_turn waits to 30s
Assistant bubbles can take 15+ seconds to appear after send. Previous
5-second wait was too short and caused false failures even when API
calls succeeded. Re-check screenshots periodically up to 30s total.
* fix(cua): screen-relative send button threshold for #22
The send action's auto-locate filtered for y1 > 2200 and fell back to
hardcoded (996, 2358) — both assume a 1080x2400 panel. The CI emulator
(API 30 google_apis pixel profile) is 1080x1920, so:
- the bottom_buttons filter never matched any clickable element
- the fallback tap landed off-screen
→ 'ping' message never sent, scenario timed out with no bubbles.
Switch to a screen-relative threshold (bottom 25%) and a fallback that
uses get_screen_size() to land in the bottom-right corner regardless of
device resolution. This was masked until now because send_message was
gated by MODEL_CAPABLE=false in earlier CI runs.
Refs: #22
---------
Co-authored-by: dzianisv <dzianis.varabyou@gmail.com>
Quick Connect now shows a one-line hint under the password field stating
that username defaults to 'opencode' and pointing to Advanced options for
servers using a custom OPENCODE_SERVER_USERNAME. Tap-target to switch
modes is included.
Closes#23
Co-authored-by: dzianisv <dzianis.varabyou@gmail.com>
MR #39530 remaining blocker was the missing top-level Binaries: field
(maintainer: AllowedAPKSigningKeys rejects the APK without it). Added
Binaries pointing at the GitHub release APK (v%v) and synced the stale
local distribution copy to the canonical fork recipe.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Built from the on-device verification capture (360px, 92KB, looping). Added to README
hero (top repo conversion surface) + docs-site/ and distribution/ for landing + launch
attachment. Demo media is the #1 conversion lever for HN/Reddit/PH — none existed before.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixed claims a technical audience would catch:
- Android build is Gradle (assembleRelease/bundleRelease), NOT EAS — EAS is only in
the unshipped iOS workflow. Changed 'GitHub Actions + EAS' -> 'GitHub Actions (Gradle)'.
- App is Android-only: dropped 'iOS Keychain', kept 'Android Keystore'.
- Diff viewer renders into native views (ScrollView), not a FlatList.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Built the actual release APK from HEAD (auth + UI fixes, BUILD SUCCESSFUL 11m48s) and
installed on the emulator. Same Quick Connect path that gave 401 on the CI APK now
CONNECTS and loads the sessions list. Definitive on-device proof of the fix on the
shipping build. Screenshots 07-08.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ran a native arm64 emulator against the live opencode server, driven via adb (free
model, no LLM). Verified on real device: telemetry consent, empty state, Quick Connect
401 auth bug REPRODUCED, Advanced+username=opencode connects + loads sessions, chat
renders (bubbles/thinking/tokens), and LIVE send -> streaming reply. Screenshots +
writeup under docs/qa/. Closes the pre-posting test-gate pixel-GUI residual for the
connect->session->reply journey.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.4.3 already shipped as versionCode 5; a duplicate code would be rejected by Play and ignored by F-Droid. Bump to 6 unblocks the v0.4.4 release. QA gate passed (units + on-device E2E + visual render check screenshots in docs/qa/render-check/).
Clears the owner's hard visual gate: a real gemini-2.5-flash reply rendered
through the actual app components (MessageBubble→Markdown/CodeBlock, DiffView)
via Expo web export, screenshotted in a real browser.
Per-surface verdict (all PASS, no app code changes needed):
- markdown: heading+bullets, high contrast light & dark
- code block: 430-char single line horizontally scrolls (scrolled-right reveals
the line END), not truncated/wrapped
- diff: fenced ```diff + native DiffView render +/- coloring and scroll
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Quick Connect (the DEFAULT add-connection mode) has no username field, so every
auth-build site (username && password ? {..} : undefined) produced undefined auth
whenever a password was set but username empty -> NO Authorization header -> 401
against a password-protected server. This is the common setup
(OPENCODE_SERVER_PASSWORD=... opencode serve) and a top install->churn cause:
user sets a password, can't connect, gives up.
Fix: extract buildAuth() to a pure, testable module; when a password is present but
username is empty, default username to 'opencode' (the server's own default,
OPENCODE_SERVER_USERNAME ?? 'opencode'). Advanced mode's explicit username is
preserved. Replaced all 6 inline ternaries in connections.ts.
+3 regression tests (68 total pass), typecheck clean. Found while setting up an
on-device emulator test of the connect flow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Launch posts claimed Expo SDK 52 but app is on SDK 54 (factual accuracy
before public posting — HN/Reddit devs check this)
- docs/qa/REPLY-FLOW-E2E-2026-06-08.md: verified send->streaming reply works
against the live opencode server via app-identical sdk.ts calls (free model);
closes the 'opencode can't reply in CI' residual at the data-contract level
- owner-submissions.md: 0.4.3 -> 0.4.4
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New high-intent SEO pages:
- /remote-access/ — step-by-step Tailscale, Cloudflare Tunnel, ngrok setups
for reaching a self-hosted opencode server from Android anywhere. HowTo +
FAQ + BreadcrumbList structured data.
- /ios/ — honest 'no iOS app yet' page: current Android app, why, options,
roadmap, how to follow. FAQ + BreadcrumbList structured data.
Added to sitemap.xml; internal links from index, guide, opencode-on-phone,
and troubleshooting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run 27139243275 FAILED with "/usr/bin/sh: Syntax error: end of file unexpected
(expecting fi)" — android-emulator-runner runs the script under dash, which
mangled the multi-line if/then/else/fi I added, so the scenarios never ran (a
real bug I introduced, not environmental). Fix:
- Move scenario-set selection into the probe step (runs under bash) and export
SCENARIOS as a step output; the emulator script is now single-line.
- The earlier probe returned a FALSE NEGATIVE: it POSTed to /session/{id}/message
with no model, but that endpoint REQUIRES model {providerID,modelID} (per the
opencode SDK the app uses). Probe now sends azure/gpt-5.4 exactly like the app,
uses -s + %{http_code} (not -sf) so the body/status are visible, and only flags
capable on HTTP 200 + an assistant text part.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CI opencode server had NO LLM provider configured — the server log only
showed "listening", never a model. opencode-ai (released npm pkg) does not read
AZURE_OPENAI_* for its own LLM; it needs an explicit provider in opencode.json +
a default `model`. So send_message/multi_turn could never pass and the gate was
stuck on --only-connect-scenario (UI journey minus the model reply).
- Wire opencode to the same Azure resource the CUA driver uses via a generated
~/.config/opencode/opencode.json (@ai-sdk/azure provider, resourceName derived
from the endpoint secret at runtime, apiKey from env, default model azure/gpt-5.4).
- Add a deterministic REST probe step: create a session + send a prompt and check
for an assistant reply BEFORE the ~30min emulator run, exporting MODEL_CAPABLE.
- Add --scenarios to android-cua-smoke.py to run an explicit named set.
- Emulator step now runs connect_and_verify_sessions + send_message +
verify_session_list when MODEL_CAPABLE=true; falls back to the UI-only journey
(connect + verify_session_list) otherwise, logging the environmental reason.
- Raise --max-steps to 40 so multiple scenarios fit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>