Commit Graph

1345 Commits

Author SHA1 Message Date
Peter Steinberger fd7e9bbace fix(android): serialize legacy gateway discovery (#131001)
* fix(android): serialize legacy gateway discovery resolution

* chore(android): avoid parallel changelog insertion conflict
2026-08-27 10:26:19 -07:00
Peter Steinberger 1c761b712e fix(android): preserve settings keyboard insets (#130995) 2026-08-27 09:20:15 -07:00
Peter Steinberger c767298465 fix(android): keep question answers when reading earlier messages (#130821)
* fix(android): retain question drafts with their pending prompt

* fix(android): scope screenshot questions to fixture runtimes
2026-08-27 04:25:54 -07:00
Peter Steinberger 2ea5bf79c0 fix(android): keep Talk's speaking indicator active during reply replacement (#130868)
* fix(android): retain Talk playback ownership through cleanup

Keep local request leases and realtime playback facts in one synchronous state owner. Preserve the underlying Talk status across temporary playback phases, retire stale cleanup, and keep reported failures visible until a new action.

* fix(android): scope Talk playback ownership to speaking state

Keep the canonical local playback lease and realtime speaking fact so retiring playback cannot clear an active replacement. Restore the existing independent status flow and retain all five speaking-state regressions; status and turn correlation remain separate follow-up work.
2026-08-27 03:30:28 -07:00
Peter Steinberger 0dbd5c81d5 feat(plugins): one consent screen for plugin capabilities, bound to the reviewed artifact (#130168)
* feat(plugins): surface plugin capability consent in Control UI and CLI

Adds plugins.inspect (declared manifest surface, operator grants, install
provenance/integrity, ClawHub trust), a Control UI consent dialog on install
and external-plugin enable, a server-side acceptance gate persisted on the
install record, artifact-anchored widen diffing, and --accept-capabilities
for non-interactive CLI use.

NOT READY TO LAND: autoreview found critical gaps (see PR notes) — the
declared surface omits 20 of 21 contract families, native plugins always
report zero hooks, several install/enable paths bypass the gate, and the
acknowledgment is not bound to the reviewed surface.

* refactor(plugins): bind capability consent to the reviewed surface

Collapses the consent error payload to the fields the client cannot fetch
(reviewToken, widened, acceptedAt) and pulls identity/declared/grants/source/
trust from plugins.inspect, shrinking the registry-free protocol reader from
395 to 91 lines and removing its divergence from the closed schema.

Acknowledgment now carries the SHA-256 reviewToken of the surface the operator
saw; the server recomputes the final staged artifact's surface and rejects any
mismatch before persisting acceptance. That closes review-then-swap, laundering
of forged acceptance through an unchanged update, and cross-artifact replay.

All 22 manifest contract families are now declared, hashed and diffed, so a
privileged family such as gatewayMethodDispatch can no longer be added without
re-consent. Consent reads the manifest runtime discovery will execute, ambiguous
install ownership fails closed, integrity resolution has one owner and no longer
labels npm SHA-1 shasums as SHA-256, and code plugins disclose that hooks
register at runtime instead of rendering an empty "no hooks" row.

* fix(gateway): register plugins.inspect in method inventories and regenerate protocol

Adds plugins.inspect to the advertised-method inventories (widening the
fixed-size slice windows so older indices stay stable), regenerates the Kotlin
protocol bindings, drops an unused exported type, and replaces two nested
conditional spreads with a plain conditional.

* refactor(plugins): split oversized consent modules and clear lint findings

Extracts the MCP controller out of the plugins page, unchanged-install
reconciliation out of update-installed, and the install lifecycle suite out of
the management-service tests, bringing all three back under the max-lines limit
without suppressions. Also renames a shadowed binding, drops an unnecessary
generic, removes a spread-to-modify in a map, and types catch callbacks as
unknown.

* chore(protocol): regenerate Kotlin bindings after rebase

* feat(plugins): let chat /plugins install review and accept capabilities

The consent gate applies to chat installs too, but the command had no way to
give consent, so external installs dead-ended on a CLI-only flag. Chat now
replies with the plugin's declared capability surface and the exact command to
rerun, and accepts a trailing --accept-capabilities mirroring the existing
--force acknowledgement. ClawHub trust acknowledgement stays CLI-only.

Staged-artifact verification is unchanged: the reviewToken is still checked
against the final artifact before acceptance is recorded.

* refactor(plugins): single-source the declared-surface groups and manifest precedence

The ordered capability group list was defined independently in the consent
engine, the protocol error reader, the CLI formatter and the Control UI, so a
new contract family had to be added in four places with nothing enforcing it.
All four now derive from one canonical list in the protocol schema with a
compile-time exhaustiveness guard.

Native-versus-bundle manifest precedence is centralized in one helper that both
discovery and staged consent call, so the two cannot drift again — that
divergence was a real bug where consent read one manifest and the runtime
executed another.

Also documents that carrying acceptance forward requires pinned artifact
integrity, so integrity-less sources such as local paths ask on every install.

* fix(plugins): enforce reviewed consent across activation flows

Route setup, repair, linked installs, updates, and chat activation through artifact-bound capability consent. Reuse canonical package discovery and recheck staged activation before config publication. Invalidate stale Control UI review requests on reconnect.

Verified focused owner and sibling tests, runtime rebuild, and real isolated CLI/Gateway install, inspect, enable, widening, and stale-token rejection flows.

* test(plugins): cover beta installs through capability consent

* test(plugins): align consent fixtures with staged artifacts

* fix(ui): review staged plugin capabilities once

* test(ui): inline the remaining plugin consent confirmation

* test(plugins): verify consent with deferred install transactions

* refactor(setup): share inference execution plan construction

* test(ui): settle applied config before deferring refresh

* fix(plugins): protect consent provenance and reuse acceptance
2026-08-27 02:58:07 -07:00
Peter Steinberger 7dbd87d160 test(android): drain runtime fixtures before sandbox teardown (#130861) 2026-08-27 02:49:47 -07:00
Peter Steinberger 1847e9ac99 fix(android): retain canvas pins for equivalent numeric hosts (#130835) 2026-08-27 01:57:18 -07:00
openclaw-mantis[bot] 5e193d50ad chore(i18n): refresh native locales (#130667)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-27 04:21:50 +00:00
Peter Steinberger b581ff0be7 feat(apps): resolve the per-profile accent live on iOS, macOS, and Android (#130598)
* feat(apps): resolve the per-profile accent live on iOS, macOS, and Android

Named follow-up from #130340: native apps now fetch the caller's own
profile accent (users.prefs.get, strict #rrggbb normalization) and prefer
it over the gateway accent, refetching on users.prefs.changed — the
gateway targets that event at the caller's own profile, so clients need no
identity logic. macOS stores it separately from the seam color so
settings-pane config refreshes cannot clobber it, and fetches bypass
ControlChannel.request to avoid degrading the channel on older gateways.
Profile-less and token connections are unchanged. Removes the ios/android
users.prefs.changed allowlist entries now that handlers exist.

* chore(macos): satisfy swiftformat explicit-self on profileAccentHex
2026-08-26 20:01:40 -07:00
Peter Steinberger 59fc8fc03f fix(android): read long replies aloud without silent failure (#130574)
* fix(android): read long replies aloud without silent failure

* fix(android): localize the complete speech retry label
2026-08-26 19:54:49 -07:00
Peter Steinberger 8e1c34e21d fix(android): keep the composer visible with slash suggestions (#130555) 2026-08-26 18:37:07 -07:00
Peter Steinberger 6f4a39c80e fix(android): preserve gateway paths for inline widgets (#130535) 2026-08-26 18:12:20 -07:00
Peter Steinberger a369c7ca02 fix(android): report disabled notification categories instead of success (#130531) 2026-08-26 18:06:31 -07:00
Peter Steinberger b54e677d79 refactor(android): remove unused sidebar and mascot paths (#130505) 2026-08-26 17:32:44 -07:00
Peter Steinberger a4c01c1a77 feat(ui): save appearance preferences per user profile (#130340)
* feat(ui): save appearance preferences per user profile

When a Control UI connection is bound to an authenticated user profile
(trusted-proxy, Tailscale, GitHub identity), theme, theme mode, and accent
become per-profile: stored in the existing user_preferences KV store via
users.prefs.set, overriding gateway-wide ui.prefs, with live cross-device
updates over a new additive users.prefs.changed event scoped to the same
merged profile. Restore default deletes only the profile key and falls back
to the gateway-wide value. talk.config projects the caller's profile accent
for native clients. Profile-less connections (token/password/none auth)
keep the existing gateway-config behavior byte-identically.

Release-note context: on multi-person gateways appearance choices are now
personal and follow you across devices; an admin restyling their own UI no
longer reskins the whole team.

* chore(protocol): allowlist users.prefs.changed for mobile clients

iOS and Android resolve the profile accent through talk.config on connect
and config refresh; live per-profile appearance push on natives is a named
follow-up.

* chore(protocol): regenerate Swift/Kotlin models for users.prefs.changed

* fix(ui): honor explicit defaults and identity switches for profile appearance

Addresses the three ClawSweeper review findings:
- resetValue for profile-bound appearance keys is the deletion fallback
  (gateway value), so an explicit product-default selection persists as a
  profile write instead of being misclassified as a reset.
- An identity switch between two known scopes forces a full appearance
  reconcile and clears appearance keys the returning identity never set,
  so a shared browser cannot keep the previous profile's look (boot keeps
  the last-seen shortcut).
- talk.config joins the profile-dependent dispatch gate so a pending
  GitHub identity sync completes before the profile accent is projected;
  token clients pass through untouched.

* chore(ui): satisfy assertion-safety ratchet and docs formatting

* refactor(ui): extract server-prefs storage primitives

server-prefs.ts crossed the 700-line cap after the review fixes; the
stateless localStorage persistence primitives move to
server-prefs-storage.ts (no behavior change). Shrink-prunes the
assertion-safety baseline for the moved casts.

* fix(ui): keep imported custom themes out of profile storage

Custom palettes are browser-local by contract, so a profile must never
carry theme=custom to a browser that cannot render it. The wire contract
drops custom from the storable theme values (readers self-heal any stored
value), a profile-bound custom selection stays retained browser-local
(including the offline-queued path), and the exhaustive theme test now
encodes the exception.
2026-08-26 16:48:26 -07:00
Dallin Romney 972b0c4b84 chore(i18n): refresh native locales (#130270) 2026-08-26 12:25:53 -07:00
Peter Steinberger d001975d31 fix(android): require listener access before forwarding notifications (#130173)
* fix(android): require listener access before forwarding notifications

* test(android): use activity-local owner in notification settings test
2026-08-26 09:38:10 -07:00
Josh Avant e6ed7e30cc feat(audit): record session action decisions (#129093)
* feat(audit): record session action decisions

* fix(protocol): preserve session sharing client compatibility
2026-08-26 08:21:37 -07:00
Peter Steinberger ef95d8f55e feat(secrets): agent-requested credentials the model never sees (#129670)
* feat(secrets): agent-requested credentials the model never sees

The new main-session secrets tool lets the agent request a credential by
name: the human enters the value in a masked question card (Control UI,
/ask/<id> deep link, iOS/macOS/Android), and the gateway diverts the
answer straight into the shared secret store at question.resolve. The
record, broadcast, waitAnswer, tool result, transcript, and model context
only ever carry a synthetic stored marker.

- protocol: additive secretStore binding, secretStoreExisting replacement
  metadata, and resolve-time secretStoreAllowedHosts (since 2026.8)
- gateway: store-bound question validation, admin-gated minting (blocks
  questions-scope self-answer escalation past secrets.store.set), shared
  redaction-first store write service reused by secrets.store.set
- tool: secrets request/list/delete; write-only by design, delete carries
  verified agent runtime identity; channel delivery is link-only so chat
  text is never captured as a secret
- Control UI: masked composer card with requester identity, store banner,
  editable allowed hosts, replacement warning, retry-on-validation-error,
  a standalone /ask/<id> page, and a named startup-JS baseline bump
- mobile: SecureField / password transformation for isSecret questions,
  no answer echo in terminal summaries; new native string registered in
  the locale-refresh inventory (generated artifacts stay workflow-owned)
- regression: claimed harness secret input stays out of session transcripts

Live-proven on an isolated dev gateway: real model turn, masked entry via
Playwright, value present only in secret_store_entries, absent from every
transcript, log, and the DOM.

* chore(protocol): regenerate protocol models and tool display

* fix(cli): read image string options through a typed helper

PR #129463 added four commander option narrowings in image.ts without
SAFETY coverage, leaving the assertion-safety ratchet red (21 > 17) for
every branch on current main. Replace the casts with a typeof-checked
read so the assertions are removed rather than annotated; each value is
still validated by its normalizer. SAFETY comments cannot work in this
file: the ratchet's raw scanner never rescans template tokens, so
comments after the first substitution template are unreadable to it.

* chore(protocol): refresh Swift models against current main

* chore(i18n): re-baseline the native inventory on current main

* docs(secrets): state the default-on tool policy and how to disable it

* fix(secrets): tell the model what the store actually does

The shipped tool description named the three actions and nothing else,
and no parameter carried a description. The model could not tell that
request blocks a human, that reason is shown to that human, what secret
and env select, or - the silent-failure case - that a secret stored with
no allowedHosts can never be substituted, so a successful request could
produce a permanently unusable credential. Move the description to the
presets module beside ask_user and document every parameter.

* refactor(agents): share one blocking-question lifecycle between tools

ask_user and secrets each carried their own registration, wait, and
cancel logic, and they had diverged: ask_user recovers an answer that
lands between its wait timeout and the cancel, while secrets discarded
it and reported no_answer even though the Gateway had already stored the
credential. One shared canceller and answer reader fixes that race for
both, folds the two divergent gateway-call types into one, and drops two
type assertions in favour of the canonical record guard (ask_user's
assertion baseline shrinks 11 -> 8).

Net +49 production lines: the shared module costs more than the
duplication it removes, and buys the correctness fix plus a single owner
for question lifecycle.

* fix(ui): keep the allowed-hosts field readable as an input

Main's composer restructure moved the free-text input styling into the
option-row context, so the store-request hosts field - which sits outside
a row - lost its border and read as static text. It is the one field the
operator is meant to review and edit before releasing a credential, so
give it its own border and focus ring.

* fix(secrets): close two credential-boundary holes in agent requests

Requests are now protected-secret only. list renders env values, so an
agent could request kind=env, watch a human type it into a masked box
under a no-visibility promise, then read it straight back; the tool text
even claimed values are never returned. Environment values stay operator
-set in Settings or the CLI, where they are agent-readable by design.

Store-bound questions are also bound to the run that requested them. The
resolve path authorized only the answering client, so a terminated or
replaced agent run could still have a credential written on its behalf -
the recorded runId was provenance, not closure-bound authority. Minting
now requires a runId and resolution revalidates that exact live run
immediately before the store write, with no await in between, failing
closed as QUESTION_REQUESTER_INACTIVE.

Both reported by ClawSweeper as P1 credential-boundary findings.
2026-08-26 08:10:16 -07:00
Peter Steinberger c2e36eec94 fix(android): skip scoped IPv6 discovery addresses (#130165) 2026-08-26 07:52:24 -07:00
Peter Steinberger 8416469994 fix(android): preserve document attachments in offline chat history (#130163) 2026-08-26 07:43:14 -07:00
Peter Steinberger 4f563f21ac fix(android): honor low camera snapshot quality (#130160) 2026-08-26 07:30:55 -07:00
Peter Steinberger a73260a5c2 fix(android): preserve filenames for shared images (#130156) 2026-08-26 07:25:19 -07:00
RoboClaw 44d60a29e6 fix: preserve unread reminder for open sessions (#129386)
* fix: preserve manual unread markers in open sessions

* fix: distinguish explicit session reads

* fix(ui): gate unread contract on gateway capability

* perf(ui): keep server capabilities out of startup bundle

* test(gateway): keep agent fixtures roster-consistent

* fix(sessions): preserve legacy read compatibility

* test(gateway): type agent fixture configs

* fix(ui): remove unread gateway fallback

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* test(infra): avoid fixed SSH tunnel port

* fix(ui): acknowledge unread after history commit

* docs: clarify unread upgrade boundary

* test(ui): drive mobile session menu by tap

* fix(ios): remove stale read reconciliation call

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-26 05:04:39 -07:00
Peter Steinberger 913ae20aeb fix(android): preserve photo library EXIF orientation (#129965) 2026-08-26 01:10:05 -07:00
Peter Steinberger d9506b2000 fix(android): preserve location opt-in during onboarding (#129961) 2026-08-26 01:05:36 -07:00
Peter Steinberger 36560c6a64 fix(android): restore attachment-only chat rewind and fork actions (#129951) 2026-08-26 00:45:55 -07:00
Peter Steinberger 144e4d5aeb fix(android): load chat media through gateway proxy paths (#129957) 2026-08-26 00:40:06 -07:00
Peter Steinberger 2941b3d779 fix(android): clean up cancelled camera recordings (#129948) 2026-08-26 00:22:31 -07:00
Peter Steinberger 7dcf0a54e9 refactor(android): reuse motion JSON parser (#129947)
Amp-Thread-ID: https://ampcode.com/threads/T-01a039f0-a030-77dc-8f5f-6efd2914415f

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 00:19:57 -07:00
Peter Steinberger fc64c5704a fix(android): preserve disconnected node gateway status (#129839) 2026-08-25 23:45:27 -07:00
Peter Steinberger 4048b57b7d fix(android): recover dropped gateway events (#129723) 2026-08-25 19:30:05 -07:00
Peter Steinberger c841a9958a fix(android): preserve notification forwarding consent (#129715) 2026-08-25 18:59:03 -07:00
Peter Steinberger 5af41d881e fix(android): preserve omitted image-only chat messages (#129711) 2026-08-25 18:34:23 -07:00
Vincent Koc 0036788055 refactor(android): reuse node JSON param parser (#120456) 2026-08-26 08:48:50 +08:00
WhatsSkiLL 771cd878dc feat(android): show session hierarchy in Threads (#128862)
* feat(android): show session hierarchy

* fix(android): surface collapsed descendant state

Worked on by:
- @Solvely-Colin

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>

* fix(android): satisfy hierarchy CI gates

Worked on by:
- @Solvely-Colin

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>

---------

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>
Co-authored-by: Colin Johnson <colin@solvely.net>
2026-08-25 19:46:31 -04:00
openclaw-mantis[bot] fe4554ade4 chore(i18n): refresh native locales (#129631)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-25 23:13:58 +00:00
Peter Steinberger d1b0b7732a fix(android): preserve captions when sharing multiple images (#129603) 2026-08-25 14:19:11 -07:00
ZYV5ge 43ffe41a4b fix(sessions): search visible categories across clients (#118912)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-25 14:15:52 -07:00
Peter Steinberger 52bce84ca9 fix(android): stop interrupted assistant audio and remove cached files (#129574) 2026-08-25 14:07:05 -07:00
Sasan e9620fba9d fix(gateway): usage.status no longer waits on provider HTTP (#121799)
* fix(gateway): refresh provider usage asynchronously

* fix(ui): report a stalled provider-usage refresh on Model Providers

The page observed the incomplete-usage marker but discarded the exhausted
outcome, so once the retry budget was spent it rendered ordinary provider
cards with no usage and no explanation — indistinguishable from providers
that report no usage at all. Keep the outcome and render the warning the
Usage page already owns, reusing usage.providerUsage.stalled rather than
minting a Model Providers key so no locale baseline churns.

A user-initiated refresh now restarts the retry budget. The notice tells the
operator to refresh, so the button has to hand back attempts to spend; only
the forced path resets it, or the budget could never exhaust.

Also fixes tsgo:core:test on the current head: createStore's inferred literal
had no usageStats, so the run-bookkeeping case could not stamp it, and
view.test.ts needed the new prop.

Closes the ClawSweeper P2 at model-providers-page.ts:169-175.

* fix(ui): keep the stalled usage notice when usage.status starts rejecting

loadModelProvidersData turned a rejected usage.status into providerUsage:
null, which the page read as a completed load. observe(false) then reset the
retry budget and cleared the stalled callout, so a permanently broken usage
endpoint rendered as ordinary cards with no usage and no explanation — the
same silent failure the callout was added to prevent. The reset also fired
mid-cycle: one incomplete response followed by one rejection restarted the
budget, so the notice could be deferred indefinitely.

Record the failure at its producer instead of inferring it downstream. A null
providerUsage also means "not loaded yet", and no caller can tell the two
apart, so load.ts now reports providerUsageFailed explicitly and the page
treats a failed read as unresolved rather than resolved-empty.

Found by a Codex review of 417d43b65d.

* revert(gateway): drop the opportunistic model-catalog fast path

It broke two chat.history tests on main — both assert the cold catalog loader
runs exactly once, and reading the prepared snapshot first means it never does.
checks-node-compact-small-10 was red for that reason.

The change was a separate-surface latency fix that this PR picked up in passing,
and the body already offered to split it. Dropping it is the honest resolution:
rewriting main's assertions to accommodate a drive-by optimization would trade
one concern's proof for another's convenience. optional-model-catalog.ts,
server-model-catalog-auth.ts and their test return to the merge-base.

This PR is now only the usage.status non-blocking contract and its clients.

* fix(usage): preserve incomplete retry state

* perf(ui): keep usage capability startup-neutral

* fix(ui): restore provider usage retry convergence

* fix(usage): restore retry and cache invariants

* fix(usage): stabilize provider convergence

* test(ui): exercise provider recovery path

* test(ui): remove stale usage route fixture field

* fix(macos): show provider usage errors

* fix(macos): bound usage retries per menu open

* fix(macos): end usage retries on menu close

---------

Co-authored-by: Josh Lehman <550978+jalehman@users.noreply.github.com>
2026-08-25 13:20:10 -07:00
Peter Steinberger bbfb7a9af0 fix(android): preserve background location during onboarding (#129543) 2026-08-25 13:16:20 -07:00
Peter Steinberger 7c790eedd8 fix(android): preserve contact display names without structured names (#129549) 2026-08-25 13:09:16 -07:00
Peter Steinberger 955d23d6b4 fix(android): preserve document-only chat messages (#129557) 2026-08-25 12:39:10 -07:00
WhatsSkiLL 0b867e14fe feat(android): unify agent and session pickers (#128309)
* feat(android): unify agent and session pickers

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>

* chore:refresh-native-i18n-inventory

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>

* fix:preserve-unknown-chat-agent-selection

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>

* fix(android): keep unknown agent picker usable

Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>

---------

Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com>
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
2026-08-25 08:11:36 -04:00
Peter Steinberger 4dc7bb7411 chore(deps): refresh dependencies after seven-day cooldown (#129187)
* chore(deps): refresh dependencies after cooldown

* fix(gateway): emit append-only Responses content events

* chore(deps): retain unverified Sherpa runtime
2026-08-25 05:00:46 -07:00
Peter Steinberger 2464c5c637 fix(android): preserve photo orientation in chat images (#129136) 2026-08-25 01:29:33 -07:00
Peter Steinberger d1eead9e70 fix(android): preserve gateway context paths in control pages (#129087) 2026-08-25 01:07:02 -07:00
Peter Steinberger 50b3e4bc5b fix(android): keep unavailable SMS out of onboarding approval (#129077) 2026-08-25 00:35:01 -07:00
Peter Steinberger 0a04f23bc3 fix(android): select writable calendars for new events (#129055) 2026-08-24 23:53:07 -07:00