openclaw/config
Dallin Romney 6b7a73d39b
chore: prepare extended-stable 2026.8.34 (#160960)
* fix(plugins): recover orphan installs without weakening ownership

Closes #134321

## What Problem This Solves

Operators could not run `openclaw plugins update --all` or uninstall a path-source plugin after both tracked plugin paths disappeared. Registry refresh succeeded but left the durable install record behind, so both commands failed on the same ownership check.

## Why This Change Was Made

Plugin lifecycle ownership now distinguishes an active package from an exact orphan record. Update and uninstall may act on an orphan only after duplicate-path and conflicting-plugin checks pass. Inspection, capability consent, active packages, and restored packages keep strict package ownership.

Post-update reconciliation accepts an unchanged safe orphan while another package updates. If an orphan install record changes, the replacement must restore a strictly valid package before any update can commit.

Uninstall removes owner-keyed plugin and channel policy plus the durable install record. Child policy remains based only on authoritative discovered children.

## User Impact

Operators can update their remaining plugins and remove stale path-source install records without editing OpenClaw state by hand. Ambiguous, conflicting, and invalid replacement packages still fail closed.

## Evidence

Current `main` at `77fee95791` failed through the real command-line flow after a local plugin was installed and both tracked paths were moved away:

```text
$ openclaw plugins update --all --dry-run
Plugin "orphan-proof" package owner "orphan-proof" has no authoritative runtime child list.
Exit 1

$ openclaw plugins uninstall orphan-proof --force
Plugin "orphan-proof" package owner "orphan-proof" has no authoritative runtime child list.
Exit 1

$ openclaw plugins registry --refresh
Plugin registry refreshed: 0/0 enabled plugins indexed.
Exit 0

$ openclaw plugins update --all --dry-run
Plugin "orphan-proof" package owner "orphan-proof" has no authoritative runtime child list.
Exit 1
```

Exact head `06de2f66734fd338e457f50a3060e2d625288274` passed the same flow:

```text
$ openclaw plugins update --all --dry-run
Skipping "orphan-proof" (source: path).
Exit 0

$ openclaw plugins uninstall orphan-proof --force
Uninstalled plugin "orphan-proof". Removed: config entry, install record.
Exit 0

$ openclaw plugins update --all --dry-run
No tracked plugins or hook packs to update.
Exit 0
```

The contributor head initially accepted a changed orphan install record with no authoritative children. The new owner-boundary regression failed with `{ ok: true }` before the maintainer repair and passes after it.

Validation:

- `node scripts/run-vitest.mjs src/plugins/manifest-registry-installed.test.ts src/plugins/management-service.uninstall-ownership.test.ts src/plugins/plugin-package-update.test.ts src/cli/plugins-cli.update.test.ts src/cli/plugins-cli.uninstall.test.ts` — 129 passed.
- Targeted Oxfmt and Oxlint — passed.
- Max-lines and assertion-safety ratchets — passed.
- Coercion, deprecated-API, dead-export, native-schema, and import-cycle guards — passed.
- `git diff --check` — passed.
- Local type checking was not run because host policy requires CI for OpenClaw type checks.

The proof fixture, isolated states, removed plugin copies, and proof manifest remain in the durable maintainer worktree.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): repair keyed multi-agent rosters without ownership (#134706)

* fix: prevent device pairing migration crash on contaminated records

Closes #134340

## What Problem This Solves

Operators upgrading from the retired JSON device-pairing store can have pending requests or malformed values inside `devices/paired.json`. One bad record aborts the full JSON-to-SQLite import, keeps every valid pairing out of SQLite, and repeats the failed migration on each gateway start.

The pairing migration failure is caught and logged. It does not stop gateway startup by itself. The field report also contained a separate legacy session-store blocker.

## Why This Change Was Made

The migration now validates every field that SQLite binds directly. It skips records with invalid required pairing data. When only optional metadata is malformed, it omits that field and preserves the usable pairing. The original legacy store is archived only after the safe import completes.

Validation includes required timestamps, optional `lastSeenAtMs`, and optional text columns. Separate warnings report skipped records and omitted optional fields. The SQLite mapper remains strict, and no schema or fallback path changes.

## User Impact

Valid device pairings survive an upgrade even when the legacy file also contains pending requests or malformed records. The gateway completes the import once and does not retry it after restart.

## Evidence

- Current-main red at `77fee95791`: a normal isolated gateway logged `NOT NULL constraint failed: device_pairing_paired.created_at_ms` for a pending-shaped leaf and retained `paired.json`.
- Anti-cheat red: changing the malformed leaf to `lastSeenAtMs: 1e100` logged `cannot store REAL value in INTEGER column device_pairing_paired.last_seen_at_ms`.
- Regression red on contributor head `ee7ea2bfbc5bde3e1b6f90ea58e4d070b7e7ff61`: the expanded boundary test failed with `Provided value cannot be bound to SQLite parameter 47`.
- Fixed-head boundary proof: `node scripts/run-vitest.mjs src/infra/device-pairing-migration.test.ts` passed 7/7; targeted formatting and Oxlint passed.
- Optional-preservation red on `4f47648f1d6bcf279697d4e8ec5044960bb27cf5`: the corrected regression failed because only one row imported instead of preserving three usable pairings.
- Final real gateway proof: startup returned `status=started`, imported three usable pairings, stored malformed optional metadata as `NULL`, skipped one record with invalid required data, archived `paired.json`, and restarted with no migration log.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): preserve per-agent memory search during upgrades

Closes #134256

Reported by @vdruts.

## What Problem This Solves

Fixes an issue where operators running `openclaw doctor --fix` after an upgrade could silently lose every per-agent memory-search override when Doctor converted `agents.list` to keyed `agents.entries` first. This removed settings such as `extraPaths`, `sources`, `experimental.sessionMemory`, and `enabled`.

## Why This Change Was Made

Doctor now visits both keyed agent entries and legacy list entries at the migration owner. It moves `memorySearch` before unknown-key cleanup, preserves explicit canonical values, and applies the existing provider, nested-field, and retired-path migrations to the moved data.

The shared agent-scope traversal replaces two duplicate traversal implementations. The regression was introduced by #112678, which moved roster normalization before the older list-only migration from #111527.

## User Impact

Per-agent memory-search settings now survive `openclaw doctor --fix` and remain attached to the correct agent. Operators no longer lose per-agent retrieval paths silently during this upgrade path.

## Evidence

- Red on current-main baseline `8abb80073c`: [Testbox run 33466170600](https://github.com/openclaw/openclaw/actions/runs/33466170600) removed the legacy block and retained only the pre-existing canonical field.
- Green on exact head `983df6b6ddf33ffad80f32874966ffeeaf0a803f`: [Testbox run 33467597600](https://github.com/openclaw/openclaw/actions/runs/33467597600) ran the real CLI, preserved supported fields and canonical precedence, verified the authored input, and produced byte-identical config on a second Doctor run.
- Regression proof: the strengthened Doctor process test fails on the baseline and passes on the head.
- `node scripts/run-vitest.mjs src/commands/doctor-config-preflight.process.test.ts` — 10 passed.
- `node scripts/run-vitest.mjs src/commands/doctor/shared/legacy-config-migrate.test.ts src/commands/doctor/shared/legacy-config-migrations.runtime.retired.memory-qmd.test.ts src/commands/doctor/shared/legacy-config-migrations.runtime.retired.test.ts` — 294 passed.
- Focused format, Oxlint, Doctor registry, coercion helper, max-lines, assertion safety, import-cycle, temp-directory, conflict-marker, and dead-export checks passed.
- Autoreview on the exact local diff: clean, no actionable P0 findings.

Agent task: https://chatgpt.com/codex/tasks/01a040d7-aa3d-7433-83eb-de7f49979443

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): detect shared auth migration by provenance (#134808)

* fix(cron): refuse stale doctor store rewrites

Prevent `openclaw doctor` from replacing cron definitions or runtime-owned state captured before its interactive repair prompt.

Doctor now rechecks a bounded definition-and-order fingerprint inside the SQLite write transaction. Definition changes stop the repair with a retry warning. Runtime columns and current runtime authority remain owned by the Gateway, while repaired authorization-input changes fail closed into recovery.

Preserve old embedded-authority migration and current quarantine recovery.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(gateway): refresh model catalog after auth changes (#134361)

* fix(models): refresh catalog content after credential-identity changes

* refactor(agents): split auth-profile mutation lineage out of runtime-snapshots

Split the max-lines boundary by moving persisted mutation lineage out of runtime snapshots; behavior is unchanged.

* refactor(agents): annotate the moved test-api global assertion for the safety ratchet

* test(agents): type the catalog worker mock and shrink the assertion baseline

* perf(agents): bound auth publication provider facts

* perf(gateway): keep auth status off catalog discovery

* fix(agents): reject retired catalog owners

* test(gateway): resolve the auth-status handler result before racing it

* fix(models): gate catalog refresh by read intent

* test(models): prove deferred catalog reads stay static

* test(models): type the unscoped catalog-refresh row context

* fix(openai): repair stale doctor route pins

Declare canonical OpenAI session-route ownership at the OpenAI plugin manifest boundary.

Clear automatic fallback-origin provenance when Doctor removes the stale model override. Preserve explicit model choices and valid locked harness sessions.

Fixes #101672.

Credit @849261680 for the original report-driven repair.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(auth): verify shared credential migration receipts (#134952)

Attach expected credential fingerprints to both credential-store receipt
variants so interrupted shared-store imports use the existing verification
and archive recovery flow. State-only receipts retain their own verification.

Regression coverage interrupts archival and removes the destination before
retrying both legacy auth file formats against both SQLite destinations.
The shared-store cases fail before this change; all 90 focused tests pass.

Related to #134608, reported by @erameson. This preserves the existing
non-replay policy for completed receipts; recovery of already-completed
receipts requires a separate maintainer decision.

* fix(cron): keep model aliases scoped to the selected owner (#135069)

Reuse the published model owner metadata for default, agent, subagent, payload, session, and Gmail selections. Consolidate override arguments while retaining agent-specific policy and existing precedence. Six regression cases fail before and pass after; 83 related tests pass. Related: #130324, #130706.

* fix(memory): resolve profile auth for compatible embeddings (#134744)

* fix(memory): resolve profile auth for compatible embeddings

* test(memory): close profile fixture SQLite handles

* fix(memory): preserve credential ownership in embedding auth

Resolve saved profile bindings through the canonical terminal auth capability while preserving literal, blank, explicit-header and destination behavior. Retain general model-auth precedence and shared guard policy; exercise real HTTP, SQLite and public CLI indexing/search. Reuse the canonical doctor CI fixture and repair executable-preflight test isolation.

Co-authored-by: 0x-Parzival <0x-Parzival@users.noreply.github.com>

* fix(memory): preserve auth policy for configured embeddings

Carry the selected provider API into the canonical auth-mode check, so direct OpenAI routes reject saved token profiles before embedding HTTP. Check auth migration readiness before classifying provider-entry credentials, including warm snapshots after a legacy credentials file is restored. Keep explicit remote credentials, literal keys, and canonical SQLite profile ownership intact.

Regression coverage exercises provider/API/credential-mode combinations and cold-empty, warm-empty, and populated canonical stores with real embedding HTTP and SQLite.

Co-authored-by: 0xParzival <145645180+0x-Parzival@users.noreply.github.com>

* test(pr): reuse landed cross-checkout fixture

Adopt the exact cross-checkout fixture already landed in main by #134740, resolving the overlapping cleanup while preserving the isolated executable command stubs. The embedding and auth implementation is unchanged.

Co-authored-by: 0xParzival <145645180+0x-Parzival@users.noreply.github.com>

---------

Co-authored-by: 0x-Parzival <0x-Parzival@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: 0xParzival <145645180+0x-Parzival@users.noreply.github.com>

* fix(auth): recover credentials stranded by completed migrations (#135346)

* fix(auth): recover inconsistent completed credential migrations

* test(auth): brace completed receipt recovery branches

* fix(auth): relogin repairs stale profile order after upgrade

Closes #135140

Reported by @mattcbianco.

## What Problem This Solves

Fixes an issue where users who signed in to OpenAI again after shared auth-store relocation could still get `Explicit auth order for openai has no usable profiles.` before a model request started.

## Why This Change Was Made

Login credentials and per-agent profile order have separate SQLite owners after relocation. Profile promotion now reads the effective credential view before it updates the agent-local order, and it preserves the inherited profile ID when it saves that order.

This keeps explicit order fail-closed behavior unchanged. It does not add a dispatch fallback or copy shared OAuth credentials into the agent database.

## User Impact

A successful OpenAI relogin now places the new usable profile before stale entries, so the next `openai/*` model call can start with the login that just succeeded.

## Evidence

- Current-main baseline: `2e3f734017`.
- Before: Codex reported a healthy ChatGPT login and OpenClaw listed a healthy OAuth profile, but the selected Codex runtime had no effective profile. Real Luna and Sol turns stopped before provider dispatch with `Explicit auth order for openai has no usable profiles.`
- After: OpenClaw device login stored the fresh OAuth profile, agent-local order became `[fresh, stale]`, and status reported a usable Codex runtime route.
- Live exact-head result: a real `openclaw agent --local` Luna turn returned exactly `ISSUE_135140_EXACT_HEAD_GREEN_2` through provider `openai`.
- Shipped CLI regression: `models auth login --provider openai --agent main` failed on the baseline because local order stayed absent; passed after the fix with `[fresh, stale]`.
- Focused CLI, owner, and sibling tests: 131 passed across login promotion, auth upsert/rollback, shared-store relocation, and runtime auth planning.
- Oxfmt, Oxlint, and `git diff --check`: passed.
- Autoreview P0: clean.

Production delta: `+4/-5` (net `-1`). Test delta: `+108/-0`.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(auth): preserve custom provider SecretRefs in Doctor

Fixes #135079.

Thanks @dannevang for the detailed report and two-provider reproduction.

## Problem

After an upgrade, `openclaw doctor` could treat custom-provider environment references and generated catalog values as plaintext API keys. It persisted them as auth profiles, and those profiles could shadow the provider's direct environment credential.

## Root cause

Config substitution discarded the fact that a resolved string came from an authored environment SecretRef. Doctor then classified model catalog strings without the provider's authored resolution facts. Runtime planning and status also treated stored profiles as usable ahead of an unqualified provider SecretRef.

## Fix

- Record resolved environment SecretRef provenance during config substitution.
- Make Doctor keep an authored provider SecretRef authoritative over root and plugin catalogs.
- Remove only persisted profiles whose values exactly match that provider's reference markers.
- Make runtime planning and model status prefer the authored provider SecretRef.
- Reuse the already loaded authored model config during status readback.

The production delta is +122 lines. The added code carries provider-specific pending/resolved provenance across config and worker boundaries and performs exact-value cleanup at the credential-migration owner. A global marker heuristic or downstream guard would not preserve this ownership or safely repair existing rows.

## Proof

- Baseline main `9d10dcb5d3`: Doctor migrated two `${VAR}` references plus stale or bare generated values; model status selected stored profiles over fresh direct environment values.
- Exact head `fc74120ab55d071e7c876b2f00c9db47032b8bb8`: Doctor migrated no model credentials, preserved both authored references, left no `authProfiles.store` row, and status selected two distinct environment credentials with zero profiles.
- Main commits after the baseline did not touch any changed file.
- Regression tests were red before the fix and green at the exact head.
- Focused tests, the remote changed-scope gate, and the exact-head remote build passed. The first PR CI run exposed source-authority and max-lines defects; the exact failing tests now pass locally.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(docker): install libgomp1 in the runtime image for managed llama.cpp (#134532)

* fix(docker): install libgomp1 in the runtime image for managed llama.cpp

The official ghcr.io/openclaw/openclaw image (bookworm-slim) never
installed libgomp1. The verified llama.cpp release binaries downloaded
by managed local llama-server setup are linked against libgomp, so the
--version preflight probe fails with "libgomp.so.1: cannot open shared
object file", making local embeddings unreachable in the official
Docker image.

Fixes #134439.

* fix(docker): clarify managed llama-server runtime requirement

Keep the mandatory OpenMP runtime in the image and identify its preflight contract in the existing comment and regression test.

Co-authored-by: chelsealong <chelsealong@126.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: chelsealong <chelsealong@126.com>

* fix(devices): allow authorized scoped node token management (#135617)

* fix(cli): allow authorized node token rotation and revocation

* test(cli): keep token scope fixture indices typed

* test(ui): scope catalog marquee assertions to session rows

* fix(msteams): thread context is dropped when a channel reply has no replyToId (#129345)

* fix(msteams): thread context is dropped when a channel reply has no replyToId

Teams channel activities frequently arrive without `replyToId`. Thread context
resolution keyed exclusively off that field, so parent-message and sibling-reply
context silently never loaded for those turns and the agent answered without any
of the thread it was replying in.

Fall back to the thread root already parsed from the conversation id. The Graph
message URL builder in `attachments/graph.ts` resolves the same value this way
via `threadRootMessageId`, and `resolveMSTeamsRouteSessionKey` likewise accepts
both, so this aligns thread context with the two adjacent call sites rather than
introducing a new source of truth.

The self-comparison guard keeps a top-level post from being treated as its own
parent, mirroring the `threadRootMessageId !== messageId` check in graph.ts.

Behaviour is unchanged whenever `replyToId` is present.

* fix(msteams): resolve thread context against the conversation thread root

Review follow-up: the fallback chose `replyToId` ahead of the conversation's
embedded thread root, which contradicts the canonical precedence used elsewhere
in the plugin.

`resolveMSTeamsRouteSessionKey` reads `conversationMessageId ?? replyToId`, and
`attachments/graph.ts` resolves the Graph message URL from `threadRootMessageId`
the same way. On a deep reply, where both identifiers are present and differ, the
previous ordering routed the session to the thread root while fetching parent and
sibling context beneath the nested reply — attaching context from one message to
the session of another.

Take the conversation root first and keep `replyToId` as the fallback for
activities whose conversation id carries no thread suffix. The self-comparison
guard is retained so a top-level post is not treated as its own parent, matching
the `threadRootMessageId !== messageId` check in graph.ts.

Adds a handler-level regression covering both identifiers present and different;
it fails under the previous ordering.

* refactor(msteams): tighten the thread-root invariant comment

Condenses the nine-line block to the three-line form used elsewhere in this file,
keeping the two load-bearing facts: why the conversation root wins over replyToId,
and that replyToId remains the fallback. No behavior change.

* test(msteams): prove channel thread retrieval at the Graph boundary

The existing thread-parent tests stub ../graph-thread.js, so they pin the
selection rule but never reach the retrieval boundary the repair actually moves.
This asserts on the HTTP requests the process issues instead: thread-context.ts,
graph-thread.ts and graph.ts all run unmocked, and the only stub is globalThis.fetch.

The stub has to be a vi.fn(). fetchWithSsrFGuard treats a fetch carrying a .mock
property as a test-installed mock (isMockedFetch) and lets it win; a plain function
is bypassed in favour of undici dispatcher-aware fetch, and records nothing.

For a channel activity with no replyToId the process now issues:
  GET /v1.0/teams/{aad}/channels/{channel}/messages/{root}
  GET /v1.0/teams/{aad}/channels/{channel}/messages/{root}/replies?$top=50
and the retrieved parent reaches the agent. With threadParentId reverted to
activity.replyToId that set is empty, so the first assertion fails on zero requests.

* test(msteams): prove channel thread retrieval through the registered handler

Replaces the direct-invocation variant with one shaped after
approval-control.mock-gateway.test.ts: the activity enters through
registerMSTeamsHandlers and is driven by the registered Bot Framework onMessage
callback, so the inbound path runs as registered rather than a handler factory
being called directly.

The message handler, thread-context.ts, graph-thread.ts and graph.ts all run
unmocked; Graph is observed at the fetch boundary. The stub has to be a vi.fn()
because fetchWithSsrFGuard only lets a fetch carrying a .mock property win over
undici dispatcher-aware fetch (isMockedFetch).

For a channel activity delivered without replyToId the process issues:
  GET /v1.0/teams/{aad}/channels/{channel}/messages/{root}
  GET /v1.0/teams/{aad}/channels/{channel}/messages/{root}/replies?$top=50
and the retrieved parent reaches the agent as a system event. With threadParentId
reverted to activity.replyToId neither request is issued.

* fix(test): restore ambient fetch and correct the mock-gateway proof signatures

Three defects in the proof added by the previous commit, all caught by CI:

- The fetch stub was never restored, so it leaked into sibling suites sharing the
  worker and extensions/msteams/src/qa/bot-framework-server.test.ts began seeing 200
  for requests it expects to reject. An afterEach now restores the ambient fetch.
  The local full-suite run passed only because vitest happened to order the files
  the other way round; running the two files together reproduces it.
- OpenClawConfig was imported from ./runtime-api.js; the module sits one level up.
- The registered onMessage callback takes (context, next, turnAdoptionLifecycle);
  both call sites passed only the context.

* fix(test): drop redundant String() conversions in the mock-gateway proof

typescript(no-unnecessary-type-conversion) flagged both call sites: the optional
chain already yields a string once defaulted, so String() changes neither type nor
value. The rule is type-aware, so a bare oxlint run over the single file did not
surface it; only the project shard does.

---------

Co-authored-by: Alexander Malysh <malysh00@gmail.com>
Co-authored-by: Alexander Malysh <a.malysh@venista.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>

* fix(ui): ignore tool-only model auth rows (#134218)

* fix(ui): ignore tool-only model auth rows

* fix(ui): ignore tool-only model auth rows

* fix(ui): ignore tool-only model auth rows

* fix(ui): ignore tool-only model auth rows

* fix(ui): preserve model accounts when filtering tool credentials

Use canonical monitored-auth evidence for Model Providers cards. Preserve
OAuth and non-expiring token profiles, alias logout targets, configured
models and usage while omitting tool-only API-key rows. Remove duplicate
usage-presence bookkeeping.

Refs #134218. Production LOC -3; tests +56.

Co-authored-by: wantosure <wantosure@126.com>

* test(ui): assert usage failures without provider cards

Replace the stale OpenAI-card readiness wait with the visible usage
failure outcome and assert that an unconfigured provider stays absent.
Preserve the RPC assertion and all configured-provider sibling cases.

Refs #134218. Production unchanged; test LOC net zero.

Co-authored-by: wantosure <wantosure@126.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: wantosure <wantosure@126.com>

* fix(cron): kind change fails with "command env must be an object" when env is omitted (#134639)

* fix(cron): omit undefined payload fields

* fix(cron): preserve valid payload kind conversions

Omit absent optional payload fields at the replacement producer and remove
unreachable same-kind fallback branches. Keep strict validation and cover
actual service persistence, malformed env, and tool restrictions.

Co-authored-by: Solomon Neas <me@solomonneas.dev>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Solomon Neas <me@solomonneas.dev>

* fix(backup): exclude workspaces before traversal

Carry disabled workspace roots into the shared backup inventory before tar and SQLite traversal. Preserve nested agent state and keep absolute-link rejection for included content.

Fixes #135218. Supersedes #135243 and #133990.

Thanks @jarvismazz and @yubingjiaocn for the reports. Thanks @ericcaiwx-star and @ruel225 for the original repair work.

Co-authored-by: ericcaiwx-star <ericcaiwx@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(infra): drop empty PATHEXT entries from Windows extension lookup (#135807)

* fix(infra): drop empty PATHEXT entries from Windows extension lookup

resolveWindowsExecutableExtensions split PATHEXT on ";" without dropping
empty components. A trailing ";", which is common in real PATHEXT values,
leaves "" in the list, so an extensionless file matches even when the
caller passed includeExtensionless: false. windows-command.ts and tui.ts
both pass false.

The sibling resolveWindowsExecutableExtSet in the same file already
filters, so this was just the one path missing it.

* fix(infra): reuse normalized Windows executable extensions

Keep extensionless command lookup explicit by sharing the existing PATHEXT parser. Remove duplicate parsing and cover empty suffixes, explicit filenames, and the command-resolution caller.

Co-authored-by: Teddy Tennant <teddytennant@icloud.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Teddy Tennant <teddytennant@icloud.com>

* fix(anthropic): context usage is lost when a proxy omits message_delta usage (#135467)

* fix(anthropic): keep context usage when message_delta omits usage

The Anthropic SDK lane guarded the shared usage accumulator against an omitted
message_delta usage object; the managed transport lane called it unconditionally,
where the payload ?? {} fallback overwrote the message_start prompt snapshot with
contextUsage: unavailable. Anthropic-compatible routes whose provider is not
"anthropic" are forced onto that transport, so the population the guard was written
for never had it.

Move the presence check into the accumulator that owns the payload contract and drop
the duplicated guard in the SDK lane, so both lanes follow one rule. A present but
empty usage object still resolves to unavailable, matching the previous SDK check.

* test(anthropic): assert delta context usage through the usage object

expect(usage.contextUsage) narrowed to the object literal returned by the local
emptyUsage helper, which does not declare contextUsage, so the core test-types
stripe rejected it. Read it through toMatchObject like the neighboring cases.

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* fix(systemd): protect credentials in managed backups (#131786)

Sanitize and privatize managed unit backups, restore them atomically on publication failure, surface unsafe remnants, and remove them across uninstall paths.

Closes #131781. Supersedes #131784.

Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>

Worked on by:
- @vyctorbrzezowski

* fix(agents): preserve requester MCP runtime ownership (#134819)

## What Problem This Solves

Requester-scoped MCP resolution can own serialized work for a runtime key while that runtime has no lease. Idle or cap cleanup could dispose the runtime and its transport before the work finished.

## Why This Change Was Made

The lifecycle owner now treats the existing requester work chain as active ownership. Idle cleanup skips that key, and cap cleanup does not queue an eviction behind that work. Required session disposal still drains the same chain.

## User Impact

Requester-scoped MCP transports and sessions now survive resolver work. Normal idle cleanup still removes the runtime after that work drains.

## Evidence

- Current-main loopback reproduction: the production manager returned one idle eviction while requester resolution was held; expected zero.
- Exact candidate loopback proof: the shipped harness runtime retained the same runtime object and MCP session identifier through the protected sweep, then the later idle sweep removed the runtime and closed the session.
- Cap regression: the original PR head disposed the refreshed requester runtime; the repaired head preserved it.
- [Exact-head agent shard](https://github.com/openclaw/openclaw/actions/runs/33675253407/job/100398989009): 89 files and 1,395 tests passed, including the real loopback boundary.
- Formatting and `git diff --check`: passed.
- Exact-head Autoreview P0: clean.

## Proof Boundary

The proof uses the production MCP runtime manager, the pinned MCP SDK 1.30.0 client and server, and a real loopback Streamable HTTP session. It does not claim external MCP service or provider authentication behavior.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(plugins): refresh persisted registry when source mounts change (#136517)

* fix(plugins): retain mounted source ownership through loading and restart

* ci: refresh checks against current main

* fix: gateway restart hangs while followup drain retries a draining error (#136713)

Closes #136684

## What Problem This Solves

Fixes an issue where operators restarting a Gateway with a queued follow-up could wait several minutes for shutdown. The queued turn retried after restart admission rejected it, so the old Gateway stayed active until its shutdown deadline.

## Why This Change Was Made

The follow-up queue now follows the existing one-way restart signal. When restart drain commits, it retires process-local queue work and its old callback before the Gateway inspects active work. A reversible restart-signal fence still waits for settlement and retries after rollback.

Durable accepted-input recovery remains the restart owner. The old in-memory queue and execution authority do not cross restart, as documented in `docs/gateway/restart-recovery.md`.

## User Impact

A Gateway with a queued follow-up can finish a supervised restart on the normal shutdown path instead of emitting a retry burst until forced termination. If restart signal admission rolls back, the queued follow-up resumes normally.

## Evidence

Current-main red at `3100ba535f`:

- Blacksmith child-Gateway run: https://github.com/openclaw/openclaw/actions/runs/33711422936
- One held model turn, one confirmed queued same-session follow-up, then `SIGTERM`.
- Queued case reached the real follow-up admission path and emitted `7` `followup queue drain failed` events.
- No-queue control emitted `0` drain failures.

Exact repaired tree at `2baff4f9ed3f84dc9493149dcc20fed441c9baea`:

- Blacksmith exact-tree run: https://github.com/openclaw/openclaw/actions/runs/33714572460
- The same child-Gateway scenario emitted no retry burst and exited in `3.465s`.
- `src/auto-reply/reply/queue.drain-restart.test.ts`: `29` passed.
- `test/gateway-queued-session-rotation.e2e.test.ts`: `2` passed.
- Materialized source hashes matched the local commit on https://github.com/openclaw/openclaw/actions/runs/33715159753.
- Type-aware Oxlint passed on all touched files.
- Oxfmt and `git diff --check` passed.

The focused tests also cover:

- a typed draining error while restart admission stays open;
- restart-signal rollback and queue resume;
- restart-signal promotion to one-way drain;
- synchronous retirement of queued turn ownership and stale callbacks.

Production LOC: `+33/-2` (net `+31`). The growth binds queue authority to the existing restart signal and closes the user-visible shutdown lifecycle gap. Tests: `+316/-0`.

## AI Assistance

AI-assisted repair. The contributor's original restart-fence distinction and credit are preserved. Maintainer follow-up added the real Gateway reproduction, lifecycle retirement, and exact-head proof.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(auth): preserve OAuth metadata during session SDK refresh (#127988)

The shipped agent sessions SDK replaced a persisted OAuth credential with the provider refresh result. Providers such as Anthropic return only rotated token fields, so the write removed identity and plan metadata.

Merge the locked stored credential beneath the provider result at the session AuthStorage persistence boundary. Rotated tokens still win, while omitted persisted fields survive.

Tests:
- Published `openclaw/plugin-sdk/agent-sessions` path with real SQLite-backed AuthStorage refresh: red on current main, green on the repaired head.
- `node scripts/run-vitest.mjs src/agents/sessions/auth-storage.test.ts`: 23 passed.
- Focused formatting, lint, and diff checks passed.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): recognize configured memory provider secret references (#137782)

Recognize structured provider SecretRefs at Doctor's mapped-provider credential-presence boundary without resolving secret bytes or accepting empty environment markers.

Preserve missing-key warnings for absent credentials and preserve the live Gateway's owner-specific unresolved-secret diagnostics.

Verified with the real doctor command on baseline main and the exact candidate: provider-owned and remote-memory references, absent keys, bare markers, and two live unresolved-reference controls. All 102 focused tests pass. Synthetic credentials were used with container networking disconnected; no provider authentication is claimed.

Closes #137742.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(sessions): preserve logical shared-store ownership (#138380)

* fix: full-access tasks lose tools after Gateway restart (#138701)

* fix: preserve full access when continuing interrupted tasks

* test: exercise full-access continuation across a live restart

* fix: retain replay safety beyond the recent transcript window

* test: make live restart checkpoints suspend explicitly

* fix: keep unchanged files out of session diffs (#138877)

* fix(auth): recover billing-disabled profiles in minutes instead of hours (#136364)

Reduce the initial billing-disable window from five hours to ten minutes for stored auth profiles and inline API keys. Inline keys become eligible after expiry; another billing failure starts a new ten-minute window. Stored-profile recovery probes and already-active persisted deadlines remain unchanged.

Correct the recovery-policy documentation and source comment. Verified with 598 focused auth/fallback tests and docs checks on Cloudflare, real SQLite persistence and eligibility proof, independent review, and successful CI on c44f81187bd745c70b23a360fe15496c40251bf9. Live provider recharge was not exercised; the maintainer accepted that documented proof limitation.

Fixes #135835. Thanks @holny.

Co-authored-by: holny <12433219+holny@users.noreply.github.com>
Co-authored-by: Altay <altay@hey.com>

* fix(auth): keep an unset default model unchanged during login (#139218)

Preserve an absent default model during ordinary provider login, just as existing model values are preserved. Remove only the provider-injected default while keeping connection settings, aliases, and credentials.

Keep --set-default as the explicit model-selection action. Extend the existing command regression across supported model shapes.

Related: #136257

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(gateway): avoid crashes when an upgrading client disconnects (#139061)

* fix(gateway): avoid crashes during interrupted upgrades

* test(gateway): bound upgrade regression websocket payloads

* fix(shell-env): preserve CLI terminal ownership during login imports (#139308)

Start the shared login-shell probe in a separate session so interactive Bash cannot take the CLI's controlling terminal. Preserve interactive startup exports and existing filtering, framing, timeout, executable lookup, and cache behavior.

Verified the current-main Doctor SIGTTOU failure and candidate completion using the built CLI in a real job-control terminal. Added a real-Bash startup regression and verified JSON output, imported keys, terminal restoration, and timeout behavior.

Closes #139257.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(auto-reply): fold trailing transcript growth into memory-flush fresh totals (#138928)

* fix(auto-reply): fold trailing transcript growth into memory-flush fresh totals

runMemoryFlushIfNeeded persisted totalTokensFresh=true using only the last
provider usage anchor, ignoring messages appended after it. A later
preflight compaction check trusts totalTokensFresh and skips re-reading the
transcript, so real growth after the anchor could silently escape both the
early memory-flush trigger and the blocking compaction gate.

Project trailing messages the same way runSessionCompactionIfNeeded already
does before persisting the total as fresh.

* refactor(auto-reply): share anchor-plus-trailing prompt token calc

Memory flush duplicated the anchor-plus-trailing-projection calculation
that preflight compaction (estimatePromptTokensFromSessionTranscript)
already used. Extract resolveAnchoredPromptTokens() and have both call
sites use it, so the two calculations can no longer drift.

* fix(auto-reply): share fresh usage projection without duplicate accounting

Include transcript growth after the latest provider usage report before
memory maintenance saves a fresh total for preflight compaction. Reuse the
existing provider-visible estimator and canonical freshness validation,
removing the extra wrapper and redundant freshness check.

Related: #138871
Co-authored-by: chelsealong <chelsealong@126.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: chelsealong <chelsealong@126.com>

* fix(cron): preserve pending paced checks across restart (#140083)

Let the shared missed-slot decision honor the accepted future paced
occurrence before startup admission or backoff repair can replace it.
Keep ordinary unpaced and overdue catch-up behavior unchanged.

* fix(daemon): systemd install no longer aborts on "ownership could not be verified" after a clock step (#137253)

Replace wall-clock deadline arithmetic with `performance.now()` for systemd ownership probes, definition mutation, effective-manager reads, and legacy metadata refresh.

This preserves the existing shared timeout budgets, fallback behavior, and fail-closed ownership checks while preventing NTP, resume, or manual clock steps from collapsing later calls to the 1 ms floor or inflating them beyond the configured budget.

Regression coverage exercises forward and backward wall-clock steps across all four deadline owners. Exact-head CI, Workflow Sanity, independent P0/P1 review, and the durable ClawSweeper review passed.

Co-authored-by: coderdailyone <codecookie@proton.me>

* fix(config): apply environment changes after in-process restart (#141296)

## What Problem This Solves

Fixes an issue where users could update or remove a configured environment value after an in-process Gateway restart, but the process kept using its old value. A full process restart applied the saved configuration correctly.

## Why This Change Was Made

Snapshot cleanup discarded environment ownership without removing the values it owned. The next startup could then mistake those surviving values for inherited environment settings. Keep that ownership through snapshot cleanup, fence old rollback transactions, and retain explicit full-reset behavior.

## User Impact

Accepted configuration changes can replace or remove their environment values after restart. Inherited environment values keep their precedence, and config-selected paths remain stable.

## Evidence

- A real Gateway using main dcc68718f5 accepts a replacement after a successful same-process restart, but public config.get still resolves the old value despite matching new applied/config revisions.
- On c7b393e10286b8b43c316cc7229f3bca94786493, the real Gateway completes restart, replacement, and removal. Public reads return the new value after replacement and the normal unresolved placeholder after removal. The inherited control stays unchanged throughout. Both config writes and final shutdown exit 0.
- Both runs use the real source CLI entry with synthetic config and normal token authentication. The candidate uses the Gateway's existing signed-device WebSocket client for public reads and the normal config set/unset CLI for writes. Complete private command, response, lifecycle, source, and resource receipts are retained. The interrupted baseline removal read is not claimed as proof.
- All 58 focused tests pass across runtime-snapshot.env.test.ts, config.env-vars.test.ts, and runtime-snapshot.test.ts. Controls cover late rollback, ambient precedence, aliases, casing, blocked variables, and explicit reset. Pinned formatting and focused lint pass.
- [Required CI passed](https://github.com/openclaw/openclaw/actions/runs/34136580481) on the exact candidate. No provider call or local build was needed for this proof.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix: pasted text is missing in model-locked Codex sessions (#142021)

* test(models): consolidate runtime policy matrix

* test(models): extract registry fixtures to satisfy lint

* fix: deliver pasted text to model-locked harness sessions

* fix(docker): restore source builds after dependency ownership guard (#142073)

* fix(docker): restore source builds after dependency ownership guard

* fix(docker): preserve existing lifecycle copy prefix

* fix(security): preserve existing WhatsApp group allowlists (#142589)

* fix(doctor): avoid installing unselected search providers (#142640)

Respect a normalized explicit web-search provider during plugin repair.
Only derive search plugin candidates from environment credentials when
no provider is selected, retaining independent plugin configuration.

Cover selection normalization, environment-only discovery, and existing
plugin disable and deny controls at the repair boundary.

* fix(doctor): migrate legacy Codex music model selectors (#143235)

Doctor left a legacy music selection such as `openai-codex/gpt-5.4` unchanged after `doctor --fix`. Its media selector scan and repair covered image and video but omitted music.

Include music in both existing loops. Doctor now reports the affected setting and saves eligible references under `openai`, using the same primary/fallback handling and provider blockers as the other media selectors.

Validation:

- Required CI passed on `51f0f308`: [run 34372705782](https://github.com/openclaw/openclaw/actions/runs/34372705782).
- Actual CLI proof reproduced the saved-state failure on baseline build `006aaacc` and verified the fix on candidate merge build `0b17e570`, which contains this PR head.
- Scalar and structured selectors passed repair, public config readback, and repeated repair. Suffixes, fallback order, explicit metadata, and canonical/custom references stayed intact.
- Non-interactive Doctor preview named the music setting without changing saved configuration. A blocked legacy provider retained its selection and authored settings while an eligible fallback migrated.
- All six regression cases passed in [the required CI job](https://github.com/openclaw/openclaw/actions/runs/34372705782/job/102537913364). They cover both legacy aliases, selector shapes, provider/model blockers, and agreement with image/video. The native pre-commit hook passed for all three files.

This completes the music-specific omission left after #142284. Proof uses synthetic configuration and covers stored references; it makes no live generation or authentication claim.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): preserve selected model metadata in diagnostics (#144332)

Keep active tool schema diagnostics on the primary model already selected
from manifest aliases. A second alias pass could use another model's
context window while labeling the tools with the selected model ID.

The regression reproduces middle selecting final's 4096-token context
instead of its own 32000-token context.

Related: #130706

* fix(discord): report parent channel denies for thread permissions (#121144)

* fix(discord): use parent permissions for thread diagnostics

Punchcard-Session: brisk-lantern-meadow-g5

* test(discord): preserve voice send diagnostics

* fix(discord): keep send diagnostics operation-specific

* fix(discord): share thread channel classification

* fix(config): avoid false model changes when saving settings (#144807)

* fix(doctor): preserve legacy registry files when quarantine fails (#144787)

Closes #144773

## What Problem This Solves

Doctor could delete malformed legacy sandbox registry data after its quarantine rename failed, then report a quarantine location that had not received the bytes. Both container and browser registry repair used the same failure path.

## Why This Change Was Made

The existing quarantine owner now reports success only after rename succeeds. It preserves the source and propagates other rename errors; a source that disappears maps to the existing missing result. Doctor retains its normal warning handling for this optional repair.

The regression cases now share the existing migration fixture and real SQLite writers. The separate mocked-writer fixture is removed, and the successful-quarantine test verifies the actual destination bytes.

## User Impact

Failed quarantine leaves the malformed bytes available for manual repair and reports the failure. Valid migration, existing SQLite rows, sharded precedence, read-only inspection, empty/missing inputs, and maintenance ownership retain their contracts. This changes no schema, configuration, retention policy, dependency, or overall Doctor exit policy.

## Evidence

Pinned main `1d4afe3966` was exercised through the built public `openclaw doctor --fix --non-interactive` command in isolated offline state. For each registry kind, a real nonempty destination directory caused `rename` to return `EISDIR`; the source was then unlinked and Doctor reported successful quarantine. Original synthetic input and destination bytes were preserved independently.

The corrected source was independently exercised through the same public repair for both registry kinds. The native filesystem trace still records `EISDIR`, but the source and existing destination remain byte-for-byte intact, and Doctor reports:

```text
doctor:sandbox run failed: EISDIR: illegal operation on a directory,
```

| Before | After |
| --- | --- |
| Failed rename was followed by source deletion and a successful-quarantine claim. | Failed rename preserves both inputs and reports failure without claiming quarantine. |
| The claimed destination had not received the malformed bytes. | After resolving the owned collision, repair moves the identical bytes to the reported file; a repeat leaves the result unchanged. |

Independent acceptance also verified real-clock quarantine, registered read-only lint, empty/missing inputs, and real SQLite migration with existing-row conflicts and mixed valid/invalid shards. Whole canonical rows, including JSON and update timestamps, remained unchanged; canonical rows take precedence over shards, and shards over monolithic input. No database writer was mocked in this public proof.

An initial fixture reused an old fixed timestamp and was correctly refused by maintenance before reaching quarantine. Its failed and prematurely batched dependent steps are retained. The corrected fixture uses a contemporaneous timestamp and stops dependent actions on nonzero repair exit. Product leases, workers, permission checks and deadlines were unchanged. The original baseline proof remains valid and was not replayed.

The consolidated regressions produced the intended four failures on baseline. All 34 focused tests across three files pass with the repair. Native formatting, syntax lint, line-size and assertion-safety checks, and the complete runtime/UI artifact build passed. Full type checks and broad suites remain hosted. The tested source tree matches published head `84dffef7a099a9918fd08ae63993dfef4df788d9`; signing changed no runtime input. The retained build keeps its original pre-signing stamp.

The source-disappearance test is a deterministic filesystem-spy primitive, not a public concurrent-process claim. No host-power-loss or elapsed lease-expiry behavior is claimed. Complete private proof and failure history are retained; publication omits account and infrastructure details.

Thanks @Alix-007 for the original repair and regression coverage.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix: retain route ownership while discovering conversations (#146927)

* fix(install): preserve runtime links when Node validation fails (#147221)

* fix(install): preserve runtime links when Node validation fails

* fix(install): preserve relative runtime targets and empty aliases

* test(install): preserve runtime fixture tuple types

* fix: doctor reports missing OAuth dir for channel config with no installed plugin (#147888)

Doctor no longer reports a missing OAuth directory or offers to create credentials storage solely because leftover channel configuration requests pairing for an unavailable plugin. Reuse the existing effective installed-plugin set in the state-integrity check, while preserving CRITICAL diagnostics and repair prompts for installed pairing plugins and explicit OAuth-directory overrides.

The missing-plugin regression fails with the production change reverted and passes after restoration. The complete state-integrity test file passes all 30 tests, including an explicit installed-Telegram CRITICAL assertion; exact-head CI is green.

Fixes #147772.

Thanks @zhangguiping-xydt for the implementation.

Co-authored-by: Peter Steinberger <steipete@gmail.com>

* fix(doctor): stopped private inputs replay after session repair (#148625)

* fix(doctor): retain private input receipts during canonical repair

* test(doctor): use lint-safe receipt rekey expectation

* fix(channels): settle task-scoped context leases once (#148922)

* fix(push): normalize malformed proxy auth errors (#149112)

* fix(cron): reject invalid stagger before silent schedule changes (#151740)

Closes #146446

## What Problem This Solves

Fixes raw Gateway cron requests reporting success after silently discarding an invalid explicit `staggerMs`, substituting a default or retaining the previous value while other requested changes commit.

## User Impact

Invalid explicit stagger values now return `INVALID_REQUEST` before creating or updating a job. This includes explicit `null`: the existing schedule schema permits an optional integer, not a nullable value. Supported integer strings, zero, numeric flooring/clamping, omitted values, and ordinary CLI duration parsing retain their behavior. No operator migration is required; Doctor's existing legacy schedule repair is unchanged.

## Why This Change Was Made

The shared schedule normalizer rejects an invalid authored value before create defaults or update retention erase it. The fix uses the existing parser and request-error boundary rather than broadening the parser or adding a second validation policy. Non-cron schedules still discard stale stagger fields.

## Evidence

The authenticated comparison used Linux arm64 and Node 24.19.0, with registered `openclaw gateway call` commands, a task-only token, actual Gateway handlers, and SQLite readback. Baseline: `bef4c20d45`. Candidate: that same main revision plus this PR's two-file delta from `f9be3cb73438f6f68c4ead73103d413564398210`—not an execution of the full original PR checkout.

| Request | Baseline | Candidate |
| --- | --- | --- |
| Hourly add with `"1e3"` | Success; stored `300000` | `INVALID_REQUEST`; no mutation |
| Non-hourly add with `"1e3"` | Success; stagger omitted | `INVALID_REQUEST`; no mutation |
| Rename/update with `"42.8"`, same expression | Success; rename committed and previous stagger retained | `INVALID_REQUEST`; no mutation |
| Rename/update with `"42.8"`, changed expression | Success; rename/expression committed with default stagger | `INVALID_REQUEST`; no mutation |

For each rejected candidate request, all cron-table rows and authenticated views remained unchanged. All 20 expected-observation checks passed on each side, including integer strings, numeric/string zero, omitted add/update behavior, numeric floor/clamp, non-cron stale fields, explicit `null`, and a wrong-token control. Physical candidate restart preserved authenticated `cron.get` responses and complete serialized cron-table values.

Five focused files passed **167 tests**, covering normalization, stagger behavior, persisted schedules, and Doctor legacy schedule repair. With the original normalizer and the same regression tests, **8 failed and 95 passed**, demonstrating the invalid-input regression. Doctor coverage is focused-test evidence, not a published-updater upgrade test.

The initial fixture attempts are not product failures: the first stopped before mutation checks because native Workshop monitors were enabled in the fixture. Baseline restart initially compared `cron.list` projections with `cron.get` and then compared SQLite null-prototype records with JSON objects. Retained same-API responses and exact serialized row values confirmed persistence without replay; the failed fixture results remain retained.

During the completed comparison, scheduling and all jobs stayed disabled; native Workshop monitor rows were retained disabled. No scheduling timers, jobs, provider requests, channel delivery, or execution-delay measurement were exercised.

Existing hosted CI is not all green: [run 35338846156](https://github.com/openclaw/openclaw/actions/runs/35338846156) reports websocket transcript-event timeouts in `session-message-events.test.ts` in `checks-node-compact-large-4`, with the aggregate CI gate failing. No CI replay or unrelated repair is included in this PR.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(cli): keep model validation out of session execution (#154763)

* fix(agents): treat empty credential environment variables as unresolved (#155558)

Closes #153484

## What Problem This Solves

The session credential resolver returned the environment variable name when the variable existed but was empty.

## Why This Change Was Made

Distinguish a present empty variable from an absent variable in cached and uncached resolution. Empty values remain unresolved; non-empty variables override literals; absent names retain literal fallback.

## User Impact

SDK consumers of `openclaw/plugin-sdk/agent-sessions` no longer receive the variable name as credential/header material for an explicitly empty variable. Required headers use the existing resolution error. This is verified SDK hardening, not a claim that normal CLI provider headers use this resolver.

## Evidence

- Exercised the built public `ModelRegistry` export with an authored models.json provider header. The export is present in release v2026.9.5. Before the fix, empty returned the variable name; after the fix, it returned the existing failed-to-resolve-header error.
- Non-empty resolved the synthetic value; unset preserved the literal name. Repeated after integrating current main.
- Focused resolver suite: six tests passed; command wall 14.43s including worker preparation, test cases 15ms total.
- Simplification kept the two existing resolver owners and corrected the absent-variable test to exercise the deliberately unset name. No new abstraction/config/schema.
- CLI models probes and local agent requests were also inspected but use a different header path; they are not evidence for this fix.

## Compatibility

Shell command resolution and unset-name literal fallback are unchanged.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): apply ambient-owner fallback to dream diary and all memory target handlers (#156437)

Apply the existing ambient-owner fallback in the shared Doctor memory target resolver, matching doctor.memory.status. Older clients that omit agentId can read the dream diary and run the five maintenance handlers when a configured owner is unambiguous. Explicit selection still wins, and ownerless multi-agent fleets still receive the selection error.

Thanks @Orionation for the fix and @Cyb3rb1ade for reporting the legacy-client failures. The configured-owner case is addressed; selecting an agent remains necessary for ownerless fleets.

Validation: scoped review found no accepted/actionable P0 or P1 findings, the author supplied a resolver comparison, and exact-head hosted CI passed. No fresh after-fix Gateway trace was captured for this landing. Production +6/-1; tests unchanged.

Closes #138369

Co-authored-by: Jonathan Sieling <jonathan@tailoredmonkey.com>

* fix(sandbox): unblock scoped recreation across runtime targets (#157808)

* fix(sandbox): unblock scoped recreation across runtime targets

* test(sandbox): validate mocked process arguments

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

* fix(security): parse gpt generation numbers in the audit tier check (#140012)

Fix security audit warnings that incorrectly classify numbered GPT-6 models as below GPT-5. Compare numeric generations in the shared model-hygiene classifier while preserving older-model warnings and Azure's legacy gpt-35-turbo naming contract.

The repair covers primary models, fallbacks, image models, and agent overrides without changing provider routing, authorization, or unknown-name advisory policy. Broader unknown-name behavior remains separate in #160580.

Fixes #139751
Thanks @holny for the fix, regression coverage, and isolated CLI evidence.

Co-authored-by: holny <holny@foxmail.com>
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>

* fix(doctor): report shared auth health without a default agent (#134902)

* fix(doctor): report shared auth health without a default agent

* fix(ci): stabilize tooling timing and fixture cleanup

Use fake time for the existing readiness-body polling test, preserve explicit
success and delegated return statuses in Bash 5.2 EXIT cleanup, and remove a
repeated seven-compiler declaration invalidation build already covered by
the shared SDK writer.

Validation: 146 readiness/fixture tests and two real declaration-build cases
pass, along with 15 shell controls across Bash 3.2, 5.2, and 5.3. The scoped
changed gate and Codex review pass.

* fix(doctor): converge redundant shared auth relocation subsets (#135096)

* fix(doctor): converge redundant shared auth subsets

Preserve richer canonical credential stores and original source receipts through cleanup. Keep differing credentials and state rows fail-closed with actionable diagnostics. Reported by @jodok and independently reproduced by @felixboenkost-droid in #132605.

* fix(doctor): satisfy shared auth relocation validation

* fix(doctor): report retained device-auth files (#133978)

Surface retained client device-auth JSON in ordinary Doctor output and opted-in
lint checks, including when identity, pairing state, or Gateway health is absent.
Resolve only retired-file diagnostics and profile-aware repair hints from the
original health context environment; keep pairing and token reads on the active
state view.

Describe remaining migration or cleanup debt without inferring import history.
Keep the legacy-file token guard and Doctor-owned migration unchanged, share
friendly and structured findings, and remove redundant string-formatting paths.

Refs: #127973, #133978.
Reported by @yetval.

Co-authored-by: yetval <yetvald@gmail.com>

* fix(update): stop detached updates after chat ownership is revoked (#137312)

* fix(update): recheck chat ownership before detached update launch

* test(update): keep helper owner cases in lifecycle support

* docs(update): describe the implemented session-less notice route

* test(update): match conditional Vitest API in owner cases

* fix(doctor): inspect source auth stores during full lint (#137610)

* fix(doctor): inspect source auth stores during full lint

* test(ci): freeze release candidate fixture clock

* refactor(doctor): simplify scoped environment restoration

* test(release): preserve elapsed time in fixture clocks

* test(auth): join migration fixture callbacks before teardown

* fix(gateway): recover queued RPC replies across restart (#138565)

Bind RPC and scheduled lifetime resolvers to their exact Gateway host. Reuse the canonical host for HTTP hooks and worker dispatch so shutdown retains pending reply recovery without weakening execution fences.

Fixes #138564

* fix(doctor): preserve legacy ownership during config repair (#138837)

Carry the retained default-agent owner through legacy migration, persist the
canonical roster through the config writer, and verify startup repair with
that same ownership preparation. This prevents a migratable legacy fleet
from being replaced by stale last-known-good configuration during update.

Verify the actual persisted roster and retained settings, keep ambiguous
owners rejected, and document migration before recovery.

Validation: 77 owner tests, changed checks, and independent P0-P2 review.
Release-note context: preserves update settings and legacy default-agent
ownership through automatic Doctor config migration.

* fix: prevent browser profile permission hangs in Doctor (#139612)

* fix: prevent browser profile permission hangs in Doctor

* docs: explain explicit browser registration repair

* fix(gateway): prevent systemd restarts from hanging (#140914)

With OPENCLAW_NO_RESPAWN enabled, a managed restart could reopen the Gateway inside the original process while systemd waited for it to exit. Use the existing external-restart action to drain, close, retain the recorded reason, and exit so the service manager creates the successor.

Verified through real Linux user-systemd restart, all five drain modes, ordinary stop/start, unmanaged SIGUSR1, independent runtime acceptance, and 384 focused lifecycle controls. Preserve existing managed-update ownership and service settings.

Thanks to @rboy1 for the report. Closes #140821.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): recover legacy node tokens with invalid scopes (#143599)

Doctor detects legacy node tokens that inherited operator scopes, but its suggested rotation preserves those scopes and is denied. Add `devices rotate --no-scopes` and use it in Doctor's node recovery guidance. Rotation removes only the matching stale local node credential in the same transaction, while preserving operator pairing, approved scopes, and existing authorization and token-delivery rules.

Reproduced through public pairing on v2026.3.11, then upgraded the same isolated state to baseline `ac5ba7d738`. The corrected command succeeds, removes the matching cache, and preserves operator access through restart and node reconnect. Omitted scopes retain their existing meaning; `--scope` and `--no-scopes` conflict.

Validation:
- 203 focused CLI, Doctor, pairing, credential-store, and Gateway authorization tests.
- Formatter, syntax lint, size/assertion guards, and a full scenario build.
- Independent public CLI and signed-protocol acceptance: recovery, rejected scope/role changes, distinct-device bearer withholding, self-token delivery, revocation, interrupted response, node restart, hosting-state reset, and operator-rotation continuity.
- Fresh security-aware source review. Hosted CI and the exact-head landing gate run on this PR.

The real node fixture used Linux. Native macOS executable discovery was not run; existing real-store tests cover the shared generation reset. No worker session was launched.

Fixes #143263. Thanks @matthewmoroz for the report.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(skills): refresh session snapshots on gateway restart (#122110)

Closes #122107

With skill watching disabled, a persisted session can keep its old skill catalog after `openclaw gateway restart`. The unmanaged Gateway restarts in the same process, so the skill snapshot generation does not advance. The next turn still receives the old skill description even though the file changed.

Advance the existing skill generation at Gateway startup, before serving turns. This refreshes both snapshot caches on the same session's next turn. Keep the current startup and shutdown owners, consolidate the contributor's two-start regression into the maintained boot suite, and document same-session refresh. Managed library selections keep their pinned revisions.

- Built public Gateway and agent CLI: main retained the old description after a completed same-process restart; the candidate delivered the revised description to the same session. Complete provider requests and public JSON confirm the catalog content. The provider was a deterministic local fixture, not a live model service.
- Default watcher, unchanged skill, configuration reload, session/history continuity, and deferred/coalesced safe restarts were exercised through public commands. Bare unauthorized restart signals remained rejected on main and candidate. The operator CLI's existing intent-based behavior with the flag disabled was compared separately and was unchanged.
- The candidate's coalesced restart used the existing forced HTTP connection-close fallback after its one-second grace period; all turns and history survived. The matched baseline closed cleanly. This proof does not establish equal close timing or warning frequency.
- The startup regression fails on main for the missing generation advance and passes with the fix. 252 focused tests cover startup, snapshots, watchers, remote-node refresh, config invalidation, workshop lifecycle, cache pruning helpers, and restart ownership/fallbacks. Targeted lint, formatting, assertion/line-count checks, and documentation links pass.
- The normal watcher already refreshes changed skills on main. This repair covers startup invalidation independently of watcher events. Older closed reports #22517, #22525, and #22568 remain overlap history; their broader symptoms are not claimed resolved by this proof.

Preserves @sappkevin's four commits and original repair intent.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(doctor): session SQLite import drops Codex assistant messages from legacy transcripts (#140296)

Fixes #140100.

Preserve legacy assistant events during Doctor SQLite import by deferring
provider-metadata rewrites until the staging cursor is exhausted. Keep
byte-exact replay checks against normalized prior rows and reuse the existing
per-repair scratch set; no persistent schema or archive policy changes.

The importer and Doctor regressions cover nonterminal assistant rows. Existing
exact-head CI and the contributor's Linux CLI proof show all six events
preserved in order instead of four. Fresh maintainer review found no actionable
issue. Recovery into an already-partial destination still does not restore its
original physical order or active-history projection; that limitation remains
explicit in the PR evidence.

Co-authored-by: Yalçın Doksanbir <yalcindoksanbir@gmail.com>

* fix(cron): forward claude-cli auth profile on scheduled runs to prevent OAuth expiration (#144266)

* fix(cron): forward claude-cli auth profile on scheduled runs

* fix(agents): resolve managed CLI auth for queued runs

Use the existing CLI credential owner for queued candidates, preserving explicit account boundaries and native login. Isolate persisted cron auth fixtures.

Completes the managed CLI credential handoff for cron and queued runs described in openclaw/openclaw#144266 and openclaw/openclaw#144047.

---------

Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>

* fix(discord): preserve sender-owned line limits and reply scope (#137969)

Co-authored-by: OpenClaw Assistant <assistant@openclaw.local>

* fix(install): never replace or break an existing nvm during installation (#145317)

Preserve compatible active Node runtimes and existing nvm installations in install.sh, the website installer synced from this repository. The installer no longer replaces system Node, rewrites shell profiles, or writes an npm prefix that breaks an existing nvm. With nvm present, reuse a compatible installed Node or ask before installing one; unattended runs that need preparation stop with the exact commands.

The 220-test installer suite and real Debian before/after proof cover preserved system Node, npm configuration, profiles, nvm defaults, and working nvm use. Exact-head CI is green. Website Installer Sync propagation remains a post-merge release-ops follow-up.

Fixes #145292. Thanks to @jb-lopez for the report and diagnostics.

* fix(sessions): recover omitted historical transcripts in Doctor (#120781)

* fix(sessions): stop doctor archiving live transcripts as orphans (#73471)

`openclaw doctor` treats a transcript as an orphan unless its path is the
CURRENT `sessionId` of some entry in `sessions.json`. That is not what a
session store holds, so the scan reports live history as unreferenced and
offers to archive it. Renamed files are then hard-deleted 30 days later by
`cleanupArchivedSessionTranscripts`.

Two independent causes:

1. `resolveSessionFilePath` can only rebuild `<sessionId>.jsonl` unless the
   entry carries `sessionFile`, and `sessionFile` is persisted for the live
   session only. Transcripts generated as `<iso-stamp>_<sessionId>.jsonl`
   therefore never resolve for any *previous* session in a compaction chain.
   Falls back to a name-based lookup via the existing
   `extractGeneratedTranscriptSessionId` helper, only when the canonical path
   is absent, so a brand-new session still gets the plain name.

2. The orphan scan reads `entry.sessionId` alone, ignoring
   `usageFamilySessionIds` (every prior id in a compaction chain),
   `compactionCheckpoints[].sessionId` and `systemPromptReport.sessionId`.
   Referenced ids are now collected from all four and matched against the
   session id parsed out of each file name.

Measured on an install with 84 primary transcripts: 58 reported orphans
before, 0 after — every one was a false positive.

Both tests fail on the unpatched build.

* fix(sessions): recover omitted historical transcripts in Doctor

* style(sessions): satisfy historical recovery lint guards

* fix(sessions): retain archived shared aliases without reclassification

* test(ci): include historical discovery in Doctor ownership plan

* fix(doctor): admit transcript-only migration targets

---------

Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>

* fix(update): recognize custom npm prefix installations (#146091)

Recognize npm ownership for custom prefix installations such as nvm with ~/.npm-global when global-root probes disagree and the prefix has no npm executable. Use npm's configured prefix and the installed OpenClaw launcher, share the resolver with update status, and include inspected paths in unknown-owner refusals. Windows npm shims resolve their package entrypoint instead of the earlier node.exe check.

Keep update target selection on the running package prefix. Older installed updaters still need the explicit NPM_CONFIG_PREFIX workaround for the first hop. Full published-driver-to-candidate upgrade preservation remains unverified; focused ownership, target-selection, status, and CLI admission tests pass, including the failing-before/passing-after complete Windows shim regression.

Fixes #146038

Reported by Discord user Rex Horizon in the support thread "As usual update fails" (#146038).

* fix(context): preserve provider-scoped prompt budgets before reply maintenance (#141052)

* fix(context): preserve provider-scoped prompt budgets before reply maintenance

* fix: preserve literal identities in reply catalog selection

Keep configured and prepared rows distinct when their display keys collide, and prefer an exact model ID before alias matching. Covers both selected IDs and row orders.

* test: align prepared budget coverage with automatic model policy

* test: avoid shadowing expected context window

---------

Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>

* fix(sessions): prevent Doctor and startup memory exhaustion (#149704)

* fix(sessions): bound legacy migration transcript reads

Prevent openclaw doctor --fix, openclaw update, and Gateway startup
from exhausting memory on large single-agent stores. Read only
legacy-targeted claims and share a streamed digest across inspection,
copy staging, and transactional source cleanup.

* fix(sessions): isolate the transcript digest contract

Keep lifecycle types independent of the SQLite reader and session scope
to remove the type-import cycle caught by architecture CI. Preserve the
streamed digest, copy validation, and transactional deletion behavior.

* fix(daemon): preserve inline auth through service upgrades (#149686)

Keep active env SecretRef values already present in legacy service environments and migrate them through the existing owner-only environment-file writers. Refs #111578.

* fix(update): verify paired trusted-proxy gateways with service credentials (#153540)

* fix(update): verify paired trusted-proxy gateways with service credentials

* refactor(gateway): keep call identity resolution with device auth

* test(update): isolate restart polling from native credential storage

* fix: background continuations lose session access and GitHub tools (#141865)

* fix: background continuations lose session access and GitHub tools

Worked on by:
- @Takhoffman

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
OpenClaw-Publication: 867311a3-8ac4-4412-9b5d-e45028eed55e

* fix: preserve Windows launcher line endings during publication

Remove the unrelated line-ending normalization introduced by the publication snapshot. The launcher is byte-identical to the PR base.

Worked on by:
- @Takhoffman

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

* refactor: isolate background GitHub availability preparation

Keep the shared attempt dispatcher within its existing line limit by extracting
the bounded availability stage. Preserve adopted session identity, explicit
capability values, lazy loading, and the canonical live-attempt guard.

Worked on by:
- @Takhoffman

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

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: brokemac79 <255583030+brokemac79@users.noreply.github.com>
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>

* fix(doctor): decode session headers across read chunk boundaries (#154034)

Doctor could skip stale transcript-path repair when a valid session header extended past its fixed 8192-byte read window. Read the complete first line with bounded incremental UTF-8 decoding so long headers and split multibyte characters preserve the existing session identity and collision safeguards.

Release-note context: restore Doctor's repair of stale session transcript paths for headers that cross a read chunk boundary. No configuration change or operator action is required. Thanks to @xydt-juyaohui for the fix.

Evidence: the existing regression fails before the fix and passes afterward (8 tests passed), and the reported Windows migration-owner flow repairs both short and boundary-split headers. The approved head has green hosted CI and a scoped-clean Codex review. Published-updater-to-candidate proof and per-file timing measurements remain unrun in this landing lane.

Follow-up: header parse failures still skip silently; record an explicit Doctor non-outcome in follow-up work.

Co-authored-by: 琚耀辉0668001366 <ju.yaohui@xydigit.com>

* fix: faulty compaction probe wedges run steering and restart aborts (#154868)

Closes #154700

Fixes: a failing active-run compaction probe escapes message steering and interrupts restart cancellation of other compacting runs. Reply-owned runs also bypassed the guarded registry path.

User impact: an unreadable compaction state refuses steering safely, chat follow-ups remain queued until the active run finishes, and compacting-mode cancellation continues to other eligible runs. Healthy steering is unchanged. No configuration, permissions, storage, or public API changes.

One contained probe reader now serves embedded queue admission, reply-owned injection, and both restart-abort owners. An indeterminate state rejects steering and skips that handle during compacting-only cancellation. A weak set records the first probe failure per handle so repeated admission checks do not flood logs or retain finished runs.

The existing abortability and supersede readers remain beside the compaction reader in the contributor's extracted module. Reply-owned handles use the same containment instead of another exception boundary.

Isolated Gateway with a plugin-owned active run and a scripted local HTTP provider; the plugin's `isCompacting()` throws. No product source was altered to inject the fault.

- **Before, main `9665d17902cf`:** `sessions_send(mode=steer)` surfaced the raw `compaction probe unavailable` exception. A `chat.send(queueMode=steer)` follow-up was incorrectly delivered to the faulty active backend; the provider recorded the injection.
- **After:** the same tool call returns the existing structured `queue_message_failed reason=compacting ... gatewayHealth=live` rejection. Chat follow-ups are not injected into the faulty backend; the provider sees the next request only after the first finishes. Both turns complete successfully. Repeated checks across the owners produce one warning for that handle.
- **Control, before and after:** a healthy probe accepts the follow-up with `targetDisposition=steered`, and the provider records that exact message.
- **Lifecycle:** the isolated Gateway restarts and answers `health` successfully after the runs settle. The contributor's live queued-compaction regression also proves that a faulty preceding handle does not prevent cancellation, settlement, and registry eviction of the eligible compaction. The reply-registry regression covers the corresponding reply-owned abort sweep.

Focused checks after merging main:

```text
runs.test.ts + runs.steering.test.ts: 69 passed
reply-run-registry.test.ts: 105 passed
Combined wrapper wall time: 30.34 s

compact.hooks.test.ts, faulty-probe case: passed (660 ms test body)
reply-registry throwing-probe and compacting-abort cases: 3 passed
Combined focused wrapper wall time: 30.09 s
```

The CLI lifecycle import-boundary check exposed an eager diagnostic-facade import from the new shared reader. The reader now imports the same logger directly from its lightweight owner. The existing lifecycle check passes all 3 cases, including signal handling after chunk rotation; rerunning it with both steering/reply owner suites passed 153 tests in 29.23 s without changing test mocks.

The live proof uses synthetic local model responses, not an external model provider. It distinguishes a contained rejection from a raw exception; it does not claim that the baseline Gateway process itself crashed.

Co-authored-by: sxh <sunxianhong@ncti-gba.cn>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>

* fix(ui): publish rejected goal operations (#156363)

* fix(commands): deduplicate session lifecycle keys (#158487)

* fix(plugins): preserve runtime artifact preference (#158487)

* fix(agents): strip private skill authoring from public ingress (#159220)

* fix(plugins): preserve include ownership during uninstall (#159945)

* fix(channels): restore system-agent approval reactions (#159132)

* fix(moonshot): reject inherited thinking-model keys (#158437)

* fix(tencent): ignore inherited effort-map keys (#158437)

* fix(workers): observe already-aborted operation promises (#158228)

* fix(gateway): reject non-object Claude history rows (#158228)

* fix(copilot): recover pool after synchronous factory failures (#157819)

* fix(tlon): preserve IPv6 ship origins (#157825)

* fix(sms): clean revoked inbound media (#157825)

* fix(slack): preserve replacement webhook registrations (#158085)

* fix(matrix): accept system-agent approval reactions (#158164)

* fix(channels): retain typing tick exclusivity across restart (#158830)

* fix(acp): format unset optional tool arguments (#159417)

* fix(meeting): clean up partial audio startup (#157982)

* fix(exec): retain prepared plugin environment (#158378)

* fix(mxc): clean temp directory after bridge failure (#158532)

* fix(discord): preserve batched native reply identities (#158886)

* fix(skills): count omitted dangerous exec aliases once (#158906)

* fix(sandbox): preserve remote mutation path bytes (#159346)

* fix(browser): accept standard CDP header containers (#159430)

* fix(plugins): preserve sparse catalog preferences (#159945)

* fix(doctor): preserve complete plugin inventory (#153901)

* fix(fs): preserve bounded filesystem correctness (#157524)

* chore: prepare extended-stable 2026.8.34

* docs: add 2026.8.34 release notes

* fix(fs): use typed errno checks for JSON symlinks

* chore: shrink assertion safety baseline

* fix: integrate cumulative 2026.8.34 backports

* fix: integrate release candidate validation repairs

* fix: close release review findings

* fix: repair cumulative backport integration

* fix: preserve system-agent channel custody

* fix: complete stable backport integration

* fix: deliver system-agent approvals to native channels

* fix: align native approval architecture checks

* fix: fence delegated approvals at final effect

* fix: close delegated approval final effects

* fix: fence delegated config publication

* fix: fence delegated config backup effects

* test: prove delegated config final effects

* test: bound delegated approval proof wait

* fix: fence every delegated config effect

* fix: close delegated config I/O races

* fix: preserve delegated backup boundaries

* fix: keep delegated include writes root-bound

* test: await delegated approval publication

* fix: reject unfenced delegated include writes

* test: register delegated include todo suppression

* fix: reject delegated includes before preflight

---------

Co-authored-by: ToToKr <friendnt@g.skku.edu>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: IvanShang <77005282+qdivan@users.noreply.github.com>
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: Yuval Dinodia <102706514+yetval@users.noreply.github.com>
Co-authored-by: Yzx <53250620+849261680@users.noreply.github.com>
Co-authored-by: 0xParzival <145645180+0x-Parzival@users.noreply.github.com>
Co-authored-by: 0x-Parzival <0x-Parzival@users.noreply.github.com>
Co-authored-by: chelsealong <chelsealong@126.com>
Co-authored-by: Alexander Malysh <amalysh@kannel.org>
Co-authored-by: Alexander Malysh <malysh00@gmail.com>
Co-authored-by: Alexander Malysh <a.malysh@venista.com>
Co-authored-by: wantosure <wantosure@126.com>
Co-authored-by: Solomon Neas <41877493+solomonneas@users.noreply.github.com>
Co-authored-by: Solomon Neas <me@solomonneas.dev>
Co-authored-by: ericcaiwx-star <ericcaiwx@gmail.com>
Co-authored-by: Teddy Tennant <teddytennant@icloud.com>
Co-authored-by: Yiğit ERDOĞAN <yigiterdogan023@gmail.com>
Co-authored-by: Vyctor H. Brzezowski <krzyszchweski@gmail.com>
Co-authored-by: qingminlong <qing.minlong@xydigit.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: holny <holny@foxmail.com>
Co-authored-by: holny <12433219+holny@users.noreply.github.com>
Co-authored-by: Altay <altay@hey.com>
Co-authored-by: Doksanbir <ylcn91@users.noreply.github.com>
Co-authored-by: coderdailyone <128658232+coderdailyone@users.noreply.github.com>
Co-authored-by: coderdailyone <codecookie@proton.me>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Agustin Rivera <31522568+eleqtrizit@users.noreply.github.com>
Co-authored-by: Alix-007 <li.long15@xydigit.com>
Co-authored-by: xingzhou <zhang.guiping@xydigit.com>
Co-authored-by: Orion <orionsieling@gmail.com>
Co-authored-by: Jonathan Sieling <jonathan@tailoredmonkey.com>
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: NianJiu <3235467914@qq.com>
Co-authored-by: sappkevin <109696421+sappkevin@users.noreply.github.com>
Co-authored-by: Yalçın Doksanbir <yalcindoksanbir@gmail.com>
Co-authored-by: MasterSwords1 <142840981+MasterSwords1@users.noreply.github.com>
Co-authored-by: JC <anyech@users.noreply.github.com>
Co-authored-by: OpenClaw Assistant <assistant@openclaw.local>
Co-authored-by: leochun92-sudo <295001307+leochun92-sudo@users.noreply.github.com>
Co-authored-by: Mike <5853861+snotty@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: RoboClaw <services+roboclaw@openclaw.org>
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: brokemac79 <255583030+brokemac79@users.noreply.github.com>
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: juyaohuidt <ju.yaohui@xydigit.com>
Co-authored-by: sxh <xxh517598@gmail.com>
Co-authored-by: sxh <sunxianhong@ncti-gba.cn>
2026-09-29 12:04:32 -07:00
..
oxlint feat(lint): enforce no-chained-type-assertions (#124119) 2026-08-15 02:13:37 -07:00
tsconfig ci: keep plugin lint in its nearest TypeScript project (#134216) 2026-08-31 09:34:21 -07:00
assertion-safety-baseline.txt chore: prepare extended-stable 2026.8.34 (#160960) 2026-09-29 12:04:32 -07:00
ci-test-timings.json fix(ci): reuse Swift build caches and balance slow test groups (#134493) 2026-08-31 15:41:59 -07:00
control-ui-startup-budget-baseline.json fix(update): preserve automatic update policy and outcomes (#134566) 2026-08-31 18:52:14 -07:00
env-var-count-budget.txt fix(anthropic): preserve cache prefixes across runtime context (#157650) 2026-09-25 00:24:17 -07:00
knip.all-exports.config.ts fix(agents): preserve provider ownership in error diagnostics (#134029) 2026-08-31 06:42:14 -07:00
knip.config.ts perf(ci): overlap owned fixtures and refresh UI shard timings (#134459) 2026-08-31 14:09:57 -07:00
knip.scripts-exports.config.ts perf(ci): overlap owned fixtures and refresh UI shard timings (#134459) 2026-08-31 14:09:57 -07:00
markdownlint-cli2.jsonc
markdownlint-templates.jsonc fix(tooling): run markdownlint on workspace templates (#91160) (#117166) 2026-08-28 12:21:13 -07:00
max-lines-baseline.txt feat(update): clean up retained migration recovery files (#133864) 2026-08-31 08:15:29 -07:00
shellcheckrc
stylelint.config.mjs perf(control-ui): load built-in theme palettes on demand (#130473) 2026-08-26 18:56:14 -07:00
swiftformat chore(swift): restore compact conditional bodies (#103832) 2026-07-10 18:12:00 +01:00
swiftlint.yml refactor(scripts): migrate JavaScript tools to TypeScript (#121005) 2026-08-09 07:21:35 -07:00