Keep Control UI PR badges and publication progress aligned with scoped GitHub metadata and recorded Gateway outcomes. Separate status reads from publication, recover shared receipts after reconnect, and preserve explicit personal confirmation and unknown-outcome fences without another polling loop.
Retain unbound legacy terminal receipts as history without blocking account choices or inventing lifecycle bindings. Cover retained-state upgrades and complete the qualified-owner options test contract.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
* fix(memory): retain worker session export retries
Reject after two obsolete redaction snapshots instead of exporting on the
Gateway thread. Record resolved targeted sessions before preparation so a
failed export retains pending work for generic retry. Full rebuilds continue
to preserve the published index.
Verified both regressions against independent controls and all four ordinary
redaction cases against the final code.
* test(memory): isolate queued retry recovery from dirty state
Use the existing maintenance handoff so ordinary dirty-file retries cannot rescue a dropped queued request. Preserve every queue recovery assertion.
Closes#146939. Related: #140984.
## What Problem This Solves
Fixes `memory_get` returning a different file from `memory_search` when an explicitly owned multi-agent roster relies on inherited workspace paths.
## User Impact
Search results and subsequent reads agree on the agent's workspace without requiring a per-agent workspace pin. Explicit workspace overrides remain authoritative. No files move, no index reset or storage migration is required, and Markdown containment and read limits are unchanged.
For explicitly owned rosters, an unpinned first agent no longer implicitly reads the parent workspace merely because it appears first. A valid retained migration owner and sole-agent inheritance still follow canonical behavior. Raw reader inputs without explicit ownership keep their existing legacy behavior.
## Why This Change Was Made
The prior path repair in #140984 deliberately deferred workspace-ownership alignment. This PR proposes only that bounded alignment: core and explicitly owned memory-host reads share the compatibility-owner decision through a pure internal helper. The existing private path bridge avoids importing the full agent runtime. The original config object is preserved for retained migration-owner lookup; optional legacy reader IDs remain accepted.
### Maintainer choices — proposed, not previously approved
1. **Accept explicit-roster alignment (recommended and implemented).** Search's canonical workspace ownership is authoritative for reads too. Alternatively, retain the mismatch and require explicit workspace pins; this PR does not silently change search to match the old getter.
2. **Preserve legacy reader compatibility (implemented).** Inputs without `ownership: "explicit"` retain first-agent/default-marker inheritance. Broader normalization of those inputs is intentionally deferred, rather than changing the shipped reader contract in this fix.
3. **Keep adjacent policy out of scope (recommended).** No context-limit merging changes, shared-system-agent workspace redesign, public SDK entrypoint, or broader path allowlist. Those require separate decisions.
ClawSweeper's source review supports this direction but requests a compatibility decision; it is not maintainer approval. Please review the ownership choice before merge. Rollback is reverting this change; the issue's explicit-workspace-pin mitigation remains available.
## Evidence
### After-fix real Gateway proof — September 13, 2026
Built candidate `be5a589a0ee148b54c51f6876ef5281ee9b29cb5`, Node 26.8.1/macOS. Used a loopback-only Gateway, registered Memory Core tools through HTTP `POST /tools/invoke`, synthetic files and real on-disk SQLite state, `provider: none`, and vectors disabled. No test-helper imports, provider calls, or production configuration/state changes.
| Setup | Actual search → get result |
| --- | --- |
| Fresh explicit two-agent roster, neither agent pinned; parent contains a decoy | main: `USER.md`, `status=ok`, `Orchid proof main`; other: `USER.md`, `status=ok`, `Orchid proof other` |
| Fresh setup, parent traversal for each agent | `MEMORY_PATH_NOT_ALLOWED` |
| Installed 2026.9.3, existing explicit workspace pins | main/other: `USER.md`, `status=ok`, matching `Orchid upgrade main` / `Orchid upgrade other` |
| Same shipped-created state after candidate Doctor migration and candidate Gateway startup | Both agents return those same markers with `status=ok` |
For each agent, passed the search result's path/start line/inclusive line count into `memory_get`. Stopped the shipped Gateway before switching binaries. Candidate initially refused the older session-identity database; followed its supported `doctor --fix --non-interactive` instruction (exit 0, both agent databases v19→v20), then repeated the actual HTTP flow. Both existing workspace pins were preserved exactly, and SHA-256 checks showed all three synthetic `USER.md` files unchanged. Doctor did change normal config metadata/defaults; the whole config was **not** unchanged. All proof Gateways were stopped.
This is fresh-runtime and shipped-created-state/Doctor migration proof, **not** an installer, `openclaw update` driver, or live-user upgrade test. Retained legacy-owner shapes beyond these fixtures remain covered by focused tests, not claimed as live proof. The schema migration belongs to the candidate's existing upgrade path; this six-file patch introduces no schema or embedding-metadata changes. Maintainer acceptance of explicit-roster read-root alignment remains required.
CI at this evidence update: the model-login catalog timing case in [large-26](https://github.com/openclaw/openclaw/actions/runs/34761804381/job/103735936253) failed, making the aggregate gate red. Its fixture disables memory and uses one pinned agent without explicit ownership; likely unrelated, but no matching base failure was verified. No CI pass is claimed.
- Focused validation: 162 tests passed across six files (one existing skipped test), including the real-manager tool regression and package-boundary contracts.
- Final-head `node scripts/check-changed.mjs` passed: core and test types, core/extension lint, formatting, dead-export scans, package/SDK boundaries, and runtime import-cycle checks. `git diff --check` passed.
- Full `pnpm build` passed in 3m26s, including plugin SDK exports, CLI bootstrap imports, built plugin-loading checks, and Control UI build. After that build, the only patch change moved an unchanged re-export below imports for lint; the rebase added only unrelated ACP tests. The repository-wide test suite was not run.
- Tests-first reproduction on upstream main: both roster orders returned the parent decoy through `memory_get` after real SQLite-backed `memory_search` found the agent marker.
- The regression exercises both agents, both roster orders, returned path/line readback, and rejection of parent traversal. It uses temporary files and databases with embeddings disabled; no provider request or private installation data.
- Compatibility coverage includes explicit workspace overrides, retained/removed migration owners, ignored legacy markers under explicit ownership, optional legacy IDs, sole/duplicate agents, and absent/empty rosters. Existing legacy path, file-reader, core ownership, and package-boundary coverage is retained.
- Independent clean-context P0–P3 source review found no actionable findings. The repository Auto Review CLI was also attempted but could not authenticate; no successful CLI review is claimed.
AI-assisted implementation and review. Maintainer approval remains required; no merge or automatic repair authority is inferred from the issue labels.
---
## Maintainer addendum — September 14, 2026
The ownership choices proposed above are accepted. This addendum records the final shared legacy-data owner, the preserved durable runtime default, and the refreshed proof. Earlier evidence remains tied to its stated revision.
### Problem
Memory search finds an agent's file, but `memory_get` returns the parent's different file as success.
### Fix and impact
Search and explicit memory reads now share one lightweight legacy-data owner. The two existing cached facts stay with the agent owner. Raw reader behavior, optional IDs, workspace pins, home/profile handling, and extra paths remain supported. No schema, migration, config key, protocol, or dependency changes.
Owner decision: explicit-roster reads must return the searched workspace's file, never a different parent file. This ends the explicit-roster exception retained in #140984. To retain the parent as an agent's workspace, configure that workspace explicitly. Legacy data ownership remains separate from the durable runtime default, preserving #146246.
Production growth is reduced from 46 to 38 net lines:
- `agent-roster.ts` moves existing roster readers into a lightweight module and owns the shared data decision.
- `agent-scope-config.ts` shrinks by 96 lines while preserving public exports and both cache lifetimes.
- Memory-host `config-utils.ts` adds eight net lines to preserve raw-reader compatibility and use the shared owner for explicit inputs.
- `openclaw-runtime-paths.ts` adds the one export needed by that call.
### Evidence
- Main `5576f256da`: the three unpinned regression rows fail with `status: ok` and `text: Parent decoy`; the pinned control passes. Command: `node scripts/run-vitest.mjs extensions/memory-core/src/tools.real-manager.test.ts -t 'reads the indexed agent file with explicit ownership'`.
- Retained HTTP proof at accepted `a016f35b265a96d4cdfd5d43dbf12d461ae417d2` (the rebuilt branch has identical owner files): real Gateway `POST /tools/invoke`, search then get, covers both roster orders, a non-first system agent, explicit pins, and parent traversal. Eight reads return the known agent file bytes; eight traversals are rejected.
- Fresh merge `f48cf5fc4eb0e717f252fefde7929632f24d494a`, parents candidate above and main `f6d984fcbe`: 297 tests pass, one existing skip, across the owner, memory, session/auth, and Doctor suites below.
- Local production/test type checks, changed-file type-aware lint, formatting, architecture, unused-export, line-count, and assertion checks pass. The assertion allowance only shrinks.
- Retained 2026.9.3-created state proof preserves pins, file hashes, and runtime default through Doctor/restart. This proves state compatibility, not the installed update driver.
- Earlier local matrix replay was stopped; its historical failures remain recorded. Current exact-head CI run [34806299349](https://github.com/openclaw/openclaw/actions/runs/34806299349) completed successfully at `1c3602c201d65ed43e45c4c1c856fe4fcc696dc8`, with every executed job passing.
<details>
<summary>HTTP requests and fresh-merge test command</summary>
The isolated Gateway runs through `pnpm openclaw gateway run --port <isolated-port> --bind loopback`.
```json
{"agentId":"main","tool":"memory_search","args":{"query":"AGENT_MAIN_ORCHID","corpus":"memory"}}
{"agentId":"main","tool":"memory_get","args":{"path":"USER.md","from":1,"lines":3}}
{"agentId":"main","tool":"memory_get","args":{"path":"../USER.md","from":1,"lines":2}}
```
The search-returned path and line range drive the read. The read returns two lines: `# main`, followed by `AGENT_MAIN_ORCHID searched source`. Traversal returns `MEMORY_PATH_NOT_ALLOWED`.
```sh
node scripts/run-vitest.mjs \
packages/memory-host-sdk/src/host/config-utils.test.ts \
packages/memory-host-sdk/src/host/read-file.test.ts \
packages/memory-host-sdk/src/host/read-file-manager-compat.test.ts \
extensions/memory-core/src/tools.real-manager.test.ts \
src/agents/agent-scope-config.test.ts \
src/agents/legacy-inherited-auth-dir.test.ts \
src/plugins/contracts/extension-package-project-boundaries.test.ts \
src/config/legacy.roster.test.ts \
src/commands/sessions.default-agent-store.test.ts \
src/commands/doctor/shared/legacy-config-migrations.agent-rosters.test.ts \
src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.test.ts \
src/commands/doctor/shared/default-agent-role-materialization.test.ts \
src/commands/doctor/shared/default-agent-role-materialization.write.test.ts \
src/commands/doctor-config-flow.workspace-persistence.test.ts \
src/agents/agent-scope.test.ts \
src/agents/agent-scope.workspace-inference.test.ts
```
</details>
## Consumers
The moved roster types retain their `index`, `key`, and `kind` fields. The following typed read sites preserve their source-location contracts; generic word matches elsewhere are not consumers of these fields.
<details>
<summary>Preserved roster-field callers</summary>
census: generic index reviewed — 19 callers listed
- `src/agents/sandbox/secret-owner.ts:53:42` — Report unresolved SSH secret references at the selected agent source path.
- `src/cli/config-model-validation.ts:117:90`, `src/cli/config-model-validation.ts:221:62` — Locate model validation issues and test whether edited paths change model ownership at the correct keyed entry or list index.
- `src/commands/doctor/shared/context-engine-host-compat.ts:113:90`, `src/commands/doctor/shared/context-engine-host-compat.ts:153:90` — Locate context-engine host compatibility warnings and repairs at the source agent path.
- `src/commands/doctor/shared/exec-safe-bins.ts:85:35` — Locate executable allowlist warnings at the source agent tools.exec path.
- `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:91:90`, `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:293:90` — Locate plugin tool allowlist warnings at the source agent path.
- `src/config/io.write-prepare.ts:993:71`, `src/config/io.write-prepare.ts:1000:41` — Preserve original roster representation, legacy occurrence identity, explicit writes/deletions, authored reference restoration, and source paths across config writes.
- `src/config/validation-core.ts:159:36`, `src/config/validation-core.ts:281:90`, `src/config/validation-core.ts:335:55` — Locate avatar, model-policy, and sandbox environment errors at the authored keyed entry or original list index.
- `src/config/validation.ts:715:90` — Attach heartbeat target validation issues to the correct authored agent path.
- `src/secrets/runtime-config-collectors-core.ts:536:35` — Locate agent text-to-speech secret assignments at the source agent path.
- `src/secrets/runtime-config-collectors-memory.ts:125:90` — Locate memory secret assignments at the source agent path; the separate unchanged implicit-agent producer supplies the legacy key when no roster exists.
- `src/secrets/runtime-config-collectors-sandbox.ts:75:90` — Carry the source agent path into sandbox secret collection candidates.
- `src/security/dangerous-config-flags-core.ts:50:36` — Report dangerous flags at canonical keyed paths when an ID exists; retain original list index for an ID-less legacy row.
- `src/skills/workshop/tool-policy-diagnostic.ts:49:40` — Report canonical keyed tool-policy paths when an ID exists; retain original list index for an ID-less legacy row.
census: generic key reviewed — 18 callers listed
- `src/agents/sandbox/secret-owner.ts:52:45` — Report unresolved SSH secret references at the selected agent source path.
- `src/cli/config-model-validation.ts:117:60`, `src/cli/config-model-validation.ts:221:42` — Locate model validation issues and test whether edited paths change model ownership at the correct keyed entry or list index.
- `src/commands/doctor/shared/context-engine-host-compat.ts:113:60`, `src/commands/doctor/shared/context-engine-host-compat.ts:153:60` — Locate context-engine host compatibility warnings and repairs at the source agent path.
- `src/commands/doctor/shared/exec-safe-bins.ts:84:38` — Locate executable allowlist warnings at the source agent tools.exec path.
- `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:91:60`, `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:293:60` — Locate plugin tool allowlist warnings at the source agent path.
- `src/config/io.write-prepare.ts:1001:44` — Preserve original roster representation, legacy occurrence identity, explicit writes/deletions, authored reference restoration, and source paths across config writes.
- `src/config/validation-core.ts:158:39`, `src/config/validation-core.ts:281:60`, `src/config/validation-core.ts:334:58` — Locate avatar, model-policy, and sandbox environment errors at the authored keyed entry or original list index.
- `src/config/validation.ts:715:60` — Attach heartbeat target validation issues to the correct authored agent path.
- `src/secrets/runtime-config-collectors-core.ts:535:38` — Locate agent text-to-speech secret assignments at the source agent path.
- `src/secrets/runtime-config-collectors-memory.ts:125:60` — Locate memory secret assignments at the source agent path; the separate unchanged implicit-agent producer supplies the legacy key when no roster exists.
- `src/secrets/runtime-config-collectors-sandbox.ts:75:60` — Carry the source agent path into sandbox secret collection candidates.
- `src/security/dangerous-config-flags-core.ts:46:44` — Report dangerous flags at canonical keyed paths when an ID exists; retain original list index for an ID-less legacy row.
- `src/skills/workshop/tool-policy-diagnostic.ts:46:41` — Report canonical keyed tool-policy paths when an ID exists; retain original list index for an ID-less legacy row.
census: generic kind reviewed — 64 callers listed
- `src/agents/agent-roster.ts:23:15`, `src/agents/agent-roster.ts:35:15` — Select the raw representation before projecting entries; preserve entries precedence and reject invalid representation values.
- `src/agents/agent-scope-config.ts:281:15`, `src/agents/agent-scope-config.ts:294:15`, `src/agents/agent-scope-config.ts:329:15`, `src/agents/agent-scope-config.ts:334:15`, `src/agents/agent-scope-config.ts:316:47` — Preserve direct and mutable roster lookup branches; source.kind in the batch index preserves keyed clone-on-read and legacy list identity.
- `src/agents/sandbox/secret-owner.ts:51:25` — Report unresolved SSH secret references at the selected agent source path.
- `src/cli/config-cli-roster.ts:17:21`, `src/cli/config-cli-roster.ts:35:55`, `src/cli/config-cli-roster.ts:53:19`, `src/cli/config-cli-roster.ts:58:17`, `src/cli/config-cli-roster.ts:90:22`, `src/cli/config-cli-roster.ts:107:38` — Preserve list order and keyed/list path translation across CLI mutations and canonicalization.
- `src/cli/config-model-validation.ts:117:14`, `src/cli/config-model-validation.ts:220:14`, `src/cli/config-model-validation.ts:221:14` — Locate model validation issues and test whether edited paths change model ownership at the correct keyed entry or list index.
- `src/commands/doctor-config-flow.ts:255:45` — Read migrated keyed entries to persist the legacy workspace and stamp explicit ownership for multiple agents.
- `src/commands/doctor/shared/context-engine-host-compat.ts:113:14`, `src/commands/doctor/shared/context-engine-host-compat.ts:153:14` — Locate context-engine host compatibility warnings and repairs at the source agent path.
- `src/commands/doctor/shared/exec-safe-bins.ts:83:16` — Locate executable allowlist warnings at the source agent tools.exec path.
- `src/commands/doctor/shared/legacy-config-core-migrate.ts:30:41`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:35:25`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:45:19`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:46:20`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:108:107` — Validate representation/value agreement, repair the selected roster in its existing shape, and name that shape in repair messages.
- `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:91:14`, `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:293:14` — Locate plugin tool allowlist warnings at the source agent path.
- `src/config/io.write-prepare.ts:892:14`, `src/config/io.write-prepare.ts:951:20`, `src/config/io.write-prepare.ts:1146:15`, `src/config/io.write-prepare.ts:1177:25`, `src/config/io.write-prepare.ts:1185:26`, `src/config/io.write-prepare.ts:1218:23`, `src/config/io.write-prepare.ts:1233:23`, `src/config/io.write-prepare.ts:1255:25`, `src/config/io.write-prepare.ts:1269:23`, `src/config/io.write-prepare.ts:1287:21`, `src/config/io.write-prepare.ts:1330:23`, `src/config/io.write-prepare.ts:1352:21`, `src/config/io.write-prepare.ts:1354:21`, `src/config/io.write-prepare.ts:1450:23`, `src/config/io.write-prepare.ts:1455:27`, `src/config/io.write-prepare.ts:1467:42`, `src/config/io.write-prepare.ts:1532:72`, `src/config/io.write-prepare.ts:1546:23`, `src/config/io.write-prepare.ts:1551:25`, `src/config/io.write-prepare.ts:993:25`, `src/config/io.write-prepare.ts:999:22` — Preserve original roster representation, legacy occurrence identity, explicit writes/deletions, authored reference restoration, and source paths across config writes.
- `src/config/legacy.roster.ts:106:23`, `src/config/legacy.roster.ts:117:35` — Select legacy list conversion, then consume the keyed roster after migration.
- `src/config/validation-core.ts:157:12`, `src/config/validation-core.ts:281:14`, `src/config/validation-core.ts:333:34` — Locate avatar, model-policy, and sandbox environment errors at the authored keyed entry or original list index.
- `src/config/validation.ts:715:14` — Attach heartbeat target validation issues to the correct authored agent path.
- `src/gateway/server-methods/config.ts:528:15`, `src/gateway/server-methods/config.ts:531:15` — Enumerate explicitly authored agent IDs in either representation before rejecting unapproved removals.
- `src/plugins/provider-auth-choice-helpers.ts:304:52`, `src/plugins/provider-auth-choice-helpers.ts:306:52` — Write normalized agent model references back using the original keyed or list representation.
- `src/secrets/runtime-config-collectors-core.ts:534:16` — Locate agent text-to-speech secret assignments at the source agent path.
- `src/secrets/runtime-config-collectors-memory.ts:125:14` — Locate memory secret assignments at the source agent path; the separate unchanged implicit-agent producer supplies the legacy key when no roster exists.
- `src/secrets/runtime-config-collectors-sandbox.ts:75:14` — Carry the source agent path into sandbox secret collection candidates.
- `src/security/dangerous-config-flags-core.ts:45:21` — Report dangerous flags at canonical keyed paths when an ID exists; retain original list index for an ID-less legacy row.
- `src/skills/workshop/tool-policy-diagnostic.ts:45:19` — Report canonical keyed tool-policy paths when an ID exists; retain original list index for an ID-less legacy row.
</details>
Registered memory tools use the shared data owner for explicit workspace reads. Search manager lookup, forget/reset/backfill, session/auth data locations, and Doctor materialization retain their existing owner contracts. There is no new state writer; config-identity provenance and the separate data/runtime batch facts keep their existing lifetimes.
### Contributor-branch refresh
The accepted content is rebuilt on the original contributor head `be5a589a0ee148b54c51f6876ef5281ee9b29cb5`, followed by a plain merge of main `ae5a4b4a55` and the five maintainer commits in order. There were no conflicts or manual resolutions, and all five messages are unchanged.
The resulting head `1c3602c201d65ed43e45c4c1c856fe4fcc696dc8` has the same complete tree as the accepted revision merged with that main. The 16-file command above passed again on this head: 297 tests, one existing skip, exit 0.
The unchanged local preflight passed on this same head in a physical checkout with its own dependencies. It covered type-aware lint, production types, core/extension/script/root-test types, and protocol checks. The initial nested-checkout attempt failed only the declaration-input isolation guard; physical isolation resolved it without disabling any check.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* feat: start remote sessions without a repository
Offer a fresh isolated workspace for cloud and paired-device sessions while preserving explicit and saved repository choices. Use a private empty backing repository per session through the existing managed-worktree lifecycle, retaining reclaim, snapshot restore, and cleanup without copying the agent workspace.
* fix: preserve workspace intent and independent snapshot cleanup
Retain legacy source selections before Git discovery updates availability, keep ordinary snapshot expiry independent of allocation contention, and update the complete source-selection browser flow. Keep request mapping and validation at their existing owners while satisfying import-cycle and export checks.
* fix: retain allocation and cleanup error causes
* test: expect normalized workspace source preferences
Skip HTML ownership parsing when no literal tag opener exists and advance
through ordered text spans once when tags are possible. Preserve code,
link-title, escape, raw-text, and malformed-markup behavior.
The actual progress-note owner showed about 80% lower time for rich notes
without angle brackets and 9-10% for mixed HTML in both measured orders.
All 14 output cells matched; adverse controls and higher child RSS remain
recorded, with no whole-Gateway or memory benefit claim.
Validation: 119 focused tests passed; fresh P2 source review was clean.
* feat(dashboard): save shared fullscreen and split defaults
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(dashboard): preserve transient and legacy presentation intent
Keep command-driven route activation and cross-tab personal choices separate from shared defaults. Restore offline layouts, keep ordinary renders storage-free, refresh generated protocol models, and cover the affected Gateway and UI boundaries.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(dashboard): preserve cross-tab choices and keyboard defaults
Keep personal override writes with the persistence owner, share the opening decision across keyboard and rail controls, and preserve prepared sidebar content during initial reconnect observation.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
---------
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Record Gateway owner identity alongside the physical lifecycle coordinator so restart cleanup preserves live and slow-starting Gateways before they begin listening. Route foreground restarts through the recorded owner even when an installed Scheduled Task uses a different launch mode.
Limit Scheduled Task termination to the installed task's recorded owners or exact installed-argv-attributed legacy processes. Revalidate ownership before termination, repair initially unavailable native process identity through the guarded heartbeat, and preserve captured Unix process-group identities through escalation.
Validated by exact-head CI run 34780366323, including the affected Windows batch's 88 passing tests, and a scoped-clean P1 review. Native Windows final-effect proof and a published-updater end-to-end matrix remain accepted proof gaps; the Windows guest was unavailable in startup recovery. Supervisor metadata uses the new owner row's JSON payload without a schema version, table, or retention change.
Reported by @deYangar (#140162).
Fixes#140162
* feat: give every plugin a compact chat activity icon
Separate package-owned activity SVGs from plugin identity artwork. Ship 154 defaults and seven exact tool overrides, preserving Echo and the progress claw. Include authenticated bounded delivery, safe mask rendering, packaging, and authoring guidance.
* test: declare Vite types for the activity asset browser test
* refactor: keep plugin artwork selection with catalog presentation facts
* test: scope activity browser types and simplify fixture copies
* perf(sessions): offload transcript preparation
Use the shared session-transcript worker with separate context and background queues. Preserve exact-secret redaction and reset recall metadata through preparation and index publication.
* test(sessions): expect preserved transcript fence errors
* fix(memory): preserve indexes when transcript preparation fails
Propagate operational worker errors through existing shadow-rebuild recovery. Advance the private chunking revision so unchanged reset-bearing indexes rebuild through the existing owner without changing transcript hashes or schemas.
* test(ci): include QA profile status routing coverage
* fix: hide subagent sessions from Activity
Filter subagent sessions before search, facets, counts, and pagination so Activity stays focused on conversations. Preserve normal nested sessions and existing diagnostic listings.
* test: use the stored session label in Activity regression
* style: group session key predicates by their owning module
* fix(agents): initialize transport hooks at execution boundaries
Keep catalog compatibility on a narrow internal import and install the existing transport host before session dispatch or completion preparation. Direct SDK completions now apply provider stream wrappers before sending a request, with owner and cancellation checks after initialization.
* build(ai): emit the model compatibility entrypoint
* perf: isolate catalog workers from unused execution code
Use explicit catalog plugin owners without automatic agent slots. Load Ollama payload helpers when their streams run and acquire the TypeScript compiler only when a typed module is actually compiled.
* test(agents): exercise admitted compaction cancellation
Cancel the manual compaction after its provider is admitted so its exact rejection remains observable. Assert that cancellation before dispatch never calls the provider again.
Allow independent reasoning effort and Fast mode for OpenClaw model-backed command and widget approval reviews. Both settings are optional; Fast mode maps to the supported provider processing tier through the shared completion path.
Validated with 272 focused tests, production typechecking, type-aware lint, and configuration inventory checks. ClawSweeper reviewed the exact head with no actionable findings. Existing configs retain their defaults; remove the new optional keys before downgrading.
Merged under the operator-approved exception for the unrelated Workboard missing-await lint failure on main (repair tracked in #147218). All other CI checks passed.
* fix: high Gateway CPU while viewing diagnostics
* test: match agent selection observer contract
* test: exercise diagnostics replacement through initial load
Closes#143431.
## What Problem This Solves
Markdown-only progress cards showed “Progress updated” in channel drafts, hiding the note.
## Why This Change Was Made
The shared Markdown parser removes formatting and authored HTML while preserving code literals and link metadata. This refines #139206 while retaining its HTML boundary. The card store remains the sole writer; transactions order updates and reset clears.
## User Impact
Notes are readable within existing headline limits. Checklist counts, replacement, clearing, and stored Markdown keep their behavior.
## Evidence
The registered-tool-to-final-renderer regression covers four literal inputs across three output variants. It fails in ten cases on the previous head and passes all twelve after the correction. Real channel recordings confirm literal characters, inactive links, replacement, clearing, and final delivery.
A Slack dispatch regression confirms that a fresh “Checking results” preamble and the matching projected note produce one native title. It fails with the duplicate title before the fix and passes after. The corrected branch passed 263 tests; the corrected merged content passed 266, including all twelve literal cases.
## Compatibility
No stored format, configuration key, protocol version, or SDK entrypoint changed. Existing callback and snapshot types gained optional presentation metadata.
## Consumers
Both agent execution paths share the note projection. The final channel renderers must treat the prepared note as literal text while preserving authored formatting in narration and commentary. Speech, session preambles, and other plain-text callers retain their existing defaults. Web-fetch extraction and visibility use the same unchanged scanner at its new shared location. Existing code-region readers retain their prior contract.
## Invalidation
Each update replaces the draft plan and note; empty input clears them.
## Tests
The prepared-text contract is forwarded through the existing event and draft boundaries. Authored narration keeps its previous formatting. The broader merged selection passed 782 tests; later formatter changes passed the recorded focused selections, including 120 tests on the final merged content. These are overlapping selections, not a summed test total. Live recordings precede equivalent encoder centralization; final focused tests cover that change.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
## What Problem This Solves
Final replies could appear twice after a mid-turn refresh or reconnect. Fixes#138735. Reported by @PanyhP.
Replaces #138887 with the remaining display fix; cursor recovery is already fixed on main.
## Why This Change Was Made
One item-aware matcher in `session-projection.ts` decides which display row owns a final. The other writer of this state is history replay; item identity and transcript order decide adoption. Commentary and tool continuations cannot adopt an unkeyed final.
## User Impact
A final appears once after refresh or reconnect. Distinct items with equal text remain separate.
## Evidence
Real Gateway and built Control UI: mid-turn refresh/reconnect changed from two final blocks to one. Completed-turn refresh stays at one. Refresh command: `bunx agent-browser@0.37.1 --session "$session" reload`.
## Compatibility
Stored transcripts, protocol fields, schemas and preferences are unchanged. Apple decoding was inspected: its inputs and independent rows are unchanged; no device run is claimed.
## Consumers
Control UI and terminal UI use the shared matcher.
## Invalidation
Live delivery and full-history replay share item ownership; existing reconnect and history reset rules remain.
## Tests
Client: 388 passed. Display projection: 143 passed. Terminal UI: 210; Control UI: 139 passed, on the merge with main. New reducer regressions failed before the fix and pass afterward.
`node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway-client.config.ts packages/gateway-client/src`
## Review scope and evidence
- The reviewed head is f05f513b69c158c77cf7cc6024610b7459851ce9, a fresh merge of fix 6768058762ce426ea15835b1e8a96ffad37d06ff with main 9746e3931a.
- The failing browser baseline is e735dcd33a. Real Gateway and matching built Control UI captures show two final blocks after a mid-turn refresh or reconnect on the baseline, and one on the reviewed head. Completed-turn refresh remains one; two separate same-text replies remain two.
- The 880 focused tests passed on that merge: Gateway client 388, display projection 143, terminal UI 210, and Control UI 139. Full hosted checks also passed on this head in [CI run 34762715688](https://github.com/openclaw/openclaw/actions/runs/34762715688).
## Ownership and compatibility
- entryMatches in packages/gateway-client/src/session-projection.ts owns display-row adoption for live delivery and full-history replay. The existing snapshot terminal selector only narrows candidates admitted by that matcher. History, live events, pending sends, and sequenced error recovery retain their existing ordering rules.
- The tool-continuation predicate also checks tool content, so missing or misleading stopReason values cannot make a tool row own an unkeyed final. The change adds 17 net production lines and 60 test lines.
- The merged #146554 behavior remains: tool activity does not retire an unfinished assistant stream. Its continuous-stream and completed-message assertions still pass.
- Apple clients use independent Swift row identity and do not decode display-part item IDs. This PR changes neither their code nor their received payloads and does not restore the old multi-payload broadcast. Native conclusions are source-only; no device run is claimed.
- Cursor commentary recovery is already fixed on main. Stored transcripts, commentary preferences, schemas, config keys, protocol fields, and public SDK contracts are unchanged. This replaces the remaining display work in #138887.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
## What Problem This Solves
After a rollback, an ordinary memory search could rebuild an index written by a newer OpenClaw and call the embedding provider without disclosing possible cost.
This corrects a regression introduced by #146713. AI-assisted.
## Why This Change Was Made
Newer versions take precedence over older versions when checking the index. Automatic synchronization preserves newer indexes; explicit reindex remains available.
## User Impact
After an OpenClaw index-format upgrade, the first search can rebuild an older
index before returning results. Rebuilding can take longer and can incur costs
from the configured embedding provider. An index written by a newer OpenClaw
remains paused; upgrade OpenClaw or reindex explicitly. Later searches reuse the
repaired index. Status inspection alone does not rebuild it.
A search that requests a rebuild retains the existing provider-cost disclosure, including on retrieval failure or timeout.
## Evidence
Real CLI runs with two declared runtime-format fixtures reproduced a rebuild on every version switch before this change. On the candidate, the upgrade rebuilt once, rollback made no embedding requests, and switching forward reused the preserved index. These fixtures exercise the real CLI; they are not two separately installed released versions.
Live Gateway searches cover older, current, newer, and mixed stored versions. The retained 2026.7.1-2 state repairs once after current Doctor prepares a copy. Three later searches return known facts without re-embedding documents. Recording providers establish requests, not billed cost. This is search compatibility proof, not an old-updater rerun.
## Compatibility
No schema, configuration, protocol, or public SDK changes. Production code grows to carry repair notices through existing response and error fields without changing the search API.
## Consumers
The classifier serves search admission, the synchronization writer, and status identity. CLI, Gateway, and tool results consume the existing diagnostic formatter. Deep status has its own recovery text. Search can also hand retry work to a separate maintenance manager, so disclosure must cross that handoff. Active Memory preserves successful-search warnings separately from failure state.
## Invalidation
The decision `whether a runtime index is older or newer` is made by exactly one mechanism at `extensions/memory-core/src/memory/manager-reindex-state.ts:153`.
The existing writer lease rechecks identity. Successful repair refreshes the existing index generation; notices apply only to searches that observe a repair request.
## Contention
Concurrent searches share one rebuild and each receives its disclosure. The existing writer and publication leases are unchanged.
## Tests
The retained candidate run passes 69 focused memory tests. A separate Gateway run passes 17 tests. The original-source tool regression run reports 10 failures and two current-version passes. The accepted merge `ed397080e63c06374724c1e4f5b41911edb38d85` now has 311 distinct passing tests: 281 memory/CLI, 13 diagnostics, and 17 Gateway tests. One CLI assertion initially saw forced ANSI colors; the complete CLI file passed with `FORCE_COLOR=0 NO_COLOR=1` on the same unchanged merge. No assertion, source behavior, or timeout was weakened.
<details>
<summary>Test commands on the accepted merge</summary>
```sh
OPENCLAW_E2E_USE_PREBUILT_DIST=1 node node_modules/vitest/vitest.mjs run --config test/vitest/vitest.extension-memory.config.ts extensions/memory-core/src/tools.index-upgrade.test.ts extensions/memory-core/src/tools.index-diagnostic.test.ts extensions/memory-core/src/tools.real-manager.test.ts extensions/memory-core/src/cli.test.ts extensions/memory-core/src/memory/manager-search-upgrade.test.ts extensions/memory-core/src/memory/manager-reindex-state.test.ts extensions/memory-core/src/memory/manager-search.test.ts extensions/memory-core/src/memory/manager.reindex-recovery.test.ts
FORCE_COLOR=0 NO_COLOR=1 OPENCLAW_E2E_USE_PREBUILT_DIST=1 node node_modules/vitest/vitest.mjs run --config test/vitest/vitest.extension-memory.config.ts extensions/memory-core/src/cli.test.ts
FORCE_COLOR=0 NO_COLOR=1 OPENCLAW_E2E_USE_PREBUILT_DIST=1 node node_modules/vitest/vitest.mjs run --config test/vitest/vitest.unit-fast.config.ts packages/memory-host-sdk/src/host/types.test.ts
FORCE_COLOR=0 NO_COLOR=1 OPENCLAW_E2E_USE_PREBUILT_DIST=1 node node_modules/vitest/vitest.mjs run --config test/vitest/vitest.gateway-methods.config.ts src/gateway/server-methods/memory-search.test.ts
```
</details>
## Test movement
The former keyword-only provenance table moved to a recording-provider version matrix. Path, citation, revision, and repeat-search outcomes remain covered. The former zero-query assertion for older/missing keyword-only indexes is not part of that new provider matrix; keyword-only repair is also covered by the real Gateway case and existing tool cases.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* feat(ui): keep open files in shared sidebar tabs
Give chat files named tabs in the existing shared strip and retain their view and editor state. Keep workspace browsing and original downloads, honor explicit line links in reused tabs, and isolate split-pane focus.
Closes#146655
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* feat(ui): render HTML in file tabs
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix: synchronize HTML preview protocol and CI fixtures
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
---------
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
After upgrading from 2026.7.1-2, existing memories could become unsearchable until a manual reindex. OpenClaw now repairs outdated memory indexes automatically during search or ordinary background synchronization.
A failed repair reports the provider error, confirms the prior index was retained, and directs the user to `openclaw memory status --deep` before retrying.
## Consumers
- Memory CLI, Gateway searches, and `memory_search` recover existing facts automatically.
- Trigger recall, voice fast context, and supplemental wiki memory searches use the same repaired index.
- Ordinary and startup/background memory synchronization repair the same outdated runtime index.
- Active Memory recognizes repaired tool results as successful recall; recovered CLI/tool hits use normal recall tracking when dreaming is enabled.
## Invalidation
The decision `whether to rebuild the index` is made by exactly one mechanism at `extensions/memory-core/src/memory/manager-sync-ops.ts:164`.
The decision `whether a recorded sync failure supersedes runtime-identity guidance` is made by exactly one mechanism at `packages/memory-host-sdk/src/host/types.ts:295`.
Current metadata prevents repeated repairs. The writer rereads it after acquiring the existing writer lease; successful publication invalidates the existing derived caches. Failed publication preserves the prior index and retry state.
Production code grows by 30 lines to share the existing writer lease and select repair errors and guidance once while preserving existing tool, command-line, and Gateway wording.
## Contention
Concurrent searches share one repair, including when an explicit reindex is already running. Active synchronization waits for the writer; reset retains its two-second busy-index refusal. The initial repair can take longer and call the embedding provider. An interactive tool can time out while admitted index work finishes safely.
## Tests
All 92 focused tests pass. Registered Gateway tests detect the original duplicate rebuild; registered tool tests cover missing, old, and current provenance, citations, retained data after failure, and recovery. The combined-corpus case checks that a healthy wiki result retains the memory provider error, preservation warning, and recovery action. Upgrade cases retain ordinary synchronization, read-only status, configuration-only mismatch, and cleanup coverage.
The published 2026.7.1-2 updater reproduced the retrieval failure in a six-agent installation. After repair, three known facts returned with citations, original files remained unchanged, and a repeat search did not rebuild. Two real concurrent requests embed 24 files once, including with a slow provider or an explicit reindex already running. Provider-error checks return concrete failure and deep-status guidance through the tool, command line, and Gateway while retaining the old index.
## Compatibility
No configuration, schema, or protocol changes. Status inspection remains read-only, and configuration-only incompatibilities retain their existing guidance. The reported loss of agent identity was not reproduced; this addresses the retrieval part of #142580.
Thanks @GitHoubi for the report and upgrade evidence.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix(agents): recover yielded subagents and preserve sidebar nesting
Defer sessions_yield until earlier async tool results have reached a subsequent model request, preventing a child from pausing before it can consume completed output.
Resume paused children through the existing adoption owner so task identity, requester, and parent completion batches survive operator follow-ups. Keep subagents beneath their expanded navigation parent even when categorized, and show their task names in activity rows.
Validation: async-yield, sessions.send recovery, lifecycle, and sidebar regressions; scoped production/test typechecks, lint, consistency checks, and independent review passed.
(cherry picked from commit 3086b77b6ce7edd25d0964b1f3559dea296e4d12)
* fix(gateway): preserve admitted follow-up task ownership
Keep plugin and settlement adoption with admission while allowing explicit operator recovery. Exercise requester preservation through dispatch and retain retryable approval handoffs on admission failure. Align the existing browser cache assertion with the fingerprinted sandbox shell from #146602.