* feat(linux): add Quick Chat floating composer to the Tauri companion
- frameless transparent always-on-top quickchat window (on-demand, canvas
pattern), positioned top-third on the cursor monitor, CSS fade+zoom in/out
- global shortcut Ctrl+Shift+Space on X11 (GNOME reserves Alt+Space); tray
'Quick Chat' item is the Wayland entry point; blur/Esc/close-request hide
- sends via the CLI agent turn with --message-file staged 0600 (argv is
world-readable procfs) and cleaned up after the turn; failures surface as
desktop notifications; Ctrl+Enter also opens the dashboard
- default-agent identity chip (emoji/monogram) from agents list --json, 60s cache
- window-scoped capability + permission set; window-state denylist; docs
* chore(linux): register quickchat.js as a knip native-asset entry
normalizeQaLiveProviderConfig injected baseUrl:"" when the host provider omitted baseUrl, failing the child gateway's config schema (baseUrl >=1 char) so every live-frontier scenario failed to start. Omit missing/empty/whitespace baseUrl so the provider default applies; valid URLs preserved.
* fix(compaction): stop duplicating the previous summary when a later split degrades
summarizeInStages stamped a merged result as generic-fallback whenever ANY
split degraded. compaction-safeguard reads that kind to decide whether to
restore the previous summary, on the documented assumption that "a generic
fallback means redistillation never happened".
That assumption only holds for chunk 0. summarizeViaLLM prepends the previous
summary as the first message, so it always lands in the oldest chunk. When
that chunk summarizes fine but a later one degrades, the merge already carries
the redistilled summary — and the safeguard prepends it a second time. Every
subsequent compaction that has any degraded split re-adds it again, so a PR
that exists to stop token burn compounds it instead.
The kind now reports whether the oldest split degraded, which is exactly the
question the only consumer asks. The coarse "any chunk" flag is gone rather
than kept alongside the precise one, so there is a single source of truth.
Behavior for a degraded oldest split is unchanged: the previous summary is
genuinely lost there and restoring it is still correct.
* test(compaction): pin previous-summary restoration boundary
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agents): keep Azure Responses model aliases routed
* Fix Azure Responses alias owner fallback
* Fix provider-level Azure alias owner
* Fix Azure custom deployment alias ownership
* Fix Azure alias endpoint ownership
* fix: remove redundant static catalog boolean wrapper
* fix(azure): preserve alias tests after main sync
* fix(azure): infer transport from retained alias
* fix(azure): derive transport from manifest alias
* chore: drop local CodeGraph metadata
* fix(azure): keep alias transport ownership consistent
* fix(azure): preserve alias ownership across fallbacks
* fix(agents): keep codex-only model suppression authoritative
* refactor(agents): split model compatibility helpers
* fix(agents): preserve conditional suppression ownership
* test(agents): deduplicate alias resolution assertions
* fix(agents): reject ambiguous model aliases
* test(agents): keep alias regressions within lint caps
* fix(agents): preserve alias target transport
* fix(agents): honor configured alias transports
* fix(agents): preserve retained alias transport
* fix(agents): narrow alias transport precedence
* test(agents): complete alias transport fixture
* test(agents): trim redundant alias fixture fields
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(auto-reply): honor abortSignal in transient-HTTP retry backoff
The transient-HTTP retry in handleAgentExecutionError slept its full
TRANSIENT_HTTP_RETRY_DELAY_MS (2500ms) on a bare setTimeout, ignoring the
turn abortSignal. A cancelled turn stayed parked for the whole backoff and
then returned { kind: "retry" }, issuing another provider attempt after
cancellation.
Mirror the sibling overload-retry branch (abort-aware since #109354):
sleep via sleepWithAbort with turn.replyOperation?.abortSignal ??
turn.opts?.abortSignal and map a sleep rejection through
resolveReplyOperationAbortAction, returning the abort action when set.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(auto-reply): share abortable retry backoff
* fix(auto-reply): make retry wait fallthrough explicit
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(chat): replay the active plan snapshot to reconnecting clients
Plan checklists were live-event-only: a client that connected or
refreshed mid-run saw nothing until the next update_plan call, and iOS
adoptRun actively wiped retained plan state on reconnect. The gateway
now records the latest normalized plan snapshot per run beside the
existing chat text buffers and replays it through the established
chat.history/chat.startup inFlightRun recovery contract (runId > plan >
text under the payload budget; a budget-dropped plan is sent as an
explicit empty snapshot so clients never preserve a stale checklist).
Web, iOS, and Android seed and reconcile the snapshot through one shared
contract riding each client's existing inFlightRun adoption gate: present
plan replaces, absent plan preserves (legacy gateway), a different or
terminated run clears, and stale history responses cannot clobber a newer
live plan. Verified live gap: openclaw/openclaw#108675 comment 5001475066.
* fix(ios): extract run-snapshot adoption below the file-length lint ceiling
ChatViewModel.swift crossed SwiftLint's 1500-line limit; the in-flight
snapshot adoption and plan reconciliation move to a focused extension.
Also refresh the native i18n inventory for shifted line anchors.
* fixup: regenerate i18n anchors and test formatting after rebase
* ci: retrigger after wedged queued runs
* fix(ui): use Object.hasOwn for the plan field presence probe
* fix(ios): annotate the plan-snapshot fixture init for periphery
* feat(clients): session detail parity — row actions, unread auto-clear, and subagent tree in the native chat sidebar
* fix(clients): overlay pending explicit unread values over stale session snapshots
* chore(i18n): translate new session action strings and regenerate Apple/Android catalogs
* chore(clients): delete dead pin helper and worktree memberwise init
* feat(macos): honor the selected microphone in Talk and surface live level, transcript, and mic picker in chat
* chore(i18n): translate new talk activity strings and regenerate Apple/Android catalogs
* feat(onboarding): recommend plugins and skills from installed apps
Scan installed macOS apps during classic onboarding (TCC-free), gather
candidates from official catalogs + ClawHub search, let the configured
model pick genuine matches, and offer an opt-in multiselect install step.
Adds a device.apps node-host command (default-off sharing, Android-parity
envelope) so remote gateways can request a paired Mac's inventory, and a
wizard.appRecommendations kill switch. Custom setup-inference completions
no longer inherit the 32-token verification-probe output cap.
* feat(onboarding): recommend apps in guided flow
* fix(onboarding): harden app recommendations against ClawHub self-promotion
Third-party ClawHub skills are never pre-selected regardless of model tier
(publisher-controlled listing text reaches the matcher prompt and could
promote itself); their labels now say they install third-party code.
Installed-app scans follow symlinked .app bundles. Matcher output stays
bounded by the resolved model's own maxTokens budget (documented invariant).
* fix(onboarding): key official catalog candidates by resolved plugin id
Real catalog entries are package manifests without a top-level id; keying the
candidate map and channel/provider classification by entry.id collapsed the
whole official catalog into one undefined-keyed entry, so no official plugin
or channel was ever recommended. Regression test runs against the bundled
catalogs.
* fix(onboarding): satisfy lint, types, deadcode, and migration gates
Split the guided-onboarding test into a self-contained custodian suite to stay
under max-lines. Narrow app-recommendation exports (drop dead node-payload
normalizer, unexport internal types/helpers, route candidate tests through the
public API), replace map-spread with a helper, unexport device.apps result
types, add installedAppsSharing to node-host migration expectations, cast the
wizard multiselect mock, and regenerate the docs map.
* test(onboarding): register new live test in the shard classifier