Commit Graph

66965 Commits

Author SHA1 Message Date
Sebastien Tardif
0d6a1dcb53 fix(install): trap SIGINT so Ctrl+C exits cleanly during upgrade doctor (#76386)
* style: restore exec approval e2e formatting

* fix(install): trap SIGINT so Ctrl+C exits cleanly during upgrade doctor

Three changes to fix the install script's Ctrl+C handling:

1. Add INT/TERM signal traps that clean up temp files and exit with
   the correct signal exit codes (130 for SIGINT, 143 for SIGTERM).

2. Preserve signal exit codes (>128) through run_quiet_step so the
   doctor path can distinguish user cancellation from normal errors.
   Non-signal failures still return 1, preserving existing caller
   semantics for all other installer steps.

3. Fix guardCancel in onboard-helpers.ts: exit(0) changed to exit(1)
   so Clack prompt cancellation (Escape/Ctrl+C) is treated as failure,
   not success. This prevents the installer from continuing with plugin
   updates after the user explicitly cancelled.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>

* fix(install): abort dashboard launch on doctor cancellation

When a user cancels the interactive upgrade-doctor prompt (Clack
cancellation exits 1, SIGINT exits 130), clear should_open_dashboard
so the installer does not launch a dead dashboard after an incomplete
upgrade.

Also propagate non-zero exit from run_doctor() so the non-interactive
upgrade path correctly skips dashboard launch on failure.

* fix: guard every run_doctor caller and add focused tests

The existing-config path called run_doctor without checking its return
value, so a failed or cancelled doctor would still launch the dashboard.
Now both run_doctor call sites guard the return value with if-then.

Adds focused tests verifying: every run_doctor caller is guarded,
dashboard flag is cleared on doctor failure, signal exit codes
propagate through run_quiet_step, and SIGINT (exit 130) triggers
abort_install_int.

* retrigger proof check

* fix: exit 130 on Clack cancellation so installer treats it as SIGINT

guardCancel now exits with 130 (SIGINT convention) instead of 1. When
the user presses Ctrl+C at an interactive doctor prompt, the installer
sees doctor_exit=130 and calls abort_install_int, aborting cleanly
instead of continuing after exit 1.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>

* fix: narrow exit 130 to doctor-prompter path only

Revert guardCancel to exit 0 by default (matching main) and pass
exit code 130 only from doctor-prompter where the installer needs
to distinguish user cancellation from normal failures.

This preserves the existing cancellation behavior for configure,
wizard, gateway, and daemon prompts while keeping the SIGINT
convention for the installer's doctor subprocess.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>

---------

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 06:19:44 +00:00
Peter Steinberger
98ca8c945e fix(ui): stop repeating tool names in expanded activity (#100606)
* fix(ui): stop repeating tool names in expanded activity

* fix(ui): stop repeating tool names in expanded activity
2026-07-06 07:17:23 +01:00
Peter Steinberger
2559c92a25 perf(test): reuse unit-fast discovery analysis 2026-07-06 02:15:32 -04:00
github-actions[bot]
d83ee4c44f chore(i18n): refresh native locales 2026-07-06 06:12:22 +00:00
github-actions[bot]
241c761d00 chore(ui): refresh control ui locales 2026-07-06 06:01:14 +00:00
Vincent Koc
7b6b39e0f8 fix(ci): keep full Anthropic Docker proof advisory 2026-07-05 23:01:01 -07:00
Peter Steinberger
f9bf5f5897 fix(android): keep stale PTT from restarting capture (#100552)
* fix(android): serialize background PTT lifecycle

Follow-up hardening for #99840 after the initial fix landed in #100483.

Co-authored-by: xialonglee <li.xialong@xydigit.com>

* docs(changelog): note Android PTT lifecycle hardening

* fix(android): order PTT ownership transitions

* test(android): opt in to coroutine scheduler API

* fix(android): serialize voice capture ownership

* test(android): drive PTT ordering with test dispatcher

* test(android): isolate PTT ordering preferences

* test(android): control PTT invoke dispatchers

* test(android): isolate PTT coroutine dispatchers

* test(android): use current Robolectric resolver API

* test(android): register speech service without deprecated APIs

* test(android): control stale PTT cleanup dispatch

* test(android): isolate voice mode reassertion

* fix(android): clean voice capture on permission loss

* fix(android): invalidate PTT before cancel wait

* test(android): preserve starts queued after cancel

* fix(android): keep one-shot PTT retries idempotent

---------

Co-authored-by: xialonglee <li.xialong@xydigit.com>
2026-07-06 06:51:46 +01:00
Peter Steinberger
62d957634c fix(agents): cap effective compaction reserve (#100621)
* fix(agents): cap effective compaction reserve

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

* docs(changelog): link small-context compaction fix

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 06:49:44 +01:00
qingminlong
ae4a1e236f fix(pdf): reject fractional page selections (#99399)
* fix(pdf): reject fractional page selections

* chore: refresh pdf fractional page checks

* chore: refresh failed checkout checks

* test(pdf): prove fractional pages fail at tool boundary

* test(pdf): cover mixed fractional page ranges

* fix(pdf): validate page selections before loading

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 06:46:32 +01:00
heichl_xydigit
00f5df24e5 fix(webchat): keep context indicator visible with stale token data (#89772)
* fix(webchat): keep context indicator visible with stale token data

The context usage indicator was disappearing after sending a message because
totalTokensFresh was set to false during the run, even though we had valid
token count data from before the message was sent.

Changes:
- Modified getContextNoticeViewModel to show the indicator even when
  totalTokensFresh is false, as long as totalTokens is non-zero
- Added isStale flag to indicate when token data is not fresh
- Applied subtle visual styling for stale data (lighter colors, 5% opacity)
- Added "(updating)" suffix to the percentage text and title when stale
- Updated tests to verify the new behavior

Fixes #89662

* test(ui): cover stale context indicator behavior

* docs(changelog): note Control UI fix

---------

Co-authored-by: 黑承亮0668000844 <bladin@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 06:44:41 +01:00
Ben.Li
b3ba030707 fix(ui): show cron job model selection (#95341)
* fix(ui): show cron job model selection

* fix(ui): scope quick-create cron model selection

* test(ui): cover cron model quick create

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 06:43:40 +01:00
cxbAsDev
924f93eee0 fix(update-check): wrap malformed npm view JSON parse in try/catch (#100338)
* fix(update-check): wrap malformed npm view JSON parse in try/catch

* proof(update-check): use real fake-npm executable in proof script

* test(update-check): focus malformed JSON coverage

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 06:43:11 +01:00
pick-cat
ea99b96ddd fix(ui): preserve live tool stream order (#93184)
* fix(ui): preserve live tool stream order

* chore: trigger CI

* refactor(ui): preserve causal tool stream ordering

* docs(changelog): note Control UI fix

* test(ui): use current tool event contract

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 06:43:05 +01:00
WhatsSkiLL
b52c22f3da fix(android): tighten Voice tab box sizing (#100491)
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
2026-07-06 06:42:31 +01:00
Wynne668
7e81f15961 fix: keep heredoc exec bodies out of command summaries (#99379) 2026-07-06 05:41:29 +00:00
Peter Steinberger
dd972b8bf7 fix: replies fail when memory flush is exhausted (#100618)
* fix(reply): recover from memory flush exhaustion

Co-authored-by: 忻役 <xinyi@mininglamp.com>

Co-authored-by: Jerry-Xin <jerryxin0@gmail.com>

* test(reply): remove unused memory flush result

---------

Co-authored-by: Jerry-Xin <jerryxin0@gmail.com>
2026-07-06 06:41:16 +01:00
krissding
e183d665ee fix(infra): add NaN guard for unparseable timestamp in session-cost-usage (#99420)
new Date(parsed.timestamp).getTime() can return NaN for unparseable
strings. Without a guard, NaN silently propagates into downstream
usage/cost calculations and corrupts billing data.

Add Number.isNaN(timestamp) check, falling back to 0 (same default
as the path when no timestamp key is present).

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 05:41:07 +00:00
ooiuuii
f7f1be276a Harden macOS SQLite WAL checkpoints (#99067) 2026-07-06 05:40:58 +00:00
Masato Hoshino
09b5cdc17f improve(doctor): warn when cron jobs keep failing consecutive runs (#99606)
* improve(doctor): warn when cron jobs keep failing consecutive runs

* improve(doctor): message restart-interrupted runs in the chronic cron failure advisory
2026-07-06 05:40:55 +00:00
pick-cat
7c9c2ef0c3 fix(infra): enforce maxBytes in body-less HTTP error snippet path (#99340)
* fix(infra): enforce maxBytes in body-less HTTP error snippet path

* chore: rebase to trigger CI after boundaries check fix

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-06 05:40:52 +00:00
xingzhou
a13acacd3c fix(discord): keep command previews emoji-safe (#99539) 2026-07-06 05:40:39 +00:00
cxbAsDev
5dbad1102f fix(sessions): log warning when parseJsonlEntries skips malformed JSONL lines (#98669)
* fix(sessions): log warning when parseJsonlEntries skips malformed lines

* fix(sessions): also warn in buildSessionInfo for malformed JSONL lines

* fix(sessions): warn in parseSessionEntries for malformed JSONL lines

* test(sessions): add warning regression tests for parseSessionEntries

* test(sessions): add parseJsonlEntries warning regression test

Add test verifying parseJsonlEntries logs warning for malformed JSONL
lines via loadEntriesFromFile, covering the second of three instrumented
session JSONL readers.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(sessions): update parseSessionEntries warning expectations after rebase

* style(sessions): remove unnecessary type assertion in test

* test(sessions): add buildSessionInfo warning regression tests via SessionManager.list

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-06 05:40:21 +00:00
cxbAsDev
437b177b7d fix(agents): suppress unhandled stdout/stderr stream errors in waitForChildProcess (#100522)
* fix(agents): suppress unhandled stdout/stderr stream errors in waitForChildProcess

* proof(agents): add real behavior proof script for child-process stream error catch

* test(agents): drop synthetic child-process proof wrapper

* proof(agents): replace wrapper with real child-process stream error proof

* style: apply oxfmt to changed files

* test(agents): drop synthetic child-process proof wrapper

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-05 22:35:15 -07:00
cxbAsDev
b6e4cd6bf4 fix(gmail-watcher): catch renewal interval errors (#100342)
* fix(gmail-watcher): catch renewal interval errors

* fix(gmail-ops): catch foreground renewal interval errors; add failing regression test

* proof(gmail-watcher): rewrite proof with real fake-gog subprocess for before/after

* fix(proof): make gmail-watcher foreground proof child exit reliably

* fix(proof): kill fake gog serve child and check spawnSync error in renewal proof

* test(gmail): narrow renewal rejection coverage

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-05 22:34:14 -07:00
Peter Steinberger
89f911f322 refactor(ui): consolidate sidebar navigation contract (#100612)
* fix(ui): restore sidebar navigation contract

* refactor(ui): remove duplicate sidebar navigation model
2026-07-06 06:30:57 +01:00
lin-hongkuan
aca316ee16 fix(anthropic): normalize tuple tool schemas for Opus 4.8 (#100492)
* fix(anthropic): normalize tuple tool schemas

* fix(anthropic): preserve schema instance values

* test(anthropic): cover Opus tuple schemas live

---------

Co-authored-by: lin-hongkuan <lin-hongkuan@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-05 22:28:37 -07:00
Vincent Koc
1f36539ab0 test(release): declare CLI live fixture agent 2026-07-05 22:27:46 -07:00
Marvinthebored
cbcd6a102c fix(clickclack): reply to a top-level message in-channel, not as a new thread (#100582)
* fix(clickclack): reply to a top-level message in-channel, not as a new thread

sendClickClackText routed any replyToId to createThreadReply, and the inbound
handler stamps replyToId = <triggering message id> on every reply. As a result
every reply to a top-level channel message opened its own thread, so the main
channel timeline showed nothing and the chat was effectively unusable.

Route a bare replyToId to the main channel as a quote-reply (quoted_message_id)
instead — matching the reply-to affordance of the Discord/Slack/Telegram
channels — and reserve threads for genuine thread context (an explicit threadId
or a thread-kind target). DM replies likewise quote-reply in the same
conversation. quoted_message_id is omitted when there is no reply context, so
plain sends are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(clickclack): cover quote payload typing

---------

Co-authored-by: Marvinthebored <marvinthebored@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-05 22:27:27 -07:00
Voscko
d924ddc428 feat(android): show cron job details (#95107)
* feat(android): add cron job detail view

* fix(android): harden cron job detail loading

* chore(android): refresh native i18n inventory

* test(plugins): register workboard typed hook

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 06:25:37 +01:00
Peter Steinberger
06fbd84d2b test(plugins): register Workboard typed hook contract (#100611) 2026-07-05 22:13:45 -07:00
Peter Steinberger
b18af41f52 perf(gateway): skip redundant startup policy loads 2026-07-06 01:10:00 -04:00
Peter Steinberger
d537139521 feat(macos): adopt the shared read-only chat transcript cache (#100275)
* feat(macos): adopt shared read-only chat transcript cache

Cache-first cold open for the macOS chat window/panel: the last known
transcript and session list paint immediately from the shared SQLite
transcript cache, then live gateway history replaces them wholesale.
Recent chats stay browsable read-only while the gateway is unreachable;
sending remains gated by connection state.

- Wires OpenClawChatSQLiteTranscriptCache into WebChatSwiftUIWindowController;
  DB at ~/Library/Application Support/OpenClaw/chat-cache.sqlite.
- Gateway identity (MacChatTranscriptCache.gatewayID), derivable offline:
  local keys on the canonical gateway state dir; remote/direct keys on the
  full canonical URL (scheme, host, resolved port, percent-encoded
  path/query); remote/ssh keys on the SSH target plus the resolved remote
  gateway port, mirroring the tunnel port resolution. Unconfigured mode
  gets no cache.
- macOS file protection: no per-file Data Protection classes; iOS-only
  attribute stays gated behind #if os(iOS) in the shared store, and the
  per-user container plus FileVault protect at rest.
- Onboarding chat stays uncached (transient guided setup session).

Part of #100194

* feat(macos): wire the offline command outbox into chat windows

* style(macos): fix orphaned doc comment; regenerate docs map

* style(macos): doc-comment lint fix; regenerate docs map on current main
2026-07-06 05:57:16 +01:00
Vincent Koc
55a0012c44 fix(ui): restore sidebar navigation exports 2026-07-06 06:56:09 +02:00
Peter Steinberger
3b33613868 docs(changelog): credit Vincent replay fixes (#100609) 2026-07-06 05:53:35 +01:00
Peter Steinberger
6441858e21 docs: refresh generated docs map (#100603) 2026-07-06 05:47:52 +01:00
Peter Steinberger
87d8fa7e3a fix(config): compare size guard against canonical input (#100591)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 05:47:49 +01:00
github-actions[bot]
82f5ac150f chore(i18n): refresh native locales 2026-07-06 04:42:22 +00:00
pick-cat
f296dc4e68 fix(sessions): persist sender metadata in user turn transcript JSONL (#90552)
* fix(sessions): persist sender metadata in user turn transcript JSONL

Thread senderId/senderName/senderUsername/senderE164 from the channel
inbound context into the persisted user-turn transcript message so that
group chat session JSONL records include __openclaw sender identity.

Ref #90531

* fix(sessions): exclude senderE164 from persisted transcript for privacy

Remove phone-number field from the persisted __openclaw sender envelope,
keeping only senderId, senderName, and senderUsername. Privacy-sensitive
E.164 metadata can be added back by maintainers if needed.

Ref #90531

* fix(infra): spread base fields in applyExecPolicyLayer return values

The `as TBase & ExecPolicyLayer` casts failed because the returned
objects did not spread `...base`, losing generic TBase fields.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(infra): use ...base spread instead of ...baseWithoutMode

The baseWithoutMode destructuring produces Omit<TBase, "mode"> which is
not assignable to TBase & ExecPolicyLayer when the as-cast is removed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(infra): preserve baseWithoutMode in second applyExecPolicyLayer branch

The second branch (security/ask override without a mode change) must
exclude base.mode from the spread so that mode is not leaked into results
when only security or ask fields are being overridden.

Without this fix, the spread ...base carries mode through to the returned
object, breaking callers that expect applyExecPolicyLayer to clear stale
mode when applying explicit security/ask policy fields.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sessions): scope persisted sender attribution

* fix(sessions): preserve sender metadata through hooks

* refactor(sessions): keep sender metadata path lean

* fix(sessions): preserve sender metadata in runtime writes

* fix(sessions): preserve queued sender attribution

* test(sessions): use complete message fixtures

* refactor(sessions): rely on narrowed user message type

* test(sessions): use shared temp directory helper

* test(sessions): align sender metadata assertions

* fix(sessions): honor sender metadata redaction hooks

* test(agents): use automatic temp cleanup

* test(sessions): cover queued turn provenance

* test(auto-reply): expect room sender metadata

* refactor(sessions): isolate queued transcript context

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 05:32:19 +01:00
Peter Steinberger
6acc0270aa docs(changelog): credit UI and TUI fixes (#100599) 2026-07-06 05:32:10 +01:00
Peter Steinberger
eedd309c4a fix(config): dedupe repeated validation warnings (#100569)
* fix(config): dedupe repeated validation warnings

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

* docs(changelog): defer config diagnostic note

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 05:30:04 +01:00
Sulaga
2b51c255f8 feat(tencent): add Tencent Hy3 provider (TokenHub and TokenPlan) (#99076)
Summary:
- Merged feat(tencent): add Tencent Hy3 provider (TokenHub and TokenPlan) after ClawSweeper review.

Automerge notes:
- Ran the ClawSweeper repair loop before final review.
- Included post-review commit in the final squash: fix(tencent): preserve TokenHub auth compatibility
- Included post-review commit in the final squash: refactor(tencent): unify TokenPlan env/flag naming with TokenHub
- Included post-review commit in the final squash: docs: refresh Tencent provider docs metadata
- Included post-review commit in the final squash: fix: allow TokenPlan provider config overlays
- Included post-review commit in the final squash: docs: dedupe Tencent provider glossary labels
- Included post-review commit in the final squash: fix(tencent): repair TokenHub model defaults

Validation:
- ClawSweeper review passed for head 30c9fc130f.
- Required merge gates passed before the squash merge.

Prepared head SHA: 30c9fc130f
Review: https://github.com/openclaw/openclaw/pull/99076#issuecomment-4888527271

Co-authored-by: leisang <leisang@tencent.com>
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
2026-07-06 04:29:48 +00:00
harish ganeshmurthy
cfcdf2a519 fix(tui): follow active gateway port (#73338)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 05:27:35 +01:00
ml12580
d11c80f440 fix(tui): show startup initialization activity (#93999)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 05:27:10 +01:00
Wynne668
f36f3f30ea fix(ui): recover from stale Control UI bundles (#99111)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 05:26:49 +01:00
Peter Steinberger
a0e591c863 fix(cli): reduce plugin hook fallback noise (#100554)
* fix(cli): reduce plugin hook fallback noise

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

* docs(changelog): defer plugin diagnostic note

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 05:26:37 +01:00
RayRuan
e9a5dc9bf6 fix(ui): preserve structured connect failures (#54758)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 05:26:33 +01:00
Goutam Adwant
ae6da5bc77 fix(ui): surface coalesced update restarts (#93082)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 05:26:14 +01:00
Peter Steinberger
0acd851a3b feat(agents): add managed git worktree lifecycle (create/provision/snapshot/restore/GC) (#100535)
Centralized managed worktrees under <state-dir>/worktrees/<repo-fingerprint>/<name>
with branch-per-task (openclaw/<name>), .worktreeinclude provisioning, an optional
.openclaw/worktree-setup.sh repo hook, and a SQLite registry in the shared state DB.
Removal always snapshots the tree (untracked included, gitignored excluded) to
refs/openclaw/snapshots/<id>; restore rebuilds the branch at the original commit with
the snapshot content as uncommitted state. Lossless run-end cleanup, 7-day idle GC for
run-owned worktrees (manual exempt), orphan reconciliation, 30-day snapshot retention.
Surfaces: worktrees.* gateway RPC (operator.admin mutations), openclaw worktrees CLI,
Control UI page, plugin-SDK facade + Workboard kind:"worktree" materialization.

E2E-verified on Testbox: full create->work->remove->restore->gc lifecycle.
2026-07-06 05:24:58 +01:00
Peter Steinberger
4b7661e9a0 feat(ios): durable offline command outbox for chat sends (#100331)
* feat(ios): durable offline command outbox for chat sends

Text messages sent while the gateway is unreachable queue in a durable
per-gateway outbox (new outbox_commands table in the per-gateway chat
cache SQLite store, schema v2) instead of failing. Queued bubbles render
with visible Queued/Sending/Not sent states and flush strictly in
createdAt order once transport health recovers; each command's client
UUID rides as the send idempotency key, so at-least-once delivery plus
gateway dedupe keeps the transcript exact.

Contract summary:
- Bounds: 50 queued commands per gateway; refused enqueues keep the
  draft. Queued rows older than 48h expire to failed("expired") rather
  than silently sending stale commands; tap-to-retry refreshes
  createdAt so an expired row can resend as new intent.
- Failure taxonomy: transport-level failures keep rows queued without
  burning retry attempts (backoff ladder, then health drops so the
  reconnect machinery owns pacing); gateway rejections burn attempts
  and fail terminally after 3, with context-menu retry/delete.
- Deletes are tombstoned synchronously and rechecked after the claim
  await so an active flush can never send a removed command; Delete is
  hidden while a bubble is already in flight; offline enqueue is
  guarded against double submit during the health probe.
- Post-reconnect live sends route behind draining outbox rows (FIFO),
  and a cold open assumes a backlog until restore adopts durable rows.
- Crash safety: 'sending' rows revert to 'queued' at startup; flushed
  turns are spliced into the session's cached transcript before their
  outbox row is deleted, and stale history snapshots cannot evict a
  just-flushed turn until a snapshot confirms it.
- Per-gateway scoping and purge ride the transcript cache: one SQLite
  file per gateway; reset/forget drops the queue with the cache.
  Fixture/unpaired transports get no outbox.

Part of #46664

* chore(ios): sync native i18n inventory

* style(chat-ui): satisfy strict SwiftFormat lint (doc comment, scope blank line)
2026-07-06 05:23:46 +01:00
Peter Steinberger
0d509abe9a feat(ios): richer Settings About screen with mascot hero and project links (#100531)
* feat(ios): richer Settings About screen with mascot hero and project links

* chore(i18n): register About screen strings in native i18n inventory
2026-07-06 05:22:26 +01:00