Missing prerequisites for two test lanes, a copy-paste slip in an SDK
sample, three undeclared identifiers in a quick start, a colon promising
keys named two paragraphs later, duplicated Related lists, two unlinked
pages that exist, an unbracketed placeholder, and a bare doctor invocation.
The thirteenth fix, an ffmpeg prerequisite on docs/tools/tts/quickstart.md,
is held back: every PR touching that page is refused by the secret scanner
before review.
Require explicit Copilot configuration, a saved profile, or COPILOT_GITHUB_TOKEN within the existing agent scope. Generic GitHub credentials continue to serve other tools and remain covered by secret audit and cleanup.
Retire the discovery opt-out through the shared Doctor migration and add one-time upgrade guidance. Update provider documentation and generated configuration inventories.
Validated with focused plugin and secret tests, real Gateway starts across all 16 sanitized configs, and independent CLI checks for activation, Doctor notices, secret detection, and matching-value cleanup.
Related: #144726
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* docs: fix verified accuracy findings in channels, cli, nodes, and reference
Close the open `accuracy` ledger rows for docs/channels/, docs/cli/,
docs/nodes/ and docs/reference/ that survived verification against source.
Each edit is backed by code or by a dated commit/release:
- Name the release for previously unscoped time-relative claims
(Slack `progress` default 2026.8.1, Telegram preview default 2026.8.1,
Discord DAVE receive recovery 2026.2.24, gateway ownership contract
2026.8.1, cron `--deliver`/`cron.failureDestination`/file store,
nodes `nodes.run` removal 2026.3.31, media `{{Attachment*}}` rename,
presence clear action, memory-config embedding identity, schema-19
`consumed_event_id`, `[view ...]` -> `[embed ...]`).
- Delete time-relative wording where no release record exists and state
present behaviour instead (mattermost, msteams, groups, audio, camera,
computer-use, secretref store scope, full-release-validation alias).
- Correct concrete defects: matrix `threadReplies` default, audio
`gpt-transcribe` -> `gpt-4o-transcribe`, camera `--duration-ms`
(no such flag), signal quickstart probe command, googlechat macOS
bind-address check, discord `bash` fences holding slash commands,
usage-window provider lists, RELEASING script naming, zalo disclaimer
placement, talk.md placeholders, images.md duplicated vision rule.
`pnpm check:docs` passes. `docs-link-audit --anchors` reports the same 3
pre-existing `#windows-app-node` errors as the merge base.
* docs(nodes): restore the approved media-template deprecation window
ClawSweeper is right: `src/plugins/compat/media-legacy-projection.ts` marks
`{{MediaPath}}`, `{{MediaUrl}}`, `{{MediaType}}` and `{{MediaDir}}` deprecated
with `removeAfter: "2026-10-01"`, gated on a clean published-plugin artifact
sweep. "No removal is scheduled" was wrong.
Both pages now keep the deprecated status, state the approved date and its
gate, and link the record's own docs target
(/plugins/sdk-migration/compatibility-policy#media-legacy-projection).
Renaming a heading changes its minted id, and docs/AGENTS.md requires the
old named anchor to survive the reorganization. Fourteen ids were dropped
without a stub, so published links -- bookmarks, search results, and URLs
written in source -- landed on a page that scrolled nowhere. Nothing
caught it: docs-link-audit only walks the docs tree, so a URL living in
source or in a bookmark is invisible to it, and an id that simply stops
being minted breaks no in-repo link once the in-repo links are repointed.
Three of these are the pages PR #143497 worked around by repointing the
source instead: /tools/slash-commands#config, /cli#status, and
/concepts/models#selection-source-and-fallback-behavior. The rest come
from a sweep of every docs id dropped since May that something in the
repo had once linked to.
Each stub is authored beside the heading that now owns the content, and
each page's id set was enumerated with the repo's own parseDocsDocument
before and after: a strict superset every time, no collisions, no
duplicate authored/canonical id.
The page was 46,017 characters in one flat run: a quick-start tutorial, a
how-to for writing a hook, three reference blocks (HOOK.md fields, the
bundled hooks, the event catalog and context payloads), and a
troubleshooting section. It is now an index with five children, one per
reader job.
Children:
- /automation/hooks/writing-hooks - hook directory layout, the handler
contract, reply delivery, the HOOK.md metadata fields
- /automation/hooks/configuration - the master switch and selection
rules, per-hook entries, discovery precedence, hook packs
- /automation/hooks/bundled-hooks - the five shipped hooks and the
behavior and options of each
- /automation/hooks/event-types - every event key with its trigger and
wait behavior, plus the context each producer supplies
- /automation/hooks/troubleshooting - hook not discovered, not eligible,
not executing
The index keeps the overview, "Choose the right surface", the quick start
with its eligibility and scope subsections, plugin hooks, best practices,
the CLI pointer, and Related.
Anchor strategy: per-anchor redirects are impossible because
redirectSource() rejects any source containing [?#]. All 38 pre-split IDs
computed with parseDocsDocument stay alive on the parent index: 11 remain
natively published (hooks, choose-the-right-surface, quick-start,
plugin-hooks, best-practices, cli-reference, related, and both the encoded
and cleaned forms of the two comma headings) and 27 became authored
<a id="..." /> stubs in a "Where each section moved" list, each linking to
the page that now holds the content. Three punctuated headings emit both an
encoded and a cleaned ID; every form is covered. No ID the index still
publishes itself is stubbed, so there is no duplicate authored/canonical ID
collision. A script asserted each pre-split ID resolves end to end: 38/38
pass, all 26 index-to-child fragment targets resolve, and the 6 pages
report 0 collisions.
Losslessness: the five children and the index blocks reassemble in original
order to a body that is byte-identical to the pre-split body,
sha256 4e46aef7b20e0bc73d3ad71b108e6d6e12e8d4c2a662e96d0bf2509578372ce8.
All 14 code fences are identical one-for-one on both info string and body
sha256. Words 5,316 -> 5,695 and table rows 69 -> 75; the deltas are the
five child frontmatters and ledes, the index's 5-row topic table, and the
27-entry moved list. Largest page is now 12,525 characters.
Prose was not rewritten. Six same-page fragment links inside the moved
content now point at the child holding their target, and seven inbound deep
links from other docs pages were retargeted at the children. Retargeting
the event-context link inside the "Every event has these fields" table
widened one column, so format-docs re-padded that table; the cell contents
are unchanged.
Closes audit finding: r3-0012
The Node requirement has changed nine times in 2026 and the reasons (the
SQLite WAL-reset corruption floor and the separate node:sqlite TEXT NUL
decoder bug) were buried in install prose; the Bun page's Caveats section
had become the de facto Bun contract while sitting under Containers.
- Add docs/install/node-compatibility.md: supported lines, why the floors
exist, platform consequences, what each installer provisions, the guard
diagnostic, and a sourced history of the requirement across releases.
- Add docs/install/bun-compatibility.md: Bun requirements, per-platform
SQLite builds, macOS library selection and OPENCLAW_SQLITE_LIBRARY with
the preload migration, the memory scan fallback, known limitations, and
release history.
- Keep docs/install/node.md and docs/install/bun.md as install how-tos;
move the contract paragraphs to the new pages and link them.
- Add a Runtimes nav group (Node, Node compatibility, Bun, Bun
compatibility) and move Bun out of Containers; no URLs change.
- Point the environment reference, memory config, and install overview at
the new pages; add zh-CN glossary entries; add a docs guide bullet to
refresh the tables when the runtime floors in code change.
* feat(sqlite): select an extension-capable SQLite library for Bun on macOS
Bun on macOS dlopens Apple's SQLite, which omits extension loading, so
sqlite-vec never loaded and memory search fell back to the batched scan.
Bun's only hook is bun:sqlite Database.setCustomSQLite: one-shot, before
the first open, and fatal to every later open on a bad path.
- Add src/infra/bun-sqlite-library.ts: validate each candidate through
bun:ffi (WAL-reset-safe version, extension loading present) before
committing; resolve explicit path > OPENCLAW_SQLITE_LIBRARY >
Homebrew/MacPorts discovery; memoize process-wide. No-op on Node and
on Linux/Windows Bun, whose static SQLite already loads extensions.
- Select before the first open in the runtime guard probe and in
requireNodeSqlite; an unusable override becomes a clean runtime-guard
diagnostic with exit 1 instead of a stack trace.
- Forward the selected library to the memory KNN child through its typed
stdin input and select there before opening; the child env stays
stripped. The #141104 scan fallback remains the no-library path.
- Report the selection at Gateway startup and in doctor.
- Retire the undocumented legacy OPENCLAW_CLAUDE_CLI_LOG_OUTPUT alias so
the OPENCLAW_* name budget stays at 493.
- Docs: Bun install caveats, environment reference, memory config.
* test(sqlite): retain the KNN stdin spy for payload assertions
* docs(bun): explain migration from custom SQLite preloads
Report configured symlink roots through the scanner's existing skip decision and the existing source-status issues. Keep traversal, indexing, and stored data unchanged while recommending canonical absolute paths.
Consolidates the diagnostic contributions from @ruel225 in #140381 and @gaoanze888 in #140281.
Validation: real baseline and candidate memory CLI checks; owner regression tests fail on baseline and pass after repair; scanner siblings pass; hosted CI passes. Independent behavior acceptance and exact-head review are retained with the PR evidence.
Fixes#140214.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Citation decoration stripped the first line's leading whitespace after retrieval. Preserve that prefix while retaining trailing cleanup and source-footer behavior at the presentation owner.
Cover the real SQLite manager-to-tool path with spaces, tabs, citation-mode controls, unchanged source bytes, and both public result forms. Clarify the documented citation formatting contract.
Give doctor one actionable rejection diagnostic with the legacy event path, filesystem error, and supported shared-note indexing alternative. Preserve the existing migration and symlink policy. Reconstructed hangs did not reproduce on 2026.9.1 or main. Refs #139166.
Replace the rejected memory.search.sync.watch recommendation with effective extra-path/root reduction, host-limit review, and an affected-agent reindex command for the Gateway environment. Reuse the existing CLI formatter to preserve profile/container routing. Watcher behavior, thresholds, timers, schemas and defaults stay unchanged.
Cover native-directory and Chokidar-path warnings, named profiles, exact-once timing and cleanup. Canonicalize the touched test fixture instead of exceeding its line limit.
Proof: real isolated baseline warning and rejected-key CLI validation; 90 watcher/filesystem/registry tests, 343 config/profile siblings, scoped checks and fresh independent review. Candidate recovery proof remains a pre-land gate.
Fixes#137156.
* feat(sessions): enable cross-agent session access by default
`tools.sessions.visibility` now defaults to `all` and
`tools.agentToAgent.enabled` to `true`; both widen access.
Narrow access via `tools.sessions.visibility` (agent|tree|self),
`tools.agentToAgent.allow`, or `enabled: false`.
Document that an omitted/empty allow list permits every agent pair.
Denial copy for narrowed visibility no longer instructs enabling the
already-on policy. Regenerate prompt-snapshot fixtures for the visibility
hedge. Maintainer-directed.
* feat(security): audit default cross-agent session access
Add `security.trust_model.cross_agent_session_access_default`: `info`
for plain multi-agent defaults, `warn` with sandbox/tool-restriction/
multi-user ingress signals. No new config keys.
* test(gateway): drain detached a2a flow between agentId send rows
The announce/ping-pong flow outlives the sessions_send tool request; the second agentId row picked up the first row's follow-up agent call for agent:orion:main, so each row now waits for gateway active work to drain before releasing its test state.
* test(security): mock the cross-agent access collector in the non-deep facade
The readonly-setup-fallback test mocks audit.nondeep.runtime with an explicit factory; it now exports collectCrossAgentSessionAccessFindings so the registered collector resolves under the mock (CI run 33699015755, checks-node-compact-large-21).
* fix(security): scope the cross-agent audit to unsandboxed sessions
The audit finding now names which agents can reach other agents
(unsandboxed sessions, or any session when
agents.defaults.sandbox.sessionToolsVisibility is "all"), emits nothing
when every agent is fully sandboxed under the default clamp, and says
sandboxed transcripts stay readable by unsandboxed callers.
Docs qualify the agent-to-agent reference with the requester-owned
native/ACP child exception and correct the security overview's sandbox
wording. Addresses both ClawSweeper rank-up moves on #136755.
* docs(security): qualify the fully sandboxed audit exemption
State in the CLI reference and high-level security audit summary that
fully sandboxed rosters under the default spawn-tree clamp produce no
cross-agent access finding. Disabling that clamp removes the exemption.
Addresses the mechanical ClawSweeper rank-up on #136755 at 3208e19534e.
* fix(security): report per-agent session tool reach in the cross-agent audit
The finding now lists which agents can reach other agents (unclamped sessions that still have a session tool allowed) with their calling context and allowed tools, lists non-reaching agents with the reason, and emits nothing when nobody reaches; help text no longer claims enabled=false isolates agents because requester-owned native/ACP child sessions stay reachable under tree or all visibility; gateway final-effect proof that a disabled policy or restrictive allow list never dispatches to the target. Addresses the ClawSweeper re-review on #136755.
* test(gateway): drain detached a2a flow in an afterEach hook
The in-row drain shared the row's 10s budget and could time out under load, leaking the next row's mock calls; the hook has its own bounded timeout.
* docs: stop describing disabled agent-to-agent access as isolation
enabled: false blocks ordinary cross-agent access, but requester-owned native subagent and ACP child sessions stay reachable under tree or all visibility; every introduced claim now says so and points strict separation to tools.sessions.visibility or separate gateways. Addresses the ClawSweeper P2 on #136755.
* test(qa-lab): prove default cross-agent send and policy denials end to end
Three mock-openai flow scenarios run a two-agent QA Gateway: default config dispatches sessions_send to agent:orion:main (accepted, target run observed, target main session created); enabled=false and a restrictive allow list return forbidden before any target work. Addresses the ClawSweeper P1 merge risk on #136755.
* fix(config): stop listing tree visibility as strict separation
Strict separation is agent or self; tree still admits requester-owned native subagent and ACP child sessions across agents. Addresses a ClawSweeper rank-up move on #136755.
* 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>
* docs(memory): add memory provenance and deletion concepts page
New concepts/memory-provenance page tells the operator policy story end to end: recorded session lineage through consolidation, the admission policy and its pipeline-only boundary, memory forget guarantees (whole-entry purge, derived-artifact sweep, forgotten tombstones) and reported boundaries (transcripts, curated writes, paraphrased prose), plus the person/source purge workflow. Cross-linked from memory architecture, dreaming, builtin, overview, CLI, and config reference; registered in docs navigation.
* docs(i18n): add glossary terms for the memory provenance page
* docs(memory): distinguish curated entries from missing lineage in the provenance page
* feat(state): add memory entry origins and session tombstone tables with session-aware write provenance
Additive same-version per-agent tables (memory_entry_origins, memory_session_tombstones) declared canonically with lazy idempotent ensure; read-only agent DB access exposed through the SDK; memory-write observers and the session-memory hook record the authoring session; archived narrative transcripts classified by authoritative run identity.
* feat(memory): admission policy, entry provenance, and openclaw memory forget
memoryPolicy.excludeSessions keeps configured sources out of the dreaming pipeline with recorded exclusions; promotion writes are marker-addressable and carry per-session origins through consolidation; memory forget purges whole entries and every derived artifact (corpus lines, diary quotes, index/FTS/vec/cache, short-term state, backups), reports curated agent writes from transcripts, and tombstones purged sessions so ingestion, backfill, and transcript indexing never re-admit them.
* docs(memory): document admission policy, memory forget, and provenance boundaries
* fix(memory): open the vec probe through openNodeSqliteDatabase
Kysely guardrail forbids direct node:sqlite constructors in production; the in-memory extension probe now uses the canonical opener.
* fix(memory): resolve archived and explicit sessions in memory forget
Explicit --session selectors union live session windows, transcript archives, and exact unresolved ids so an operator-named session always purges and tombstones instead of silently no-oping after lifecycle archival; reports carry per-session resolution sources. ClawSweeper P1 on #130151.
* fix(memory): reconcile consolidation origins for every workspace agent
Shared workspaces record entry origins per source agent; consolidation now re-keys and prunes origins in each participating agent's database (membership from the authoritative dreaming workspace mapping), so a non-owner agent's later memory forget still finds the live consolidated entry. ClawSweeper re-review P1 on #130151.
* fix(memory): enforce canonical SecretRef resolution
Make Gateway runtime snapshots the exclusive owner of memory SecretRef materialization. Bind embedding credentials and headers to provider-owned destinations, and fence per-agent stale reuse by the provider destination/auth contract.
Release note: Memory search resolves secret references through configured provider policy and keeps embedding credentials scoped to their intended destination.
* fix(lmstudio): preserve resolved memory headers
memory remote headers are already materialized by the Gateway snapshot and now bypass SecretRef re-resolution; provider-owned headers retain canonical resolution; final loopback request proof covers literal preservation and precedence.
* fix(memory): bind stale credentials to auth owners
Resolve memory adapter credential owners from snapshot manifest metadata, conservatively fail cold when metadata is absent, and prove Gemini/Google destination changes plus zero-egress unresolved refs.
* fix(memory): scope compatible embedding credentials
Apply destination ownership to the core compatible adapter while preserving destination-owned credentials and intentionally unauthenticated endpoints. Distinguish loopback principals, consolidate duplicate security tests, and verify the final credential boundary through a live isolated Gateway request.
* test(memory): align destination auth precedence
* fix(memory): bind credentials to query identity
Include URL query parameters in embedding destination ownership so provider credentials and headers never cross tenant boundaries.
* fix(memory): preserve query-bound embedding destinations
Move llama.cpp chat and local embeddings onto a verified externally managed llama-server runtime. Remove the in-process native runtime, forked embedding workers, and node-llama-cpp dependency while preserving guided setup, local GGUF models, tool-capable agent runs, diagnostics, and operator docs.
* feat(memory): support globbed extra paths
Allow root-relative glob patterns on existing memory.search.extraPaths entries across discovery, watching, reindex identity, and QMD migration.
* fix(memory): drop stale lease type import
* fix(memory): enforce extra path glob scope
* feat(memory): add provenance and recall metadata to the memory index
* feat(memory): provenance-gated promotion and capture hygiene
* feat(dreaming): LLM consolidation with deterministic gates, on by default
* feat(active-memory): deterministic recall lane with escalation default
* feat(memory): user model file and standing intents
* docs(memory): document the memory architecture
* fix(memory): live-QA fixes — metadata writers, provenance classes, intent scope, claim accumulation
* docs: correct retired cron/audit config keys, cron failure-alert default, memory recall default, and tool-search telemetry claims
- configuration-reference: cron block documented cron.webhook and cron.failureDestination, both retired by the config-surface reduction tranches (58452de711, edecdbd05e); the cron schema is strict so a copied snippet is rejected. Document only the live keys and note the doctor --fix migrations.
- configuration-reference: root-level audit block is retired; canonical path is logging.audit (src/config/zod-schema.root-shape.ts).
- configuration-reference: cron.failureAlert.after default is 2, not 3 (src/cron/service/failure-alerts.ts).
- memory-config: rememberAcrossConversations defaults on for personal installs (packages/memory-host-sdk/src/host/config-utils.ts), matching the canonical table earlier in the page.
- tool-search: telemetry records catalogSize, per-source counts, and search/describe/call counts, and only on tool_search_code results. No byte accounting exists in the runtime.
* docs: retire remaining references to removed cron, audit, and logging config keys
Sweep follow-up to the previous commit, covering the same bug class in the pages that still contradicted it.
- cron-jobs/cli-cron: global cron.failureDestination is retired; the destination fields now live on cron.failureAlert (src/config/zod-schema.root-shape.ts, merged by legacy-config-migrations.runtime.retired.ts:379). Per-job delivery.failureDestination bullets left intact.
- gateway/audit, cli/audit, gateway/protocol: root-level audit.* is retired; canonical path is logging.audit.*.
- logging: logging.redactSensitive is retired (dead-config-keys.test.ts:198; removed by legacy-config-migrations.runtime.tier-eval.ts:12). resolveConfigRedaction hardcodes DEFAULT_REDACT_MODE = tools, so redaction is unconditional. Also documented that redactPatterns replaces the defaults on the log path (redact.ts:419) while tool payloads always merge them.
- logging: consoleStyle accepts only pretty|json (zod-schema.root-shape.ts:106); compact remains the automatic non-TTY rendering style (logging/console.ts:40) but is no longer settable, and doctor maps a stored one to pretty.
- security: security --fix no longer touches redaction and the logging.redact_off audit check is retired (src/security/audit-loopback-logging.test.ts asserts it never fires).
* chore(docs): regenerate docs map after retired-key cleanup
* fix(memory): scope qmd search deadline
Preserve the phase-scoped QMD timeout semantics on current main.
Co-authored-by: Peter Steinberger <58493+steipete@users.noreply.github.com>
* docs: refresh generated docs map
* test(memory): reconcile manager mock with current main
* test(memory): isolate deadline mock import
* style(memory): format search test mocks
* test(memory): decouple mock from deadline symbol
* chore(memory): refresh reviewed pull request head
---------
Co-authored-by: Benjamin Badejo <ben@benbadejo.com>
Co-authored-by: Peter Steinberger <58493+steipete@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>