70933 Commits
Author SHA1 Message Date
Peter SteinbergerandGitHub 7116ad6e28 fix(agents): preserve fresh workspace hatch (#111553) 2026-07-19 15:35:54 -07:00
Peter SteinbergerandGitHub 473962b7de fix(onboarding): scope recommendations by workspace (#111560) 2026-07-19 15:34:56 -07:00
Peter SteinbergerandGitHub 5fcf7e75de test: clear plugin runtimes between shared files (#111556) 2026-07-19 15:28:46 -07:00
Peter SteinbergerandGitHub a1e5b6ef0e fix(doctor): honor active migration targets (#111555) 2026-07-19 15:27:03 -07:00
Peter SteinbergerandGitHub 6c25f5ae33 fix(ui): keep steered composer messages visible until the transcript owns them (#111540)
* fix(ui): keep steered composer messages visible until the transcript owns them

Typing while the agent was busy showed a brief steering state and then the
message vanished until the next full history reload. The steer path removed
the queued row at ack time and only restored the steered chip when the tab
had adopted the active run id, which never happens for runs started by
automations, other clients, or tabs opened mid-run.

- steer sends reuse the durable row's sendRunId as the wire idempotencyKey so
  delivered-turn and history-proof reconciliation can correlate steered rows
- the steered chip is restored after every accepted ack, keyed to the active
  run when the tab still tracks it, else to the steer's own gateway lifecycle
- terminal events materialize acknowledged steered chips into the transcript
  before clearing them, and stale history reloads keep idempotency-marked
  local turns until authoritative history catches up
- in-flight (unacknowledged) steers are never materialized, so a rejected
  send cannot leave a phantom user turn behind

Fix authored with Codex; reviewed and hardened via structured review.

* fix(ui): use bracket access for __openclaw test marker (no-underscore-dangle)

* fix(ui): resolve steered attachment payloads through the store before materializing

Queue rows carry attachment metadata only; composer upload bytes live in the
payload store. Resolving through getChatAttachmentDataUrl keeps attachment-only
steers visible when their chip retires (Codex connector review finding).

* fix(ui): only user-role entries satisfy queued-turn presence checks

An assistant entry can carry the same run's idempotency key; matching it let
the chip re-add and terminal materialization skip the user's copy
(ClawSweeper rank-up move).
2026-07-19 15:17:08 -07:00
Peter SteinbergerandGitHub bcbbf28093 docs(plugin-sdk): migrate retained SDK contracts (#111535)
* refactor(plugin-sdk): finish retained public demotions

* test(plugin-sdk): tighten surface budgets

* fix(plugin-sdk): retain static tool metadata contract

* fix(plugin-sdk): keep retained exports pending

* fix(plugin-sdk): satisfy registry lint

* docs(plugin-sdk): document retained consumer seams
2026-07-19 15:00:45 -07:00
Peter SteinbergerandGitHub c6f7ddff1a fix(ui): show attributed sender identity properly in chat (#111537)
* fix(ui): show attributed sender identity properly in chat

Three defects around multi-user sender attribution:

- Sender labels rendered as 'name (<profile uuid>)': resolveSenderLabel
  appended the id part even when it is an opaque UUID. Suppress UUID
  suffixes server-side and strip already-baked ones in the UI normalizer
  so historical transcripts read clean too.
- Attributed messages showed initials instead of the author's avatar:
  chat sender metadata carries only the profile id, so resolveAvatar now
  derives the canonical /api/users/<id>/avatar route (upload -> gateway
  Gravatar proxy -> initials) from UUID-shaped sender ids.
- Author identity was hover-only: identity-resolving gateways keep the
  always-visible avatar gutter even in 1:1-shaped sessions, and the
  gutter renders the message author's avatar (with identity-colored
  initials fallback) instead of the local viewer's.

* fix(ui): preserve legacy author identity and 404-safe sender avatars

Review findings: stripping the baked UUID suffix discarded the only
author key on legacy rows (the gutter then showed the local viewer's
face under another person's label) — the UUID now survives as
structured non-display sender identity. And the derived avatar route
can 404 (no upload, no Gravatar), so the gutter image swaps to
identity-colored initials on error instead of a broken image.

* fix(ui): attribute bare-UUID legacy sender labels to their profile

A nameless legacy sender labels as the bare profile UUID; recognize that
whole-label shape as the sender id (keeping it as last-resort display)
so the row resolves the author's avatar instead of misattributing to the
local viewer.
2026-07-19 14:59:33 -07:00
Marcus CastroandGitHub b887cd01d8 fix(whatsapp): use canonical media primitives (#107017)
* feat(media): forward outbound image optimization

* feat(media): support bounded Opus transcoding

* refactor(whatsapp): reuse canonical outbound media helpers

* refactor(whatsapp): reuse canonical base64 validation

* chore(plugin-sdk): refresh API baseline
2026-07-19 18:54:14 -03:00
Peter SteinbergerandGitHub 0fff74c800 feat(ios): replace the tab bar with a black overlay sidebar (web parity + dashboard) (#111339)
* feat(ios): unify navigation on a black overlay sidebar and drop the phone tab bar

* test(ios): rework navigation tests and snapshot UITests for the sidebar shell

* chore(i18n): sync derived native catalogs for sidebar strings

* feat(ios): reveal the sidebar beneath a push-away rounded content card

* feat(ios): web-parity sidebar — agent switcher, pinned pages, sessions vocabulary, uniform content card

* chore(i18n): translate sidebar and session strings across native catalogs

* fix(ios): sidebar footer shows connection dot only when degraded, like the web

* feat(ios): sidebar toggle top-left with edge-swipe open; card surface owns the drawer chrome

* fix(ios): single sidebar refresh owner, alias-aware Home badges, RM tap-close, user pin order, drop vestigial idiom shims

* feat(ios): align sidebar with the mobile prototype — agent roster, theme-following palette, web session subtitles, row badges

* chore(i18n): translate agent roster and Recent strings across native catalogs

* feat(ios): frosted-glass agent picker with uniform drawer insets

* fix(ios): address sidebar review findings — settings toggle edge, per-agent scoping, alias-aware selection, cron paging, refresh cadence

* fix(ios): clear landing CI blockers
2026-07-19 14:50:39 -07:00
Peter SteinbergerandGitHub 5420c5c409 feat(clickclack): session discussions — bound channels, side agent, lifecycle sync (#111503)
* feat(clickclack): add session discussions

* chore: remove release-owned changelog entry

* refactor(clickclack): split discussion service workflows

* fix(clickclack): satisfy plugin surface gates

* fix: narrow routing peer in inbound test and refresh docs map
2026-07-19 14:47:14 -07:00
Peter SteinbergerandGitHub c45e3dd315 fix(gateway): avoid macOS startup freeze during worker reconciliation (#111533)
* fix(gateway): warm system CA before worker reconciliation

* fix(gateway): tolerate denied worker warmup
2026-07-19 14:39:25 -07:00
Peter SteinbergerandGitHub 6a408983d0 fix(onboard): make first-run hatch reliable (#111465)
* fix(onboard): harden first-run handoff

* style(ui): avoid onboarding comparator shadowing

* fix(agents): preserve mixed refresh failures
2026-07-19 14:38:58 -07:00
Peter SteinbergerandGitHub 77378cb0f3 fix(ui): keep stray file drops from leaving Control UI (#111531)
* fix(ui): guard unhandled file drops

* test(ui): exercise accepted file drop target

* test(ui): prove file drop guard in browser
2026-07-19 14:38:30 -07:00
Peter SteinbergerandGitHub 171a3852ba fix: install exact app recommendations and retry failures (#111518)
* fix(onboard): preserve recommendation install retries

* fix(onboard): preserve bootstrap recommendation retries

* fix(onboard): commit recommendation outcomes after config

* fix(onboard): guard recommendation state transitions

* fix(onboard): reconcile durable skill installs

* docs(clawhub): clarify search publisher contract
2026-07-19 14:10:50 -07:00
Peter SteinbergerandGitHub 13ed8b5aa1 perf(state): cap the per-agent SQLite handle cache with LRU eviction (#111411)
* perf(state): cap the per-agent SQLite handle cache with LRU eviction

Multi-tenant hosts open one WAL database per agent (~3 file descriptors
each); the process-local handle cache was unbounded, so large fleets
exhausted descriptors. Cache hits now refresh LRU recency, and cache-miss
opens evict the oldest non-transactional handle before constructing the
new one, capped at 64 open handles. Eviction closes the process-local
handle only; registry rows and durable data are untouched and evicted
databases reopen transparently on next access.

* fix(state): fleet-wide introspection reads stop opening writable agent databases

Live testing an 81-agent gateway surfaced three full-fleet sweeps that
opened every agent's SQLite database writable (schema ensure + registry
write transaction per open): the 60s health snapshot's per-agent session
summaries, usage-cost cache reads behind the gateway usage endpoints, and
the zalouser doctor detector that ran from every CLI startup. Each sweep
churned the new bounded handle cache and saturated the event loop.

Session listing gains a readonly non-registering variant (SDK exposes it
as an additive readOnly flag on listSessionEntries); health and usage
reads use it, with transient-lock tolerance owned by the health caller.
The zalouser detector gates on the channel/credentials actually existing,
collects once per pass, and detection reads go readonly while migration
apply stays writable. The usage refresh queue's flat 50ms busy retry
becomes exponential backoff capped at 5s, and refreshing summaries retain
their cache timestamp so the 30s TTL gates fleet rescans.

* perf(state): make per-agent handle eviction and reopen cheap

Eviction only works if reopen is cheap. Reopens after eviction previously
repeated full first-open work; ensureOpenClawAgentSchema takes BEGIN
IMMEDIATE and the registry upsert writes the shared state DB, so reopens
blocked in synchronous busy waits (observed 14-22s event-loop stalls with
reconcile workers holding write transactions). Owner/schema validation and
registration now run once per path per process with invalidation on
quarantine and disposal; the read-only integrity guard still runs on every
physical open. Eviction closes with a PASSIVE WAL checkpoint instead of
TRUNCATE, which waits on readers; orderly dispose keeps TRUNCATE so
sidecars are flushed before unlink.

* ci: regenerate plugin-SDK API baseline, drop prod test-seam exports, mock readonly accessor

The additive readOnly flag on the SDK listSessionEntries changed the
declaration surface, so the generated API baseline is regenerated via the
sanctioned script. Knip forbids production exports consumed only by tests:
the usage-cache testApi export is removed outright and the refresh-queue
seam moves behind an env-gated global exposed by a test-support module.
health.plugins.test's full session-accessor mock gains the newly imported
readonly listing.
2026-07-19 13:58:01 -07:00
Peter SteinbergerandGitHub f6a3f22629 refactor(channels): consolidate message action read gate (#111529) 2026-07-19 13:57:39 -07:00
Peter SteinbergerandGitHub 1a574923cb fix(onboard): honor remote flags in interactive setup (#111517)
* fix(onboard): honor interactive remote flags

* fix(onboard): clear credentials for changed gateway

* fix(onboard): validate remote URL before probe

* test(onboard): mark synthetic remote tokens

* test(onboard): avoid literal credential fixtures

* refactor(onboard): name remote probe auth role

* refactor(onboard): keep remote probe seed canonical

* test(onboard): type remote URL validator mock
2026-07-19 13:25:46 -07:00
Sally O'MalleyandGitHub cb0bbcb177 fix: update debugging skill file references (#111474) 2026-07-19 15:59:01 -04:00
22c336c0b4 fix(plugins): load active generation after upgrades (#111141)
* fix(plugins): load newest managed generation when a prior install lingers

An upgrade writes a plugin's new version into a distinct managed project
directory (an `__openclaw-generation__` dir) without removing the previous one.
The persisted install record keeps pointing at the older, still-present
directory, so `mergeRecoveredManagedNpmRecord` falls through to returning the
persisted record and the runtime imports the stale version even though the newer
generation is installed. `isUnavailableManagedNpmInstallRecord` only self-heals
when the persisted path is gone, so an upgrading install (both directories
present) is not covered — `plugins inspect <id> --runtime` reports the old
version and `install`/`update` appear to silently no-op.

- Recovery now selects the highest-version record per plugin id across the
  legacy flat dir and any generation dirs, instead of whichever project root
  sorts last (generation-dir suffixes are hashes, so on-disk order is unrelated
  to version).
- When the recovered managed install is a strictly newer version at a different
  path, repoint to it, reusing the metadata merge the unavailable-path branch
  uses. The repoint is gated to persisted records that live inside the managed
  npm root, so intentional custom/outside npm installs are left untouched.

This is a recovery-layer self-heal, so it also repairs installs that already
diverged from an earlier upgrade.

Refs #107228

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

* fix(plugins): honor active managed generations

Recover ambiguous managed installs by package recency only when no usable active ledger path exists, and let doctor retire non-authoritative generations safely.

Co-authored-by: Peter Lindsey <peter@lindsey.jp>

* test(plugins): use shared temp cleanup

* fix(plugins): compare active paths per platform

* fix(plugins): use managed install timestamps

* test(plugins): timestamp managed project roots

* test(plugins): exclude retired legacy recovery

* refactor(plugins): keep recovery candidate private

---------

Co-authored-by: Peter Lindsey <peter@lindsey.jp>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 12:50:35 -07:00
Peter SteinbergerandGitHub 50114c5163 fix(test): recognize CI env aliases (#111521) 2026-07-19 12:44:18 -07:00
Peter SteinbergerandGitHub 06480d767c feat(dashboard): stitch session dashboards end-to-end — live provider, show_widget pin, board commands (#111218)
* feat(dashboard): pin canvas widgets through board domain

* feat(control-ui): wire session dashboards end to end

* fix(control-ui): preserve direct reset confirmation

* fix(control-ui): localize dashboard default tab

* test(dashboard): satisfy changed typecheck gate

* fix(dashboard): satisfy changed lint gate

* fix(dashboard): harden pinned widget lifecycle

* fix(dashboard): use extracted mock normalizer

* test(dashboard): reconcile hardened board admission tests

* fix(dashboard): align grant and canvas protocol requests

* fix(dashboard): preserve refresh and pin invariants

* fix(boards): treat missing board tables as empty on read-only paths

* fix(boards): accept read-only handles in board table probe

* fix(control-ui): preserve virtual board widget types

* fix(dashboard): align view types with protocol

* fix(dashboard): keep resolved board view typed

* chore(canvas): drop test-only html read wrapper

* chore(release): defer dashboard note generation

* fix(protocol): align board widget put params type

* fix(control-ui): preserve queued reset approval

* fix(control-ui): gate dashboard pinning capability

* chore(test): isolate dashboard capability fixture

* fix(control-ui): refresh dashboard pin affordance
2026-07-19 12:36:54 -07:00
Peter SteinbergerandGitHub 431da14410 fix(plugins): load standalone files without manifests (#111515) 2026-07-19 12:21:11 -07:00
Peter SteinbergerandGitHub 9c12542946 fix(gateway): reload when included config changes (#111511)
* fix(gateway): watch included config files

* fix(config): keep rejected includes watchable

* fix(gateway): bound rejected include watches
2026-07-19 12:17:02 -07:00
Peter SteinbergerandGitHub 5ec3b2dab4 fix(install): isolate source postinstall state (#111514) 2026-07-19 12:14:21 -07:00
Peter SteinbergerandGitHub 1ddb6f1bbb fix(system-agent): preserve setup success on audit failure (#111504) 2026-07-19 12:06:59 -07:00
Peter SteinbergerandGitHub 31e52dc5c5 feat(gateway): allow explicit operator.admin in device auto-approval with critical audit finding (#111509) 2026-07-19 12:05:04 -07:00
Peter SteinbergerandGitHub 5a6a19c133 fix(qa): align runner inbound dispatch contract (#111507) 2026-07-19 11:51:24 -07:00
Peter SteinbergerandGitHub 163a6c6e16 fix(agents): declare visible-session constraints upfront in sessions_spawn (#111502) 2026-07-19 11:42:05 -07:00
Peter SteinbergerandGitHub 3e154b6761 fix(agents): let swarm collector fan-outs honor group caps (#111505)
* refactor(agents): single admission owner per child mode (#111401)

* refactor(agents): branch admission gather per mode — no cross-mode fallback pairing
2026-07-19 11:40:55 -07:00
Peter SteinbergerandGitHub c69d6e148e feat(ui): who's-online roster menu on the footer facepile (#111501)
* feat(ui): who's-online roster menu on the footer facepile

Clicking the sidebar footer facepile now opens a scrollable roster of
everyone online — avatar, name, and email subtitle per person, with your
own entry pinned first and marked (you). Uses the stock menu-surface +
wa-dropdown idiom; long rosters scroll inside the popup via the dropdown's
menu part. Session-row facepiles stay non-interactive so row clicks keep
navigating.

* fix(ui): roster menu interaction hardening from review

- close explicitly on wa-select (preventDefault also cancels the
  dropdown's own select-and-hide)
- clear the open state when a presence update unmounts the footer
  facepile, so the menu cannot remount stale when presence returns
- restore focus to the visible facepile button on keyboard dismissal
  (the dropdown's own trigger is a hidden throwaway anchor)

* fix(ui): return focus to the facepile trigger on roster row selection
2026-07-19 11:34:17 -07:00
Peter SteinbergerandGitHub 40d31f3481 feat(control-ui): show relative commit age and hover-only copy button on About (#111495)
The About page commit row now embeds the commit's committer timestamp
(commitAt, advisory like branch/dirty) and renders it as an always-relative
age with the exact localized date-time on hover. The copy button becomes a
compact borderless icon revealed on hover/focus only where hover exists;
touch keeps it visible.
2026-07-19 11:13:25 -07:00
Peter SteinbergerandGitHub 6d4082ef97 fix(ui): prevent unsafe custodian nudge answers (#111430)
* fix(ui): harden custodian nudge actions

* refactor(ui): split custodian page helpers

* fix(ui): classify custodian nudge delivery

* fix(ui): ignore stale custodian question replies

* fix(ui): restore rejected custodian questions

* fix(ui): preserve custodian wizard uncertainty

* refactor(ui): keep nudge text helper private

* test(ui): model uncertain custodian delivery

* fix(ui): scope delayed custodian dismissals

* fix(ui): retain in-flight custodian nudges

* fix(ui): block nudges during hosted wizard input

* chore(protocol): regenerate gateway models
2026-07-19 11:10:41 -07:00
Peter SteinbergerandGitHub b9cf1add0f fix(qa): retry transient history rebuilds (#111475)
* fix(qa): retry transient history rebuilds

* fix(qa): preserve history retry timeout cause

* test(qa): avoid retry timeout shadowing
2026-07-19 11:10:15 -07:00
Peter SteinbergerandGitHub c7e7ac2728 refactor: remove expired plugin compatibility surfaces (#111451)
* docs(secrets): remove retired web credential paths

* refactor(web): remove retired provider compatibility paths

* refactor(providers): delete retired compatibility routes

* refactor(secrets): remove retired credential aliases

* refactor(plugin-sdk): delete retired compatibility surfaces

* docs(plugin-sdk): remove retired migration guidance

* chore(plugin-sdk): refresh rebased surface budgets

* chore(plugin-sdk): refresh API removal baseline

* refactor(compat): migrate retired internal callers

* chore(plugin-sdk): refresh current-main baselines

* test(config): migrate plugin-owned secret assertions

* test(gateway): narrow plugin secret refs

* fix(plugin-sdk): preserve private boundary type identity

* chore(compat): remove stale sweep references

* chore(lint): lower max-lines budget

* refactor(secrets): remove unused web helper

* build(plugin-sdk): drop removed compat entries

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): use Linux API baseline hash

* fix(plugin-sdk): preserve private bundled build entries

* fix(plugin-sdk): package private runtime facades

* fix(plugins): preserve external credential contracts
2026-07-19 11:04:48 -07:00
Peter SteinbergerandGitHub e6d7aee41d fix(ui): stop chat normalizers throwing on null/undefined message entries (#111494)
normalizeMessage / isToolResultMessage / isStandaloneToolMessageForDisplay
cast their unknown input to a record and then read `typeof m.role`. That
still evaluates `m.role`, so an undefined entry (e.g. groupMessages calling
normalizeMessage(item.message) with an absent message on a pending or
malformed transcript row) threw "Cannot read properties of undefined
(reading 'role')" inside the gateway event handler — a caught but recurring
error storm on the live team instance. Coerce non-object input to {} at the
source so every downstream `typeof m.<field>` check works and role degrades
to "unknown". The existing safeNormalizeMessage try/catch wrapper existed
precisely for this hazard; the guard now lives in normalizeMessage itself.
2026-07-19 10:54:55 -07:00
50ce5dedea fix(android): improve Wear thread follow behavior (#111459)
* improve(android): move Wear agent, session, and model pickers to Home

* fix(android): improve Wear thread follow behavior

* fix(android): stabilize Wear thread follow lifecycle

* fix(android): keep streamed Wear tail visible

Co-authored-by: JARVIS-Glasses <whatsskilll@gmail.com>

---------

Co-authored-by: Colin <colin@solvely.net>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-19 10:54:10 -07:00
ShakkerandShakker 8e0c50380c fix: preserve message-only terminal output (#111493) 2026-07-19 18:51:06 +01:00
Peter SteinbergerandGitHub 83303f1ba8 feat(channels): batch 1 producers drop media placeholder bodies (#111447)
* feat(channels): batch 1 producers drop media placeholder bodies

Media-placeholder program batch 1: Google Chat, Zalo, LINE, and
Mattermost stop minting <media:kind> placeholder bodies. Media-only
messages carry an empty caption plus one structured fact per native
attachment (type-only when a download fails or is rejected, so payload
positions and kind signals stay aligned). The shared
formatMediaPlaceholderText SDK formatter renders text-only carriers
(Mattermost pending-room lines) from structured facts; per-channel
placeholder builders and the expected-count side channel are deleted.

* fix(mattermost): satisfy type, deadcode, and SDK manifest gates
2026-07-19 10:32:09 -07:00
Peter SteinbergerandGitHub 4c55a4fc28 feat(plugins): expose requester context to tool hooks (#111190)
* feat(plugins): expose requester context to tool hooks

* chore(plugin-sdk): restore generated API baseline

* chore: drop release-owned changelog entry

* docs: refresh documentation map
2026-07-19 10:31:58 -07:00
Masato HoshinoandGitHub 6fb3aae404 fix(gateway): stop the unmanaged gateway named by its lock (#111378)
`openclaw gateway stop` resolved the unmanaged fallback port from config and
discovered pids through lsof only. On hosts without lsof, and whenever the
gateway runs on a port other than the configured one, discovery came back empty
and the command reported `Gateway service disabled.` with `ok:true` and exit 0
while the gateway kept serving.

The gateway lock already holds the verified owner pid and port. Restart learned
to read the lock port in #105241 to keep an unmanaged restart honest when the
configured port drifts; stop's fallback never did. Read the lock identity once in
the not-loaded fallback, use its port for discovery, and signal its owner when no
listener is found. Verified listeners still win when lsof is available.

Signalling still goes through `signalVerifiedGatewayPidSync`, which re-reads argv
immediately before SIGTERM, and lock identities are only returned after a
liveness and start-time or argv check, so dead, recycled, port-less and
non-gateway lock owners are refused and the command stays `not-loaded`.

Closes #72948
2026-07-19 10:20:31 -07:00
Peter SteinbergerandGitHub d4c7549d1c test(nextcloud-talk): stabilize room timeout proof (#111490) 2026-07-19 10:08:45 -07:00
ShakkerandShakker 0db0142ec8 fix: preserve streamed text before terminal errors (#108509) 2026-07-19 18:07:12 +01:00
ShakkerandShakker 1e80a0ee88 chore: format chat run error changes 2026-07-19 18:07:12 +01:00
ShakkerandShakker 9dd021a5cc fix: align run error on mobile 2026-07-19 18:07:12 +01:00
ShakkerandShakker 0d511d1a67 chore: remove unrelated run failure changes 2026-07-19 18:07:12 +01:00
ShakkerandShakker d2a4025d1f test: cover chat run error recovery 2026-07-19 18:07:12 +01:00
ShakkerandShakker 25b1c6c400 fix: show run failures above the composer 2026-07-19 18:07:12 +01:00
ShakkerandShakker 9a699c7037 fix: keep chat errors out of assistant messages 2026-07-19 18:07:12 +01:00
vyctorbrzezowskiandShakker 86b1961bcc fix: show run failures above composer 2026-07-19 18:07:12 +01:00
mushuiyu886andGitHub 34978b5a07 fix(browser): report final URL when opening managed Chrome tabs (#111374) 2026-07-19 10:01:42 -07:00