Closes#159542
## What Problem This Solves
Fixes: the PDF tool fails with `No PDF model configured.` when an authenticated, vision-capable active model is available through OpenRouter but no PDF or image model is configured.
## User Impact
User impact: an agent can analyze text-layer and scanned PDFs with its active vision model without duplicating that model in `pdfModel`. Explicit PDF/image preferences, existing native-provider precedence, authentication, and provider PDF image restrictions remain intact.
## Why This Change Was Made
Pass the admitted active model to deferred PDF resolution and use it only after existing candidates are exhausted. The fallback requires image support, provider authentication, and no `documentModels.pdf.image: false` restriction. The PDF guide now describes this final fallback and deferred resolution accurately.
No overlap with Pash/Sarah changes.
## Evidence
Real isolated Gateway runs used a scripted, authenticated OpenAI-compatible endpoint standing in for OpenRouter; no paid provider was contacted. The agent searched for and called the actual `pdf` tool on two real PDF files. Neither `pdfModel` nor `imageModel` was set for the before/after runs.
| Case | Fix removed | This change |
| --- | --- | --- |
| Text-layer PDF | `No PDF model configured.` | `Extracted PDF marker ORCHID-159542.` |
| Image-only PDF | `No PDF model configured.` | `Received 1 rendered PDF page image(s).` |
Provider request captures confirm that the text marker came from extracted PDF text and that the scanned document produced an `image_url` PNG part. These are deterministic transport/extraction checks, not a claim about a paid model's reasoning quality.
The before run used the same candidate tree with only the three changed production files restored to main `f0b149f44df9a47ea6822bec2b7b438a938ec54a`. Both trees were rebuilt and launched through `pnpm openclaw`. Plain main builds, including a newer green-CI main revision with identical PDF owners, were blocked by the unrelated `transcript-visible-record.ts` `INEFFECTIVE_DYNAMIC_IMPORT` build guard; the fix-removed comparison isolates this PDF change without modifying that guard.
Controls:
- Gateway: explicit `pdfModel: openrouter/openai/gpt-4o` wins over the active `openrouter/anthropic/claude-sonnet-4.5` model.
- Gateway: a text-only active `openrouter/openai/gpt-3.5-turbo` is rejected with `No PDF model configured.` for the image-only PDF.
- Gateway: explicit Anthropic native PDF selection returns `native: true`; the local endpoint receives the original base64 PDF document bytes, not extracted images.
- Manifest-backed resolver regression: an authenticated vision provider declaring `documentModels.pdf.image: false` is not selected. The same regression fails against the original contributor resolver (`restricted/vision` selected instead of `null`) and passes with the guard.
- Existing tests retain missing-auth and native-candidate precedence controls.
Focused checks: 79 tests passed across the PDF model, PDF tool, and production assembly suites (37.92 seconds wall time, one worker). `pnpm tsgo:core`, `pnpm tsgo:core:test`, scoped Oxlint, formatting, and `git diff --check` passed. Removed redundant forwarding assertions while retaining the observable assembly regression.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Add opt-in major and minor GC collection flags to heap sampling RPC parameters and result metadata. Preserve default retained-allocation behavior, sampling bounds, and frame redaction. Verify collected-allocation attribution with a 200 MiB native V8 workload.
The pending-work guard dropped new scheduling requests while another
session's cooldown retry could hold a timer for almost 30 seconds. Keep
the earliest pending deadline, preserve the minimum running-cycle delay,
and carry intent through coalescing so it bypasses unbounded idle waits.
Use a 75 ms intent delay to coalesce row sweeps before a click; automatic
warming retains its 250 ms delay and idle callback. Preserve per-key
cooldown, serial history fetches, Web Locks, visibility, presented-pane
readiness, cache ownership, counts, and request budgets.
Controller-boundary regressions fail on base 6652f7eac8 for both intent
and list revisions arriving behind a cooldown. Also cover later requests
not postponing automatic warming, held idle callbacks, pointer sweeps,
and intent coalesced behind an in-flight history request. Document the
intent behavior in the Control UI reference.
Validation: 308 tests across 17 prefetch, chat-history, and session-snapshot
files; UI typecheck; CI oxlint wrapper on touched TypeScript; UI build;
format and diff checks; independent P2 review with no actionable findings.
The broader changed check found TS2532 in the new idle-callback test;
fixed its indexed access and reran the ui-chat type graph, all 31 prefetch
tests, touched-file lint, and review successfully. The other five selected
test type graphs and all preceding changed-file guards passed unchanged.
The prefetch file passed 31 tests in 13.35 s single-worker wall time
(about 0.35 s test time). No remote CI was run.
Live proof: three interleaved before/after rounds per dwell, four targets
per round (12 samples per variant/dwell), separate synthetic Gateways.
Medians from the requested probe, before -> after:
- 150 ms dwell: first completed prefetch send unavailable -> 89.5 ms
(0/12 -> 2/12 observable samples); click-to-visible 123 -> 205 ms.
- 300 ms dwell: first completed prefetch send 235 -> 88 ms
(12/12 -> 5/12 observable samples); click-to-visible 111 -> 116.5 ms.
The supplied probe records requests only on response, hiding pending sends.
One additional interleaved round with send-time instrumentation measured
87.5 ms (150 ms dwell) and 85 ms (300 ms dwell) after, including requests
whose responses took seconds. The next supplemental round hit repeated
Gateway authentication timeouts and multi-second SQLite writes; stopped
that probe. Earlier scheduling is proven; end-to-end click improvement
under this host load is not. Stopped the isolated Gateway and removed
its seeded state and temporary browser profiles.
* feat(ui): navigate expanded videos within a chat turn
Consolidate the already-reviewed gallery implementation and test-audit corrections before rebasing onto current main. The original published history is retained on openclaw/video-gallery-pre-rebase-20260926.
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
OpenClaw-Publication: bddde60e-b0c9-4628-b0ec-5ac069ef912f
* refactor(ui): trim rebased video gallery integration
Keep playback cases in their original grouped-message fixture, match their observable payload, and consolidate gallery status and unfiltered membership at their existing owners. Preserve upstream image keyboard panning, lazy video viewport loading, retry and filename fixes. No merge is authorized.
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
* feat(ui): navigate expanded videos within a chat turn
Worked on by:
- @vyctorbrzezowski
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
OpenClaw-Publication: 7aeaf603-c2a8-4b4a-976c-b713f6cd6198
* fix(ui): keep expanded videos clear of viewer controls
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
---------
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
Lost CI-completion deliveries can leave the required ci-gate status pending after CI finishes. Add scheduled reconciliation through the existing trusted Security Review resolver and enforcement job.
Preserve overlapping completion windows, reject incomplete listings before publication, keep provisional statuses eligible, and trust only Actions-owned gate statuses. Bound each pass to the oldest 100 heads and retain the successful window anchor while a backlog remains.
Reruns whose original creation time predates the three-hour listing margin retain the documented existing recovery path through a push or Security Review rerun.
Validation: exact-head CI passed, focused entry-point and workflow regressions passed, and live read-only GitHub transport proof completed. Production scheduled recovery remains post-merge proof.
Avoid main-only refreshes and repeated blocked routes. Add explicit prior-CI
admin admission for reviewed conflict repairs, pinned to current preparation,
verified earlier CI and current review/security requirements. Preserve honest
operator validation evidence in the native retained merge outcome and dispatch
through the protected SHA-pinned REST squash endpoint.
Pass complete CI changed-path manifests through files so large PRs retain
Node planning inputs beyond the 64 KiB Actions output limit. Keep frozen-target
compatibility and reject missing or malformed current manifests.
Validation: reproduced the historical oversized-manifest failure; 570 CI scope
and workflow cases pass (241.60s wall). Fifteen native admission/dispatch cases
pass (45.90s wall), four existing admission/recovery cases pass (52.48s), and
wrapper inventory passes (21.89s). New manifest cases add subsecond test time;
native process cases cover actual pinned dispatch, authority and receipt gates.
Type checks, typed lint, workflow validation, formatting and source ratchets pass.
Review corrections cover absent or wrong-publisher security checks and ref-suffixed
workflow paths; all three fail against the previous predicates.
WKWebView starts a Screen Time configuration observer once it is in a
window with an HTTP(S) main frame. ScreenTime delivers configuration via
KVO on WebKit's private update queue while -[WKWebView dealloc] removes
the observer on the main thread, so tearing a web view down during its
initial reply throws NSInternalInconsistencyException and aborts the
Swift Testing process (macos-swift run 36289828982, job 108537817128).
Link a test-only OpenClawWebKitTestSupport target into OpenClawIPCTests
that replaces WebKit's Screen Time install hook with a recorder, add a
regression test for windowed HTTP web views, and document the guard.
Product builds keep Screen Time.
Use the modern host context composer and CPU-tracked worker factory when available. Preserve the published 2026.9.6 profile loader and native worker lifecycle only when those capabilities are absent, without masking modern failures. Document the context difference and cover the missing-capability regressions.
* fix(pairing): honor gateway.publicOrigin for device join codes
A loopback Gateway behind public HTTPS ingress sets gateway.publicOrigin, and
cloud node enrollment already used it, but device join codes, the device-pair
plugin, and Doctor's node-hosting check each resolved the pairing endpoint on
their own and ignored it, failing with advice that omitted publicOrigin.
Make src/pairing/setup-code.ts the single owner (device-pair publicUrl
override, then gateway.publicOrigin, then existing discovery), expose it to
the device-pair plugin through plugin-sdk/device-bootstrap, remove the
plugin's duplicate resolver and enrollment's private fallback, and share one
loopback error that names gateway.publicOrigin.
* fix(pairing): preserve explicit remote endpoint selection
Honor preferRemoteUrl before gateway.publicOrigin so qr --remote keeps
its selected endpoint aligned with remote credentials. Retain publicOrigin
as this Gateway's ingress ahead of automatic Tailscale, remote, and bind
discovery, with the pairing-specific override taking precedence.
Cover configurations with both URLs at the resolver and QR CLI boundaries,
and correct the CLI, cloud-worker, and SDK precedence documentation.
Validation: 221 focused tests passed; scoped core, extension, script, and
test typechecks passed; independent review found no actionable P0-P2 issues.
* fix(pairing): preserve device routes with explicit cloud ingress preference
Keep existing device join-code, QR, and /pair discovery order. Use
publicOrigin only at the loopback fallback, while cloud enrollment asks the
same resolver to prefer public ingress at both preparation and issuance.
Use the canonical lazy runtime binder for the SDK export so the collision
guard recognizes one implementation without loading setup code at startup.
Document the two call-site intents and cover Tailscale, LAN, loopback, and
cloud enrollment behavior.
Validation: export-name collision and SDK surface guards passed; 222
focused tests and scoped typechecks passed; independent review was clean.
* chore(device-pair): drop the max-lines suppression the smaller plugin no longer needs
* fix(device-pair): preserve origin-only setup URLs
Retain the plugin command URL mapping through the shared pairing resolver while preserving context paths for join codes and cloud enrollment. Clarify public-origin fallback and remote QR prerequisites.
* feat(macos): renew Cloudflare Access browser sign-in automatically
Saved Gateways behind Cloudflare Access stored an app-scoped Access token
that hard-expired, after which the dashboard showed the blocking signed-out
page. Nothing renewed it ahead of time.
While the user is present and a saved Gateway is in use, the app now runs
the existing browser sign-in in the background during the last quarter of
the token lifetime (15 minutes to 7 days), at most once per profile per day.
A same-account renewal of a still-live session keeps the dashboard document
and route and quietly reconnects the native socket; expired sessions and
account changes keep the full sign-in path. A user-initiated sign-in cancels
and joins an in-flight automatic attempt first.
* refactor(macos): keep renewal status out of native Gateway settings
* refactor(macos): remove dead command, approval, tunnel, chat, and permission paths
Deletes production code with no remaining callers, found with Periphery and
confirmed by repository search and a clean build: the retired SSH node
command builder in CommandResolver, the unused outbound approval-policy
snapshot conversion, ExecApprovals.parseDecision, the port-only
RemoteTunnelManager.ensureControlTunnel wrapper, the old cron transcript
opener in WebChatManager, and the computer-control permission diagnostic
renderer with its two localization inventory entries. Tests that only
exercised the removed code are removed or pointed at the live owners.
* fix(macos): let each renewed Access token renew inside its own window
The automatic attempt throttle was one per profile per day, so a 24-hour token renewed at hour 18 blocked the renewed token's own window until it had already expired. Attempts are now keyed to the session token; retries of one token wait half its renewal window, at most a day.
* fix(macos): check Access renewal often enough for 15-minute windows
The periodic check slept an hour, so a short or legacy session's 15-minute renewal window could pass unchecked while the app stayed active, and returning from idle triggered no check. Check every five minutes instead.
* fix(macos): keep the Gateway socket when an Access renewal cannot be saved
A same-account renewal disconnected the native socket before device-token retirement and the Keychain save, so a failure there left chat and push offline although the old session stayed valid. Renewals now disconnect only after the save; browser-session sockets never use or persist device tokens, so the old socket can stay up until then.
* fix(macos): never switch accounts during automatic Access renewal
An automatic renewal whose browser returned a different Access subject was saved and took the account-change path, retiring the old account's windows and credentials. Automatic renewals now commit only a same-account renewal of a still-live session and are rejected before any side effect otherwise. The renewal window reuses the existing Access JWT decoder.
* feat(gateway): report an hourly activity pulse from sessions.list
Aggregate filtered session activity before pagination, using the caller’s local midnight and the canonical activity timestamp. Report starts, current runs within the window, and distinct associated people without changing candidate membership or sessions.search.
* feat(ui): redesign the Activity sessions feed
Show a local-day activity pulse above Sessions, highlight complete navigable rows, and present recap and image status as quiet text links. Move truncated counts below the feed and partial-history context into the people popover.
* fix(activity): count every live session as running now and keep the oversized-image notice
* fix(activity): keep the oversized-image notice next to a preview error
* chore(protocol): regenerate Swift gateway models for activityPulseSince
* fix(activity): bound the pulse to the viewer's civil day, refresh at midnight, and qualify incomplete people counts
* refactor(gateway): resolve the activity pulse window in a helper so narrowing holds
* test(activity): anchor layout-stability checks on the pulse card and the renamed note class
* fix(ui): keep the shared showing count string unchanged for the Debug overlay
Keep cron and system conversations out of ordinary Android chat navigation while preserving saved pins and selected-session access. Reuse Gateway creation facts and the Web visibility policy, and expose retained automation conversations through Threads → Automations and the existing transcript-opening route.
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
* feat(ui): add a native conversation surface for macOS
Keep the web chat header, transcript, composer, side panels, and overlays in one embedded pane while native windows own surrounding navigation. Add a lazy document-bound conversation bridge with state publication, in-page session navigation, Dashboard handoff, presentation, and composer focus.
Preserve existing browser and iOS settings embeds. Validate navigation completion, superseding web navigation, hidden-window route ownership, and agent-scoped state with unit and mocked-Gateway browser coverage. Raise only the paired startup JS baseline/cap by the authorized measured 249 bytes; retain all other budgets and allowances.
* fix(ui): preserve native conversation reply and presentation ownership
Echo each command result with its originating document ID, including stale-document rejections. Suspend retained panes and clear drag state only on effective presented-to-hidden transitions. Restore the normal 12px leading header inset through the lazy native embed stylesheet.
Cover same-requestId commands across documents, repeated hidden updates for web and native presentation, and matching browser/native header geometry. Keep all performance budgets unchanged.
* fix(ui): separate the native conversation bridge contract
* fix(ui): preserve native conversation interactions and bounded navigation
Preserve transcript file, session, and panel link ownership. Show a translated toast when Dashboard handoff fails, and keep visible inactive native windows presented. Bound host navigation by a 15-second receipt deadline, fence expired continuations, and allow an explicit navigation to recover a failed state acknowledgement.
Validation: pre-fix regression failures; 82 focused tests, 3 mocked-Gateway E2E cases, 52 performance tests, UI lint/typecheck/i18n, import-cycle checks, production build at 371541 bytes under the unchanged 371581-byte startup cap, and scoped-clean independent review.
* fix: keep gateway startup retryable after failed reads
* fix: retain terminal agent ownership refusals
* fix: preserve confirmed startup refusal causes
* fix(gateway): preserve startup refusal evidence across reads
Keep unavailable config, backup and SQLite reads retryable while preserving
confirmed config, schema, ownership and maintenance refusals as exit 78.
Carry failure causes through existing admission owners without changing
public refusal data, persistent schemas or migration policy.
Resolve the main integration and retain its identity-store ownership.
Include exact upstream test-call corrections needed by the current helper API.
* test(gateway): narrow startup failure fixture variants
* test(state): expect typed schema refusal on committed reads
* fix(state): classify expired schema read scopes for actor retirement
Reuse the established read-admission invalidation type when a managed schema scope ends. Preserve original scope rejection and the worker owner’s relocation and active-callback fences while allowing idle old actors to retire for valid replacement callers. Integrate current main and prove ordinary and same-path managed replacements with native workers.
* test(media): use valid PNG in Windows file URL fixture
* chore: reconcile startup read repair with main
Take the upstream schema-scope production correction unchanged. Preserve complementary fresh managed-scope and retained prepared-reader coverage with the upstream typed refusal assertion, and retain the main iterative include scan beside the reviewed config read helpers.
* fix(subagents): preserve resident source admission failures
Use canonical cache absence semantics in both snapshot identity getters. Remove catches that translated expired current read scopes into missing cache facts; preserve full-row publication handling and same-file source renewal. The unchanged scope regression fails before this correction and the registry, lifecycle and projection suites pass afterward.
* Merge main to retain its Windows media fixture repair
* Merge main while preserving its worker-lifecycle reverts
* fix(state): retain complete typed startup refusal reports
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* docs(cloud-workers): profile changes apply without a Gateway restart
* docs(cloud-workers): validate saved profiles and qualify reload mode off
Run config validation after saving, since it reads the saved file. Note that gateway.reload.mode off defers passive profile changes to a manual restart, and drop the guide's remaining restart instruction.
* docs(cloud-workers): name reload mode off in profile troubleshooting
Reload mode off keeps watching config but defers passive profile changes to a manual restart; the troubleshooting entry now says so instead of implying watching is disabled.
* docs(cloud-workers): distinguish UI saves from file edits in reload mode off
In reload mode off, Gateway config writes such as Control UI saves schedule a restart themselves; only direct file edits wait for a manual restart (config-write-flow resolveConfigRestartRequirement).
* fix(ui): stop images reloading while scrolling
Reuse loaded native images within the scoped metadata cache and bypass viewport admission for warm managed previews. Preserve ticket expiry, invalidation, and exclusive node ownership across remounts.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(ui): stop images reloading while scrolling
Worked on by:
- @steipete
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: 60dabc7e-feb1-44b5-84da-0f0d3137e62f
* fix(ui): retire cached images when sharing access changes
Invalidate the existing media policy epoch on redacted sharing notifications before the roster refresh. Prove normal scroll reuse and final denial after visibility, role assignment, and role definition withdrawal in Chromium.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
* fix(ui): stop images reloading while scrolling
Worked on by:
- @steipete
- @vyctorbrzezowski
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
OpenClaw-Publication: 0f6560b1-3b05-40a0-9d04-8e6965c6ea7d
---------
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
Allow saved Android Gateways to have persistent phone-local display names.
Preserve names across metadata updates and document the accepted downgrade limitation.
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Remove Tasks and TaskFlow runtime, APIs, CLI, SDK surfaces and panels after the Cron, session, native execution and media completion ownership cutovers. Preserve stored rows and import provable legacy native assignments through Doctor; ambiguous ownership stays untouched with a warning.
Follows #158221, #158217, #158225, #158222, #158702 and #158776. Related: #156532. Task-specific public APIs retire immediately; retained responsibilities use their existing owners.
Maintainer-authorized administrative landing after full CI run 36312986498 attempt 2 passed on 274595e2, with subsequent actual conflicts reviewed and focused checks passing. Current PR CI preflight hits the 64 KiB changed-path metadata limit before tests (run 36335042695); its duplicate security-review status mirrors that planning failure. Review and scoped proof are recorded in the PR. Published 9.4 native import is proven; remaining native completion and 9.4 rollback witnesses are explicitly unproven.
* fix(agentsapi): apply plugin prompt contributions
* fix(agentsapi): keep prompt hooks advisory for tool restrictions
ClawSweeper request: "Establish an upgrade path for restrictive prompt hooks"
The maintainer chose to continue turns with hook context when Agents API cannot enforce toolsAllow. Preserve configured Gateway tool policies and document the limitation for both new and resumed sessions.
* fix(harness): load bounded history only for prompt hooks
ClawSweeper request: "Skip or bound history reads when no prompt hook runs"
Load Agents API history lazily through the shared hook owner. Reuse the existing Codex byte, event, and tool-result limits without changing Codex history behavior. Cover unavailable history without hooks and admitted bounded history with hooks.
* style(agentsapi): format prompt hook preparation
---------
Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
* fix(agents): recover interrupted children without stale failures
Preserve typed restart interruptions, identify unfinished children in parent recovery input, and prefer retained-session continuation after checking uncertain effects. Keep genuine execution failures and exact-owner recovery delivery exhaustion visible. Cover restart persistence, parent dispatch, projection ownership, and the rendered sidebar.
* fix(agents): bind restart child recovery to its requester store
Resolve physical session storage through its async owner before selecting retained child runs. Cover retired and unknown stores, upgrade reconciliation with typed restart ownership, and unchanged ambiguous history. Align the existing websocket and startup assertions with interrupted session status.
* fix(agents): fence restart recovery to the parent session incarnation
Require the captured requester session identity after selecting the latest child generations. Preserve unscoped run-history queries and prove reset-parent exclusion at dispatch. Strengthen upgrade coverage by reopening SQLite records with and without retained restart ownership.
* fix(agents): retain parent lifecycle ownership through child recovery
Capture the requester lifecycle revision in every spawn path and use it for actionable restart rosters. Prove the real Gateway reset boundary at an HTTP model provider: reset keeps the session ID, current work is delivered, and pre-reset work is excluded while diagnostic history remains available.
* fix(agents): scope settled recovery guidance to parent lifecycle
* test: verify saved batch recovery at the HTTP provider boundary
Reuse the real Gateway and Responses fixture for current, reset-revoked, and restored-history saved batches. Replace the corrupt Windows media-tool PNG with the canonical valid image fixture while preserving all assertions.
Distinguish invalid timezones from invalid datetimes at the zoned parser owner, and preserve the scheduler caller's existing failure behavior. Share timezone validation across cron add/edit schedules so valid timestamps are no longer blamed for invalid --tz input.
Add registered add/edit regressions and retain invalid-calendar and DST-gap controls. Blacksmith proof captured seven failing CLI cases before the fix, 79 passing CLI tests and 75 passing parser tests after it, 624 passing focused tests, and a passing check-changed gate. No local test, build, install, or formatting commands were run.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Improve Android model search with conservative typo tolerance and multi-term ranking while preserving provider groups, selection, and availability.
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Use the shared CLI failure envelope for config unset and patch usage errors when JSON output was requested. Preserve config set's parse-only JSON alias and existing dry-run results.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix: show skipped tools separately from blocked activity
* fix(sdk): preserve non-success events for skipped tools
* fix(ci): preboot simulators before SimSlim reconfiguration
* fix(android): fold late skipped results into completed work
* fix(sessions): fail tail for missing explicit keys
Report an actionable error and exit nonzero when sessions tail cannot find
an explicitly selected session. Keep an empty implicit selection a
successful no-op, including follow mode.
Add command-owner regressions for explicit missing keys with and without
follow, plus preservation of the implicit empty-selection behavior.
Document the distinction in the CLI guide.
Blacksmith Testbox proof: the unchanged owner fails exactly the two new
missing-key assertions (26 pass); the fixed owner passes all 28 tests and
the registered CLI sibling passes all 53 tests. Independent review is clean.
Changed checks reach the unrelated provider-lifecycle.ts TS2322 already
fixed upstream by a241b3bf6e.
* fix(cli): reject blank session keys in sessions tail
* test(sessions): avoid shadowed session key in tail regression
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* perf(sessions): keep member evidence off the transcript queue
Use the existing projection read lane for indexed membership evidence so full transcript hydration cannot delay member lists. Preserve worker custody, current authority checks, and incognito ownership. Add content-free collaboration wait phases for profile, evidence, projection, and discussion provider reads.
Testbox fixture: evidence behind four full history reads improved from 190.57 ms to 4.40 ms mean. The deterministic queue regression fails with the old routing and passes with the candidate. Focused sharing, discussion, mutation, and worker lifecycle proof passed, as did pinned-base changed checks and independent review.
* fix(tasks): keep reconciled task snapshots readable
An overlapping publication could leave an orphan dirty scope even when its full resident task and delivery facts matched canonical readback. Retained readers then refused native subagent recovery after all mutations had settled. Preserve the existing witness and receipt fences, but retain refresh debt only for actual canonical differences.
The retained-reader regression fails on the old no-op refresh and row ABA paths, while changed delivery metadata remains a negative control. Testbox proof passed 148 task-owner and sibling tests, 20 standalone runs of rotated-parent recovery, and three 11-file shard replays (326 passed, one skipped each). Core and scoped test typechecks, lint, formatting, and independent review passed.
* improve(android): put Settings beside the gateway selector
Default to Home, Threads, Skills, and Overview while preserving personalized pins and the existing navigation owner.
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
* test(android): make sidebar drag fixtures independent of defaults
Pin Settings explicitly for fold gesture scenarios and configure the catalog drag order. Use the footer gear for Settings navigation without changing production code or assertions.
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
---------
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
The reuse guard added in e52420e1ee validates a cached worker's original
read admission. Ending a managed existing-schema scope threw a plain Error,
so the guard propagated it instead of retiring an idle worker before an
ordinary open on the same database.
Classify ended schema scopes as StateDatabaseReadAdmissionInvalidatedError
at the schema-policy owner, retaining the diagnostic message. The existing
guard now joins idle actor retirement; active actors remain refused and
unrelated admission or cleanup failures still propagate. Strengthen the
existing expired-caller assertion to require the invalidation code.
No schema, retention, permission, or update-driver contract changes.
Reproduced the existing-schema test on main: 1 failed, 1 passed before fix.
Focused schema/worker suites: 25/25. Changed two-test file: 2/2 in 15.06s
Vitest time, 30.15s wrapper wall with one worker; no new fixtures or cases.
Linux Node 24.19.0: all 39 requested sqlite/state worker and caller-mode
files passed 405 tests; reconstructed CI shard 19 passed 913 tests across
45 files in 221.57s. Native Blacksmith Testbox command exited 0:
https://github.com/openclaw/openclaw/actions/runs/36329004971
Core tsgo, infra/state test graphs, changed-file lint/format, import-cycle
check, diff check, and independent Codex P2 autoreview passed.
Treat typed state-ownership contention as an expected skip during advisory startup capture cleanup. Preserve exclusive maintenance acquisition and all receipt and authority checks; other maintenance and cleanup failures still warn.
Add a CLI preflight regression and permission-error control, and document the distinction. Regression fails before the fix and all 11 tests plus check-changed pass on Blacksmith Testbox.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Reject unsupported timeouts before creating system-event jobs instead of silently dropping the option. Share payload timeout support validation with edit while retaining script guidance and system-owned job protection.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Prepare notification session facts before taking the subscription lease, then read current bindings and revalidate access before delivery. Release retained facts before waiting for the push provider.
A 20-client Gateway fixture with a one-second deferred session read reduced subscribe/preferences p99 from 1085/1087 ms to 84/85 ms. Preserve a regression that fails on the old lock ordering.
Read-only metadata actors can outlive native-only fixture cleanup. Reusing
an inode-matched actor without checking its original admission could reopen
a retired path and acquire the plugin lifecycle lease in the wrong database.
Validate the original admission, join idle actor retirement, and refuse
replacement while its callbacks remain active. Await agent and shared-state
fixture teardown before deleting roots, and retain refusal details in failures.
Reproduced main CI run 36322558996's 44-file shard on Linux/Node 24.19.0:
two lease-loss failures in 195.31s; immediate verification saw empty lease rows.
All three deterministic relocation cases fail before the owner fix.
Proof: fixed local focused suites 56/56; standalone plugin execution 23/23
in 70.11s (77.64s wrapper wall, one worker). Linux Testbox ordered shard
503 passed, 2 skipped; worker suite 33/33 in 37.89s (39.53s wrapper wall,
one worker). Real-worker coverage proves relocated writes and active-owner
fencing that isolated helper mocks cannot establish. Testbox workflow:
https://github.com/openclaw/openclaw/actions/runs/36325573876
Core tsgo, infra/state test graphs, changed-file lint/format, diff check,
and independent Codex P2 autoreview passed. No lease budget, schema,
permission, or installed update-driver contract changes.
* perf(gateway): reduce session roster refresh storms
Apply held participant snapshots without refetching unfiltered rosters. Reset connection-owned ancestor delivery after successful list responses so bootstrap events cannot leave clients relying on an unadmitted revision. Preserve filtered membership and trailing refreshes for overlapping reads.
* test(gateway): complete direct request ancestor context
Provide the required ancestor-delivery reset in the shared direct request fixture used by authenticated plugin HTTP reads. Preserve all viewer privacy, merged-profile ownership, and readiness revocation assertions. The original five failures now pass with the complete context contract.
Closes#149502
## What Problem This Solves
Fixes: an explicitly pinned conversation loses its credential when OAuth re-login replaces the same account's generated profile ID.
## User Impact
Signing back into one unambiguously matched account preserves its existing profile ID and session selection. Different or ambiguous accounts do not take over an explicit pin. A missing pin stays strict and now logs actionable recovery guidance.
There is no new session permission, protocol field, SQLite migration, schema version, or stored field. Upgrade does not rewrite credentials or session rows. If a credential was already deleted before upgrading, the operator must deliberately select a configured `model@profile` or reconnect the intended account with `models auth login --provider <provider> --profile-id <selected-profile-id>`; the missing identity is never guessed.
## Why This Change Was Made
Keep only same-account continuity at the login owner, with the pre-purge snapshot, provider identity matcher, and write-transaction revalidation. Runtime fallback cannot establish the identity of a deleted credential and could silently switch accounts. A separate reset API adds protocol and permission surface without preventing the failure. The existing explicit selection/reconnect paths cover deliberate recovery, so the proposed reset API and unrelated Codex import changes were removed. The large mocked identity suite was replaced by focused ambiguity/error/override cases; the real persistence cases retain continuity and concurrent-reassignment coverage.
[Maintainer decision (Ayaan): approved](https://github.com/openclaw/openclaw/pull/149591#issuecomment-5855670538). Verified same-account sign-in may restore the former profile ID after an explicit force purge; different, ambiguous, or unverifiable accounts do not reuse an ID. There is no new session-reset permission or schema change. Missing pre-upgrade identities continue to require deliberate operator recovery.
No overlap with Pash/Sarah changes.
## Evidence
- Reproduced on pinned main `ea3c488f4fb6`: the production `models auth login` flow (`runModelsAuthLoginFlowCore`) with an isolated scripted OAuth plugin wrote a new ID after forced re-login; the unchanged user-pinned session failed with `No credentials found for profile`.
- The smaller change kept the original ID after same-account re-login and resolved OAuth for the same session written by main. Different-account forced re-login left the explicit pin unavailable, logged the recovery diagnostic, and did not select the new account. Automatic pins resolved the new profile on both main and the candidate.
- The already-broken main-written row recovered through the existing explicit `--profile-id` reconnect path. The user-pinned row, including its internal session ID and timestamps, was unchanged by re-login and auth resolution.
- Before/after SQLite checks were identical: shared `user_version=19`, agent `user_version=23`; ordered `sqlite_schema` SHA-256 values were `42e3cb1d5e4a44081e99a2991c43a8fa77675c6d8ebc74886a7aab38700df36a` (shared) and `d6596c675858b8fdbccced25f9626164634b772ea200830f03c99586ded62b1f` (agent).
- 36 focused tests passed across identity boundaries, production persistence, and session selection after merging main. Typechecking rooted at the touched files and type-aware lint passed. The three-file test command took 46.56 seconds wall time including worker preparation; the five persistence cases took about 1.03 seconds of test execution. Hosted CI timing is pending.
- After the approved main refresh to `32dcd02906ef`, head `d69620a27208` passed all 36 focused tests again (33.35 seconds wall time). The ten PR files are unchanged from the fully exercised `6b25647966e4` revision, and the Pash/Sarah intent check still has no overlap.
- The contributor's previously recorded real OpenAI device-code/two-turn/restart evidence applies to the retained identity-preserving login mechanism. The initial simplification proof above used synthetic OAuth credentials and auth resolution; the full network proof below additionally exercises the production CLI and real Gateway model turns without paid services.
### Full CLI/Gateway network proof
On `6b25647966e4`, a separate loopback HTTP provider served real device-code, authorization, token, and OpenAI-compatible model endpoints. The same driver on both refs used `pnpm openclaw`, a real PTY for `models auth login`, isolated HOME/state/ports, Gateway-backed agent turns, and the dashboard account picker's `sessions.patch` request (`model@profile`). After forced same-account login without `--profile-id` and Gateway restart, the candidate returned the scripted account-A response in the same session (`8f7ec68c-b05f-4c0b-b12b-6d01ced4bd71`) with its explicit user pin intact. Main failed the equivalent next turn with `selected_auth_profile_unavailable`.
Both refs rejected a pinned account-A turn after login as account B; the error was visible in CLI output and no account-B answer was substituted. A separate real `chat.send` control created persisted `authProfileOverrideSource: "auto"` sessions on both refs. After re-login as B and restart, the same sessions completed another turn, `chat.history` contained the account-B response, and each automatic pin rotated to the new credential while retaining source `auto`. Both schema hashes were unchanged across this control. Explicit `--profile-id` reconnect plus restart also recovered the original user-pinned conversation on both refs.
The candidate's full schema hashes and versions remained unchanged throughout: shared version 19/hash `b691989fd451644ad9c0233eb04359bde42744456b1532a94ecd32e1ca429859`, agent version 23/hash `d6596c675858b8fdbccced25f9626164634b772ea200830f03c99586ded62b1f`. A separate before/after CLI re-login check found both complete session rows deeply equal as parsed JSON. Turns themselves appropriately update transcript/activity data.
**Baseline/build limitations:** `9ae6e1853062` and `ea3c488f4fb6` fail their supported runtime wrapper on the known `INEFFECTIVE_DYNAMIC_IMPORT` guard for `transcript-visible-record.ts`. The full network baseline is `a4656d4eea79`, immediately before the commit introducing that import; its fresh runtime build succeeded. OAuth login, credential writer/persistence, and account-picker model-selection behavior are unchanged between that baseline and the PR merge base; intervening relevant changes factor session-selection helpers and clean up manual API-key input handling. Broader OAuth refresh-peer changes are not exercised by nonexpiring fixture credentials. The candidate's full build completed; it does not change the offending import, and the reason the warning differs with its bundle graph is not established.
The older main baseline's optimizer created SQLite-owned `sqlite_stat1` during the flow, changing its full agent-schema hash; excluding that single statistics-table definition exactly recovers its initial hash. Its application schema and versions did not change. The older baseline also lacks an unrelated upstream `worktrees.gc_protection_json` column, so cross-ref shared hashes differ; the earlier current-main existing-state proof above supplies the unchanged-schema upgrade comparison. No schema changes belong to this PR.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* refactor(agents): deslop agent subsystems third pass
Consolidate subsystem projections, runtime forwarding, and typed contracts while preserving authentication, lifecycle authority, and persistence semantics. Preserve canonical remote sandbox path bytes and decode padded structured-input labels consistently. Keep model-facing display text unchanged.
* fix(agents): preserve remote mutation parent identity
Canonicalize the full parent before unlink or rename, leaving final-entry symlink handling to the guest filesystem owner. Keep missing path spellings on one mutation queue identity. Retain public event payload types at their dispatch boundary and shrink verified assertion allowances.
Use transactions on the actual SQLite state and device databases for ordinary writes. Remove redundant coordination databases, transport, and exclusion layers while preserving bounded process ownership for startup, schema work, and offline maintenance.
Tie test and QA scratch retirement to settled workers and native resources, preserve active plugin captures, and join SDK declaration compiler processes before synchronous semantic rendering.
Validation: main-tier CI on 5e731c1f64 had 144 successful jobs and one Windows ACP initialization timeout. Qualified unchanged replay 36314027585 passed all 896 tests with the original 48-file order, six projects, toolchain, and deadlines. The original timeout remains unexplained and recorded in the PR. Reviewed main-conflict integration through 39caa592ef passes focused SQLite, Doctor, image, and Cron proof plus affected typechecks and lint. No accepted actionable independent-review findings remain.
Squash landing of #157413 under explicit maintainer authority to resolve logical main drift and admin-merge using the completed CI evidence. No PR-specific schema or public configuration migration.
* feat(apple): resolve native chat agent identities
Resolve agent names and bounded text or emoji avatars in the native macOS chat sidebar, toolbar subtitle, composer, and New Thread picker. Configured roster names win over resolved identities, and unnamed agents display Assistant.
Extend the existing shared agent catalog and choices with agent.identity.get requests by agentId. Refresh identities with the roster, preserve its order and routing metadata, and fence publication to the captured Gateway connection. The iOS New Thread picker uses the same owner. Keep image avatar loading and Quick Chat outside this change.
Web references: ui/src/lib/assistant-identity.ts, ui/src/lib/agents/identity.ts, and the configured-name precedence in ui/src/lib/agents/display.ts. Shared normalization/request tests pass; macOS app and test bundle compile; localization and changed-file checks pass. Native UI screenshots and independent review remain with the campaign coordinator.
* fix(apple): publish agent rosters before identity hydration
Keep the native sidebar and shared New Thread picker usable while optional agent identities load. Publish each identity on its captured connection, preserve picker selection, and clamp badge text to two complete graphemes while retaining normalized identity data. Cover availability, route retirement, selection, and badge rendering with shared regressions.
* feat(presence): let agents inspect people and active devices
Expose a read-only presence tool for online people, requester-scoped device activity, and optional network and IP geography. Reuse live connection, profile, node, and geolocation owners; preserve activity provenance and worker requester authority without retaining activity history.
* fix(presence): register tool display metadata
* fix(presence): require live source authority for agent reads
Preserve authenticated Team read scopes and explicitly admitted owner or scheduled sources before synthetic Gateway dispatch. Retain the same source through worker execution and revalidate before returning observations. Append the advertised RPC to preserve existing method indices.
Include the already-landed c0e6951d6f Cron fixture scheduler corrections so the branch typechecks independently. Add real local router and worker admission regressions for unknown, limited, scheduled, and revoked sources.
* fix(protocol): regenerate appended presence method enum
* fix(tooling): include presence schema in PR wrapper extraction
* test(gateway): drain buffered MCP keepalives on shutdown
* test: qualify synthetic approval kind assertion
Keep the negative-type assertion from #159132 explicit in the inventory and bind its allowance to the exact synthetic invalid-input fixture. The existing zero-any and inventory checks remain active, and a changed-input control fails at the fixture qualification.
* test(presence): reconcile rebased lookup and inventory fixtures
* build(presence): refresh plugin assets after dependency update
* test(presence): verify activity reporting retains device attribution