Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7823553
fix(chat): copy unsupported attachments into the workspace
yuga-hashimoto Oct 17, 2026
79545d0
fix(privacy): keep runtime error text on device and gate crash report…
yuga-hashimoto Oct 17, 2026
a162a40
fix(opencode): decode agents whose hidden flag is null
doublepi123 Oct 17, 2026
145b406
test(opencode): cover a null hidden flag on V2 agents
doublepi123 Oct 17, 2026
21b8a75
fix(chat): retry agents that fail to load instead of a dead mode chip
doublepi123 Oct 17, 2026
f2f262e
fix(chat): retry agents outside the catalogue lock, keep a shown list
doublepi123 Oct 17, 2026
4cc8a3e
fix(chat): clear the agents error once the retry succeeds
doublepi123 Oct 17, 2026
e53d84d
fix(chat): keep the agents error one line, leave early retries to load
doublepi123 Oct 17, 2026
1454e1e
fix(chat): show the agent chip loading instead of ignoring taps
doublepi123 Oct 17, 2026
56654e4
feat(codex): choose reasoning effort and fast mode per turn
doublepi123 Oct 17, 2026
e57c41d
fix(chat): apply review on the agent loading state
doublepi123 Oct 17, 2026
1f5b892
style(codex): sort imports
doublepi123 Oct 17, 2026
88b37c8
fix(assistant): use app_name for the voice assistant title
doublepi123 Oct 17, 2026
f66bb97
fix(chat): drop the agents error line with a kept list, keep a racing…
doublepi123 Oct 17, 2026
5894762
docs(codex): drop the first-version claim, describe the fast tier tex…
doublepi123 Oct 17, 2026
e43c5ea
Merge pull request #391 from yuga-hashimoto/fix-issues-386-387
yuga-hashimoto Oct 18, 2026
2dbba4c
Merge pull request #388 from doublepi123/fix/upstream-agent-hidden-null
yuga-hashimoto Oct 18, 2026
6268d15
Merge pull request #390 from doublepi123/fix/upstream-agent-retry
yuga-hashimoto Oct 18, 2026
9a68782
fix(build): pin proot 5.1.107.97, the version Termux still serves
claude Oct 18, 2026
a4a9038
docs(legal): say the fdroid pin can lag the mirror by a few versions
claude Oct 18, 2026
47c8383
Merge pull request #393 from yuga-hashimoto/ccr-d1e4054d-xnh8mt
yuga-hashimoto Oct 18, 2026
04f1d3c
Merge main and resolve Codex turn settings conflicts
doublepi123 Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,29 +116,37 @@ settings to remove these mounts.
AndCode uses **Firebase Crashlytics** (a Google service) for crash and non-fatal error reporting in
release builds (`CrashReporter` in `core/diagnostics`). It:

- Is **disabled by default** and shares the "Anonymous usage analytics & crash reports" opt-in in
Settings. Nothing is collected until the user turns that setting on (in debug builds collection is
always off regardless of the setting).
- Sends the app version, build type, and OS version as custom keys that `CrashReporter.install()`
sets explicitly.
- Sends crash stack traces and short diagnostic log lines. Custom log lines and custom-key values
that AndCode's own code passes through `CrashReporter.log()`/`recordException()` are run through
redaction (`SecretRedaction`, `CrashReportSanitizer`) that strips *patterns resembling* API keys,
tokens, `Authorization`/`Cookie` headers, and passwords before they are attached to a report, and
truncates length. **This is a best-effort pattern match, not a guarantee that every possible
credential shape is caught** — for example, some provider key formats not covered by the current
patterns, or a key with no recognizable label, could slip through if it ever ended up in a log
line. Fatal crashes and their exception messages/stack traces that Crashlytics' own SDK collects
automatically (rather than through `CrashReporter`) are **not** run through this redaction, since
they are captured by Crashlytics itself before AndCode's code runs.
tokens, `Authorization`/`Cookie` headers, quoted credential fields, URL credentials, and
passwords before they are attached to a report, and truncates length. **This is a best-effort
pattern match, not a guarantee that every possible credential shape is caught** — for example,
some provider key formats not covered by the current patterns, or a key with no recognizable
label, could slip through if it ever ended up in a log line. Fatal crashes and their exception
messages/stack traces that Crashlytics' own SDK collects automatically (rather than through
`CrashReporter`) are **not** run through this redaction, since they are captured by Crashlytics
itself before AndCode's code runs.
- Does **not** receive a runtime/provider's raw error text. A session failure is reported as a fixed
category made only of the runtime id and an identifier-shaped error type (for example
`MessageAbortedError`); the free-form message a provider returns — which can quote prompts, model
output, file paths and hosts — is shown on-device only.
- The Crashlytics SDK also automatically collects standard device/app/installation diagnostics
independent of anything AndCode's code sends explicitly — for example, device model, OS version,
app version, a Firebase installation identifier, and free/total storage and RAM at crash time. See
Firebase's own documentation for the current complete list, since AndCode's code does not control
this baseline collection.
- Is disabled in debug builds (`setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)`).

Firebase Analytics is available for anonymous app-open/session and runtime outcome metrics, but is
**disabled by default** and is only enabled after the user opts in. It does not receive prompts,
source files, model responses, tokens, or workspace paths. The opt-in choice is stored locally and
can be withdrawn in Settings.
Firebase Analytics is available for anonymous app-open/session and runtime outcome metrics, and is
governed by the same **disabled-by-default** opt-in as crash reporting. It does not receive prompts,
source files, model responses, tokens, or workspace paths, and the advertising ID is not collected
(`google_analytics_adid_collection_enabled=false`). The opt-in choice is stored locally and can be
withdrawn in Settings.

Crashlytics data is governed by Google's and Firebase's own privacy terms; see
[THIRD_PARTY_SERVICES.md](THIRD_PARTY_SERVICES.md).
Expand Down
4 changes: 2 additions & 2 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ BSD-3-Clause text used by `libandroid-shmem` is bundled verbatim, unmodified, in

| Package | Version | License (verified against upstream source, not guessed) | Copyright | Corresponding source (content-addressed) |
|---|---|---|---|---|
| `proot` | 5.1.107.96 | [GPL-2.0](THIRD_PARTY_LICENSES/GPL-2.0.txt), per [`TERMUX_PKG_LICENSE`](runtime_tools/termux-packaging-recipes/proot.build.sh) | The PRoot contributors | Upstream source archive `v5.1.107.96.zip`, SHA-256 `75f654fe60dea92dabff2bf083ae8bfe4f91baa6a1a374786a6bf391015eebaa` (from [github.com/termux/proot](https://github.com/termux/proot/archive/v5.1.107.96.zip), tagged release, not `master`) |
| `proot` | 5.1.107.97 | [GPL-2.0](THIRD_PARTY_LICENSES/GPL-2.0.txt), per [`TERMUX_PKG_LICENSE`](runtime_tools/termux-packaging-recipes/proot.build.sh) | The PRoot contributors | Upstream source archive `v5.1.107.97.zip`, SHA-256 `badd014e6868c0d57b5eae6db08afc284b77f85990c71665d1659e121482df26` (from [github.com/termux/proot](https://github.com/termux/proot/archive/v5.1.107.97.zip), tagged release, not `master`) |
| `libandroid-shmem` | 0.7 | [BSD-3-Clause](THIRD_PARTY_LICENSES/BSD-3-Clause-libandroid-shmem.txt), per [`TERMUX_PKG_LICENSE`](runtime_tools/termux-packaging-recipes/libandroid-shmem.build.sh) and the project's own `LICENSE` file | Copyright (c) 2013 Sergii Pylypenko; Copyright (c) 2017 Fredrik Fornwall | Upstream source archive `v0.7.tar.gz`, SHA-256 `1e5ff8459bc0a8c229dd8a94b27d119987e09ef3414331c2b5ebfff20b98e867` (from [github.com/termux/libandroid-shmem](https://github.com/termux/libandroid-shmem/archive/refs/tags/v0.7.tar.gz), tagged release, not `master`) |
| `libtalloc` | 2.5.0 | [LGPL-3.0-or-later](THIRD_PARTY_LICENSES/LGPL-3.0.txt) for the actual runtime library. Termux's own packaging metadata tags the *package* `GPL-3.0` (a coarser, package-level tag), but the shared library source itself (`talloc.c`/`talloc.h`, the only files that become `libtalloc.a`/`libtalloc.so`) carries its own header: *"the following LGPL license applies to the talloc library. This does NOT imply that all of Samba is released under the LGPL"* — version 3 or later. Resolved; no longer `REQUIRES_LICENSE_REVIEW`. | Copyright (C) Andrew Tridgell 2004; Copyright (C) Stefan Metzmacher 2006 | Upstream source archive `talloc-2.5.0.tar.gz`, SHA-256 `912afa237510ae542a7733998eb18a12bcda35ab6729c8e2ddb43e8d0ebab007` (from [samba.org/ftp/talloc](https://www.samba.org/ftp/talloc/talloc-2.5.0.tar.gz), versioned release path, not a mutable branch) |

The fdroid flavor compiles all three from the pinned `TermuxPackages` commit, which can lag the
mirror by a version or two (currently `proot` 5.1.107.94, `libtalloc` 2.4.3, `libandroid-shmem` 0.7).
mirror by a few versions (currently `proot` 5.1.107.94, `libtalloc` 2.4.3, `libandroid-shmem` 0.7).

**Packaging recipes, mirrored (not just linked):** the exact `TERMUX_PKG_*` build recipe used for
each package above — the thing that actually produces the `.deb` pinned by hash in
Expand Down
2 changes: 1 addition & 1 deletion THIRD_PARTY_SERVICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Claude Code, Antigravity, and OpenCode specifically.
| **Alpine Linux** (`dl-cdn.alpinelinux.org`) | Minimal Linux root filesystem the on-device runtime runs in | N/A (unauthenticated download) | The app downloads the official Alpine minirootfs archive over HTTPS | N/A | No | [Alpine Linux license terms](https://alpinelinux.org/) (per-package) | Delete the local runtime | Independent open-source project |
| **Debian** (`deb.debian.org`, `security.debian.org`) | Bookworm root filesystem used only for the glibc-dependent Antigravity CLI | N/A (unauthenticated `apt` mirrors) | `apt` inside the Debian rootfs fetches packages over the configured mirrors | N/A | No | [Debian's licenses](https://www.debian.org/legal/) (per-package) | Delete the local runtime | Independent open-source project |
| **Termux package mirror** (`packages.termux.dev`) | Source of the `proot`, `libandroid-shmem`, and `libtalloc` binaries bundled into the APK at build time | N/A (build-time download, pinned by hash) | Build machine only, not the end-user's device at runtime | N/A | No | Upstream licenses vary — see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) | N/A (bundled at build time) | Independent open-source project |
| **Firebase Crashlytics** (Google) | Crash and non-fatal error reporting in release builds | N/A (app-level Firebase project, no end-user login) | The app sends crash reports and small diagnostic breadcrumbs directly to Firebase; custom log lines/keys AndCode's own code adds are redacted on a best-effort basis (see PRIVACY.md §7), but automatic fatal-crash collection by the Crashlytics SDK itself is not routed through that redaction | N/A (no user credential; app-level Firebase config) | This *is* a Google-operated collection service, not an AndCode server | [Firebase Terms of Service](https://firebase.google.com/terms) / [Google Privacy Policy](https://policies.google.com/privacy) | Disabled automatically in debug builds; release collection follows the app's diagnostics policy | Independent Google service; not affiliated with the AI-agent integrations above |
| **Firebase Crashlytics** (Google) | Crash and non-fatal error reporting in release builds | N/A (app-level Firebase project, no end-user login) | The app sends crash reports and small diagnostic breadcrumbs directly to Firebase; custom log lines/keys AndCode's own code adds are redacted on a best-effort basis (see PRIVACY.md §7), but automatic fatal-crash collection by the Crashlytics SDK itself is not routed through that redaction. Runtime session errors are reported as a fixed category only (runtime id + error type), never the provider's raw message | N/A (no user credential; app-level Firebase config) | This *is* a Google-operated collection service, not an AndCode server | [Firebase Terms of Service](https://firebase.google.com/terms) / [Google Privacy Policy](https://policies.google.com/privacy) | Disabled by default (and always in debug builds); enable or withdraw consent in Settings | Independent Google service; not affiliated with the AI-agent integrations above |
| **Firebase Analytics** (Google) | Opt-in anonymous app-open/session and runtime outcome metrics | N/A (app-level Firebase project, no end-user login) | The Firebase SDK sends telemetry directly to Firebase; AndCode excludes prompts, code, responses, tokens, and workspace paths | N/A (no user credential; app-level Firebase config) | This *is* a Google-operated collection service, not an AndCode server | [Firebase Terms of Service](https://firebase.google.com/terms) / [Google Privacy Policy](https://policies.google.com/privacy) | Disabled by default; enable or withdraw consent in Settings | Independent Google service; not affiliated with the AI-agent integrations above |

## OpenCode credential paths
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ package com.yugahashimoto.andcode.core.diagnostics
object CrashReporter {
fun install() = Unit

fun setEnabled(enabled: Boolean) = Unit

fun log(message: String) = Unit

fun recordException(
error: Throwable,
message: String? = null,
customKeys: Map<String, String> = emptyMap(),
) = Unit

fun recordRuntimeSessionError(
runtimeId: String,
errorType: String?,
) = Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ object CrashReporter {
@Volatile
private var crashlytics: FirebaseCrashlytics? = null

/** Initializes Crashlytics and records only non-sensitive app metadata. */
/**
* Initializes Crashlytics but leaves collection off until [setEnabled] opts it in.
*
* Crash reporting follows the same explicit user opt-in as Analytics: nothing leaves the device
* until the user allows it. The manifest default is off too, so no report can escape before this
* runs.
*/
fun install() {
val client =
runCatching { FirebaseCrashlytics.getInstance() }
.getOrNull()
?: return
crashlytics = client
runCatching {
client.setCrashlyticsCollectionEnabled(!com.yugahashimoto.andcode.BuildConfig.DEBUG)
client.setCrashlyticsCollectionEnabled(false)
client.setCustomKey("app_version", com.yugahashimoto.andcode.BuildConfig.VERSION_NAME)
client.setCustomKey(
"build_type",
Expand All @@ -32,6 +38,13 @@ object CrashReporter {
}
}

/** Turns collection on or off with the user's analytics/crash-reporting opt-in. */
fun setEnabled(enabled: Boolean) {
runCatching {
crashlytics?.setCrashlyticsCollectionEnabled(enabled && !com.yugahashimoto.andcode.BuildConfig.DEBUG)
}
}

fun log(message: String) {
crashlytics?.let { client ->
runCatching { client.log(CrashReportSanitizer.message(message)) }
Expand All @@ -55,4 +68,22 @@ object CrashReporter {
client.recordException(error)
}
}

/**
* Reports a runtime session failure as a fixed category only.
*
* The provider's raw error text is intentionally not sent: it is free-form and can carry prompt
* content, model output, file paths and hosts. The on-device notification still shows it.
*/
fun recordRuntimeSessionError(
runtimeId: String,
errorType: String?,
) {
val client = crashlytics ?: return
val category = CrashReportSanitizer.errorCategory(runtimeId, errorType)
runCatching {
client.log(category)
client.recordException(IllegalStateException(category))
}
}
}
19 changes: 19 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<!--
Firebase Analytics pulls in the advertising-ID permission; the app never uses the ad ID, so
it is removed here and its collection is disabled in the manifest metadata below.
-->
<uses-permission
android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove" />
<uses-feature android:name="android.hardware.camera" android:required="false" />

<queries>
Expand Down Expand Up @@ -62,9 +69,21 @@
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="34">

<!--
Analytics and crash reporting are both opt-in: the manifest turns their collection off
before any app code runs, and AnalyticsReporter/CrashReporter enable it only when the
user turns the setting on. `google_analytics_adid_collection_enabled` keeps Firebase
Analytics (when opted in) from collecting the advertising ID.
-->
<meta-data
android:name="firebase_analytics_collection_enabled"
android:value="false" />
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
<meta-data
android:name="google_analytics_adid_collection_enabled"
android:value="false" />

<activity
android:name=".MainActivity"
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/assets/legal/oss_licenses.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ BSD-3-Clause text used by `libandroid-shmem` is bundled verbatim, unmodified, in

| Package | Version | License (verified against upstream source, not guessed) | Copyright | Corresponding source (content-addressed) |
|---|---|---|---|---|
| `proot` | 5.1.107.96 | [GPL-2.0](THIRD_PARTY_LICENSES/GPL-2.0.txt), per [`TERMUX_PKG_LICENSE`](runtime_tools/termux-packaging-recipes/proot.build.sh) | The PRoot contributors | Upstream source archive `v5.1.107.96.zip`, SHA-256 `75f654fe60dea92dabff2bf083ae8bfe4f91baa6a1a374786a6bf391015eebaa` (from [github.com/termux/proot](https://github.com/termux/proot/archive/v5.1.107.96.zip), tagged release, not `master`) |
| `proot` | 5.1.107.97 | [GPL-2.0](THIRD_PARTY_LICENSES/GPL-2.0.txt), per [`TERMUX_PKG_LICENSE`](runtime_tools/termux-packaging-recipes/proot.build.sh) | The PRoot contributors | Upstream source archive `v5.1.107.97.zip`, SHA-256 `badd014e6868c0d57b5eae6db08afc284b77f85990c71665d1659e121482df26` (from [github.com/termux/proot](https://github.com/termux/proot/archive/v5.1.107.97.zip), tagged release, not `master`) |
| `libandroid-shmem` | 0.7 | [BSD-3-Clause](THIRD_PARTY_LICENSES/BSD-3-Clause-libandroid-shmem.txt), per [`TERMUX_PKG_LICENSE`](runtime_tools/termux-packaging-recipes/libandroid-shmem.build.sh) and the project's own `LICENSE` file | Copyright (c) 2013 Sergii Pylypenko; Copyright (c) 2017 Fredrik Fornwall | Upstream source archive `v0.7.tar.gz`, SHA-256 `1e5ff8459bc0a8c229dd8a94b27d119987e09ef3414331c2b5ebfff20b98e867` (from [github.com/termux/libandroid-shmem](https://github.com/termux/libandroid-shmem/archive/refs/tags/v0.7.tar.gz), tagged release, not `master`) |
| `libtalloc` | 2.5.0 | [LGPL-3.0-or-later](THIRD_PARTY_LICENSES/LGPL-3.0.txt) for the actual runtime library. Termux's own packaging metadata tags the *package* `GPL-3.0` (a coarser, package-level tag), but the shared library source itself (`talloc.c`/`talloc.h`, the only files that become `libtalloc.a`/`libtalloc.so`) carries its own header: *"the following LGPL license applies to the talloc library. This does NOT imply that all of Samba is released under the LGPL"* — version 3 or later. Resolved; no longer `REQUIRES_LICENSE_REVIEW`. | Copyright (C) Andrew Tridgell 2004; Copyright (C) Stefan Metzmacher 2006 | Upstream source archive `talloc-2.5.0.tar.gz`, SHA-256 `912afa237510ae542a7733998eb18a12bcda35ab6729c8e2ddb43e8d0ebab007` (from [samba.org/ftp/talloc](https://www.samba.org/ftp/talloc/talloc-2.5.0.tar.gz), versioned release path, not a mutable branch) |

The fdroid flavor compiles all three from the pinned `TermuxPackages` commit, which can lag the
mirror by a version or two (currently `proot` 5.1.107.94, `libtalloc` 2.4.3, `libandroid-shmem` 0.7).
mirror by a few versions (currently `proot` 5.1.107.94, `libtalloc` 2.4.3, `libandroid-shmem` 0.7).

**Packaging recipes, mirrored (not just linked):** the exact `TERMUX_PKG_*` build recipe used for
each package above — the thing that actually produces the `.deb` pinned by hash in
Expand Down
Loading
Loading