PLUR1BUS turns OpenClaw into an agent with long-term memory: a per-agent isolated LanceDB store as the source of truth, a mirrored Obsidian vault as a human-readable view, and a small set of background jobs that classify, consolidate, and (when warranted) notify.
PLUR1BUS 7.12.8 — verified on OpenClaw 2026.8.x, 2026.9.1 and 2026.9.2
Current source version: 7.12.8. PLUR1BUS 7.12.8 supports OpenClaw 2026.8.1
as its primary host target and is additionally verified against OpenClaw
2026.9.1; the declared compatibility floor is openclaw@2026.8.1 and plugin
API >=2026.8.1. The package is built and tested against the immutable build
baseline openclaw@2026.8.2; see the
compatibility contract for the full runtime
matrix and evidence. The upstream source base is the immutable official tag
v7.4.10, commit c0a8a4c28ff1cb9c632e185f21f4502d67d1b605.
PLUR1BUS registers its own "PLUR1BUS" tab in OpenClaw's Control UI
(/plugins/memory-lancedb-namespaced/control) — memory health, the workspace
matrix, provider status, and migration progress, all in the browser. It rides
on the OpenClaw Gateway's own port and authentication (no separate port, no
separate login); reach it through however you already reach your Gateway
(loopback, or a Tailscale/VPN front end), then open that tab.
By default, each agent gets its own LanceDB store under {baseDbPath}/{agentId}/ and a matching Obsidian vault folder for browsing. An explicit named-namespace configuration can read the same validated agent from multiple storage namespaces while keeping one active writer. The plugin captures conversation-derived memory cards automatically, runs a daily consolidator and a critical-push classifier as cron-driven background jobs, and exposes a small set of Telegram commands so the user can inspect, edit, or toggle behaviour without leaving the chat.
Persona evolution used to run once a week, hand the model only the current
profile and ask for "one small change"; the reply outcomes were merely a gate.
Now it runs daily (04:15, staggered per agent, personaVoice.cron/timezone
to override) with two brakes kept in .adaptive-learning/persona-evolution-state.json:
at least minDaysBetween days (default 2) since the last change, and at
least minOutcomes (default 10) new real reply outcomes since then, more than
half of them positive. Heartbeat turns no longer count. The model sees the
protected seed, the numbered learned lines, and up to six positive and six
negative excerpts (question, answer, the user's reaction) and answers with
ADD: - …, REPLACE <n>: - … or NONE. The managed block may hold 24 lines
(personaVoice.maxBullets, was 12 including the seed) and the injected
directive cap follows from that (lines × 130 + 80, default 3200 characters).
Existing weekly jobs are migrated to the daily slot on the next setup run.
Twenty-five releases in three days, all live-tested on a three-agent installation. The changelog has the details per version; the highlights:
before_prompt_build handlers one after another, each with its own 15 s
timeout. Reply-outcome tracking ran first and waited for up to twelve
sequential LanceDB updates, so recall started only after that timeout. The
DB work now runs in a serial per-agent queue after the turn's recall
(7.12.30). A new recall prelude log line reports where the pre-recall
time goes; the query embedding runs under a budget (neo.recall.global.embedTimeoutMs).entry.delivery; the resolver reads both the old and the new shape. Voice
notes and photos without a caption now produce a dispatch ticket, and the
fallback warning names the exact step and reason.vectors.<gen>.f32 with byte offsets in vector-index.json
instead of JSON number lists; a 200 MB candidate journal shrank to about
13 MB plus a 38 MB sidecar. Lossless; inline vectors keep being read.
scripts/migrate-neo-vectors-sidecar.mjs migrates existing stores.neo.recall.global). A metadata index (candidate-index.jsonl) keeps
the horizon independent of the journal cap and makes the search cost
60–140 ms after the first read.consolidate-daily now runs optimize() per agent
(dailyConsolidation.lancedbOptimize, versions older than 24 h are pruned).emotion-refine job refines rows the fast tiers were
unsure about, and always rows with importance ≥ 0.9.openclaw plur1bus-command --agent <id> --session <key> [--locale xx] "/…"
runs any chat command as operator against the same Neo store the hooks use.knowledge_update refuses to promote an invalidated memory into canonical
KNOWLEDGE.md, but afterwards removed only the keys it had actually
integrated. Anything unpromotable — invalidated (a soft delete sets both
status="deleted" and epistemicStatus="invalidated") or gone from the
table — stayed queued forever in .adaptive-learning/knowledge-pending.json
and kept inflating pendingCount, so the maintenance nudge told the agent
that N insights were waiting when some of them no longer existed. The queue
now drops those entries on the next run, before the "nothing to integrate"
exit. Pruning is restricted to the ids the query actually asked for, so a
capped query or a failed fetch never drops pending work.MEMORY.md and USER.md
before injecting them (classifyWorkspaceMemoryPaths). PLUR1BUS did not
implement it, the host answered unsupported, logged "excluding automatic
memory context: selected memory runtime does not support provenance
classification" and left both files out of every session — the agent only
saw its curated memory when it read the file by hand. The runtime now
classifies workspace memory paths with the same rules as memory-core
(curated roots and memory/**.md → agent, dreams → system, anything
outside the workspace or non-memory → untrusted).embedding.requestTimeoutMs, default 15 s) and no SDK-side retries. The
OpenAI SDK default of ten minutes let a stalled request hold a recall until
the 20-second worker timeout (started=yes elapsedMs=0 in the log).expiresAt, validFrom, validUntil,
updatedAt, remindAt, …) as JavaScript BigInt. The recall pipeline's
lifecycle gate accepted only numbers, so a BigInt 0n expiry counted as
"expired" and every candidate was rejected before scoring, ACL and
reranking. Auto-recall injected zero memories on every turn and
memory_recall reported "no results" while the rows were in the table with
scores around 0.5. projectRecallEntry now coerces all Int64-backed fields
to safe numbers and isRecallEntryLive accepts BigInt input. Direct
MemoryDB.search was unaffected, which is why manual checks kept passing.Gespräch became Gespra ch, läuft became la uft) and umlaut
stopwords such as über or für never matched. Combining marks are kept
and the text is recomposed to NFC.started=no), reports the wait time, the active slot count and
maxConcurrentRecall, and the job is dropped from the queue instead of
running uselessly later. This is the signal to watch when
runtime.maxConcurrentRecall is too low for the host.<temporal-context> block now states that transcript messages carry no
timestamps and compaction summaries no dates, and that no time or date may
be asserted for earlier conversation content unless it comes from the block,
a memory record's created-at/age attribute or a tool result. Agents had
been filling that gap with invented times (#133).scripts/run-semantic-discover-once.mjs used homedir() without importing
it from node:os and crashed unless PLUR1BUS_VAULT_PATH and
PLUR1BUS_DB_BASE were set. The import is in place.schicht15.maxPromotionsPerRun compared the lifetime number of promoted
cards against the limit, so a workspace that had ever promoted that many
was blocked for good (a live install sat at "7/3" since June with
KNOWLEDGE.md frozen). The limit now counts a 24-hour window; promotions
recorded before timestamps existed do not count, so a stuck workspace
unblocks on the next run. Dedup of already promoted cards is unchanged.criticalPush.hideTypes
(for example ["gesundheit", "geld_konto"]).listen EINVAL before the owner service starts. Linux
keeps its exact address; other Unix platforms elect the owner through a
deterministic exclusive loopback TCP claim that carries no data, while the
data plane stays the private filesystem socket with its 256-bit token. An
oversized macOS socket path (103 bytes) is diagnosed before any mutation./var is
/private/var on macOS), skip the four shared-memory integration paths on
hosts without stable fd-backed directory routing with an explicit reason, and
assert that such hosts reject shared writes without creating a shared root.
Two focused macOS CI jobs run next to the unchanged Linux suite.jinaai/jina-embeddings-v5-text-nano-retrieval as
option 1 and default; OpenAI is option 2, E5 the keyless fallback, Jina v3
stays selectable for existing installs. Non-interactive and dry runs need
PLUR1BUS_ACCEPT_NONCOMMERCIAL_LICENSE=1 to accept CC BY-NC 4.0 and fall
back to E5 otherwise, rather than failing or acknowledging a license silently.embedding.local.maxTokens, default 512 (32 to 8,192).
The same batch then costs +0.6 GB (nano) or +1.1 GB (v3) and 5 or 22 s.
Memory cards are summaries; raise the cap deliberately if long cards must be
embedded in full. The cap is part of the shared model pool identity.jinaai/jina-embeddings-v5-text-nano-retrieval
is pinned (five artifacts, about 265 MB, SHA-256 checked). A jina-v5
runtime branch validates the EuroBERT config, takes the graph's own
sentence_embedding (normalized last-token pooling) or pools over the
attention mask itself, truncates to 32 to 768 Matryoshka dimensions, and
distinguishes queries from documents by the published Query: and
Document: prefixes, refusing any other prefix. Targets jina-v5-nano-*
appear in the schema, the dimension planner, the dashboard switch (same
re-embedding migration), wizard option 4 and installer choice jina5, each
behind the CC BY-NC 4.0 acknowledgement. An option, not a proposal: the
default stays where it is until the PLUR1BUS lab test against v3 is in,
and existing configurations are untouched.memory_forget keeps type = "memory" while its
status becomes deleted. The classify-recent cron took such rows for
fresh candidates, typed them and pushed them as critical, while the review
path (which only reads active rows) never knew the references. Candidate
selection and the classifier now skip every row whose status is not
active; the run result reports them as skippedInactive.before_agent_reply only, which the host does not fire
for a Telegram chat message; the message went to the agent. The handler now
also listens on before_dispatch, where the host hands over the quoted text./plur1bus critical accept all, reject all, or several references in one
command handle every pending review of the authorized scope; unknown or
ambiguous references are reported, the rest is applied.main.telegram.default,
numbered when an account admits several users). The user id itself never
appears on the page.main, and main.dir for the pre-alias
directory identity of the same workspace), and an all-zero list says that
the partitions exist but nothing has been shared into them yet. Paths stay
off the page.openclaw plur1bus-obsidian detect|use|create|confirm --session <key>
called the host's RPC helper as (method, params); its signature is
(method, opts, params, extra), so every method saw an empty request and
nothing was printed. The CLI now passes the parameters in the right slot,
with the method's scope, and writes the result as JSON.vault_write; the discovery also needs semantic_index_write,
granted by the semantic-discovery confirm plan with a confirmed action.
The scheduled run now counts as the operator's standing confirmation; the
vault receipt, mode: apply and allowWrite stay required.plur1bus-obsidian use/confirm --session <key> and the gateway methods
ended with identity_binding_required: the operator-side session resolver
produced only agent and workspace, no user or conversation identity. A
direct-chat session key now resolves to the same identity-bound context
that conversation's chat commands get, so the receipt binds to that
conversation's user. Other session kinds keep the plain agent context.discover-semantic-links cron called the discoverer without a
mutation policy, which the policy layer reads as blocked, so scheduled
discovery never wrote a link. The handler now builds the same
receipt-bound policy per workspace that the bridge service uses. Without a
vault receipt it stays blocked by design.plur1bus-obsidian use/confirm, gateway
methods plur1bus.obsidian.prepare/confirm) failed with "Invalid agent
ID": the handlers read the parameters off the host's method context object
and did not accept the CLI's session name. Both are handled now, and the
agent id falls back to the key's agent:<id>: prefix.obsidianBridge.workspaces list the target probe only
looked at <OpenClaw home>/workspace; the vaults in workspace-<agent>
stayed invisible. Every workspace* directory is probed now.requireVaultPathConfirmation: false the bridge acts without a
receipt, so a configured target now reads "ready" instead of "not yet
confirmed", and the card says how many targets are configured.obsidianBridge.workspaces (entries
with id, agentId, path) or vaultPath. The reviews work without it
through the host's agent workspace; semantic-link discovery and the target
card do not.allow-scripts
only, so the browser silently blocked every native form submission there;
the 7.6.0 switches and the 7.8.0 Compact button only worked in a standalone
browser tab. A writable page now carries one nonce-bound script that posts
the form with fetch and reloads itself; the result shows up as the usual
banner. Read-only pages still carry no script. The script intercepts the
click on the submit button: a sandboxed frame refuses the submission before
it ever fires the submit event (7.8.1 listened there and never ran). The
action travels as a GET with the single-use token in the query, because the
host authenticates the frame's cookie for GET only and answers a POST from
the opaque origin with 401 (7.8.2 hit that). 7.8.4 routes the Compact
button through the db-adapter, which owns optimizeTable; 7.8.3 asked the
pool's raw store objects and failed.controlUi.writeActions: "all",
each private partition under "Cards by agent" gets a button that runs
LanceDB's fragment compaction (table.optimize()) for that partition in
the background, one at a time. The adapter had the primitive since August;
nothing ever called it. The row shows progress and the result.openclaw plur1bus-feature-cron --help takes longer than the old
five-second budget, so every run took the fail-closed branch and
safety-disabled the direct jobs. Budget is 30 s now.main is the default
agent when the host flags none._neo. Reserved
store directories are skipped by the health scan.DREAMS.md, in the host's entry
shape inside its managed diary block. Only the agent's private partition
writes there; shared partitions never do. dreaming.narrative.diary turns
it off. To give the diary one author, switch the host's own managed dreaming
off. The host reads that switch from the memory slot owner's entry, so it is
plugins.entries.memory-lancedb-namespaced.config.dreaming.enabled: false,
not memory-core's flag.The operator tab can now change two things, if you let it. controlUi.writeActions
is off by default, so nothing changes for an existing install.
reranker makes the reranking choice switchable from the page: local BGE,
local JinaAI (both keyless), Cohere when a key is configured, or off. That is a
runtime choice with no data migration behind it.all additionally exposes the embedding target and the re-embedding
migration: dry run, copy, and a separate switch. The confirmation token that
binds those steps stays inside the Gateway and never appears in the browser.Every change carries a single-use form token from the page render it was clicked
on, because the tab cookie the host mints is SameSite=None. While
writeActions is off the page renders no form, forbids form targets in its
Content-Security-Policy, and refuses POST outright.
Also fixed: the reranking card used to name jina as a reranker.provider
value. The schema rejects it. Jina reranking is a local model, selected through
reranker.local.model.
OPENAI_API_KEY (and OPENAI_API_KEY_FALLBACK for
the fallback embedder) when neither apiKey nor apiKeyEnv is set, which
is how a default install carries its key. The table now follows the same
rule, names the variable it found, and never reads its value. Keyless
providers show not required; an unconfigured optional fallback shows
optional.missing badge. They need no
key of their own, so the status says so instead of reading like a fault.reranker.apiKeyEnv was labelled reranker.apiKey, sending the reader to a
line that does not exist in their config.host_route is explained. The source had no legend entry, and the
missing help text claimed the feature stays off — untrue for exactly the
capabilities that report it.enabled: false still turns them off.apiKeyEnv too. A working Cohere reranker
configured through an environment variable was reported as missing. The five
capabilities that fall back to OpenClaw's own model route are now marked as
host-routed instead of looking unconfigured.cohere, jina and the local BGE fallback.gateway-runtime dispatcher capabilities. Missing
capabilities fail closed; PLUR1BUS never rewrites OpenClaw source, dist, or
node_modules files.2026.8.1/2026.8.2, while runtime behavior is guarded
by feature detection rather than version-string branches. OpenClaw
2026.9.1 stable is additionally verified (see the compatibility contract).observed, every other new write as explicit untrusted; nothing invents
trusted any more. The skill miner clusters observed | corroborated | trusted
plus valid pre-cutoff legacy rows and no longer applies a 30-day lookback, so
an existing install keeps mining its history instead of reporting scanned: 0.
The cutoff marker is written once, at the first upgrade, before the first write.SKILL.md is written first (tmp + fsync +
rename), then the evidence transitions; a partial failure stays
activation_partial and can be re-applied idempotently.updateCard, compaction, auto-capture, light-dream rewrite — checks the
tombstone registry before table.add. Same-text replay by the user is still
allowed.recall.globalInjectMaxChars, default 17000) trims
memories before time and reminder context, so a large recall can no longer
crowd the rest of the prompt out./plur1bus curation resolve <keep|drop> ends a
neo conflict without any hard filter, and /plur1bus curation drop-injected
demotes only injected behaviour conflicts after a preview and a nonce —
genuine conflicts are never touched.Condensed summary of 7.3.0–7.3.5; see the changelog for detail.
fakt was
missing from the type enum, so every classification run failed validation.maxMemoryCount; before this, garbage collection had no
scheduler at all.importance = 1.0 works again (7.3.4) — the value is the agent's reserved
manual core marker; it was silently ignored because the core score also demanded
an emotional intensity the agent cannot set. Core scores are now normalised to
the features that actually exist.thinking: off.sharp@0.35.3 close the dependency audit; PLUR1BUS now requires Node.js 22.22 or newer.npm audit reports 0 vulnerabilities.rem-dream when merging.enabled: true is explicitly authored, instead of shipping an enabled handler without a scheduler binding./state command fix — The top-level status command no longer crashes on an out-of-scope ctx reference.typescript is now declared as an optionalDependency so the code-index feature works out of the box without forcing TS on all users.buildInstallLogEvent now correctly passes featureMode instead of hardcoding "preserve"; dry-run vs. remote-target warnings are properly distinguished; dead code removed.jq subprocess calls consolidated into batch eval+@sh extracts.await on drainEmbeddingQueue() caused the unresolved Promise to be serialised as {} in postMessage; callers now receive correct drain results.statSync race condition fixed (file deleted between readdirSync and statSync); addQueryVector null-return guard added.symbols.find() in AST visitor replaced with a Map for O(1) lookup.openclaw.plugin.json version aligned with package.json.scope: "user" writes now require an authenticated user identity (user scope is owner-bound) and are filtered in recall/visibility checks..openclaw/extensions/emotional-state-injector/ files are included in the npm tarball; runtime activation still requires the OpenClaw plugin entry/allow config and a gateway restart..plur1bus/code-index.json and can render bounded <code-context> query output.PLUR1BUS can build a local JS/TS code index without CocoIndex:
npm run code-index -- /path/to/workspace
npm run code-index -- /path/to/workspace --query "/plur1bus code-index"
The index is written to .plur1bus/code-index.json and contains normalized files, symbols, import/call/register edges, and symbol chunks. --query prints a bounded <code-context> block from the generated index. It uses the TypeScript Compiler API through the optional typescript dependency and keeps the PLUR1BUS schema independent of the parser implementation.
/plur1bus obsidian conflicts build renders them as Obsidian pages./plur1bus start onboarding — Read-only status and onboarding guidance for feature profiles and vault setup.scripts/auto-capture-lancedb.mjs gains workspaceKey field; schema migration is backward-compatible and idempotent.shouldSkipAutoRecallForInternalTurn prevents feedback loops when the gateway injects synthetic cron messages./mf +/-/~). Coverage-gap detection finds topics with few or weak memories.sessionThreshold (default: 50) or intervalDays (default: 7) is reached. Optional LLM-generated natural-language report._meta-cognition-state.json per workspace.minClusterSize, similarityThreshold, maxNudgesPerDay.gpt-4o-mini is only an explicit override example.emotionTier to a specific tier or use auto for dynamic escalation.--explain flag for /memory) — Human-readable rationale per result: score breakdown, boost factors, temporal relevance./mf feedback for recall-quality improvement./correct treated as recall event — After safeUpdate() inserts the corrected card, applyRetrievalReinforcement refreshes lastRetrievedAt, increments retrievalCount, and boosts memoryStrength.getById(newId) races or fails, reinforcement is silently skipped; the correction itself is never rolled back.searchByTopic, getCard, and recall pipeline filter by ACL. Unauthorized access is logged./mf) — Thumbs-up/down/neutral feedback on any memory result. Persisted per workspace./share) — Copy any card into a workspace-shared pool with ACL protection.maxPromptMemories (default 12), dedup threshold 0.78, acronym recognition, canonicalMaxItems (default 5).halfLifeDaysMap: transient (60d), episodic (180d), longContext/project (600d)./memory, /forget, /correct accept any length. >6k chars are semantically compressed; >100k chars prompts for file/vault source.recommended profile (all features active, Obsidian/reviews marked pending_setup). Core memory works immediately; advanced features require explicit confirmation.merge-proposals.jsonl, never auto-applies.recommendation (review_only or apply_via_safe_reconsolidation), never modifies memory directly.memoryId + optional contentHash.mode: "apply" creates per-file backups, manifest (beforeHash/afterHash), and audit-log entry. Vault path confirmation required before first write.These features run after normal recall and only append results; they never replace the primary recall result and never write memory data.
Reads a precomputed .plur1bus/semantic-lens-index.json from the workspace and adds a small number of community/bridge/faded memories that normal recall may have missed.
enabled: false in schema.maxLensMemories: 3, maxBridgeMemories: 2, maxFadedMemories: 1, maxCommunities: 2.MVP reactivation hook that appends a <memory-reactivation> block when a conversation appears to resume after an idle gap, a compaction, or a continuation signal.
enabled: false in schema; visibleHints: false.maxReactivationMemories: 3, maxFadedReactivationMemories: 1, maxOpenThreads: 3, maxCommunities: 2.Record notes can contain an idempotent managed block (id="graph-links") with wikilink edges. The block is regenerated, not appended, and conflicts with manual edits are reported.
explicit (memoryIds/sourceRefs), type (type-based rules), semantic (precomputed link index).semanticDiscovery builds .plur1bus/link-index.json from memory mirrors + vectors behind a confirmation gate; it is not auto-applied.Memory mirrors use technical filter tags, not semantic memory tags:
plur1bus/memoryplur1bus/agent/<id>plur1bus/workspace/<id>plur1bus/category/<cat>plur1bus/scope/<scope>These tags are used for vault filtering and graph grouping; they do not carry semantic memory content.
| Command | What it does |
|---|---|
/state | Status snapshot: memory card count, sync state, last plausibility run, any open issues with reason + fix hint. |
/memory <query> | Search the agent's memory via the recall pipeline. Accepts queries of any length. Add --explain for result rationale. |
/forget <text> | Forget a memory card. Archive-first guarantee — the card is JSON-archived before deletion. |
/correct <old> zu <new> | Update a memory card. Archive-first guarantee. Accepts zu, →, or -> as separator. |
/mf <id> + / - / ~ | Feedback on a memory result: 👍 positive, 👎 negative, ~ neutral. Persisted per workspace. |
/share <id> | Copy a memory card into the workspace-shared pool. ACL-protected. |
/enable <feature> | Turn on a whitelisted feature (vaultSync, kritischPush, dailyConsolidation). |
/disable <feature> | Turn off the same. Writes atomically into openclaw.json; gateway restart required. |
/plur1bus setup | List the available profile choices without changing configuration. |
/plur1bus setup safe | Explicitly apply the Safe profile; core capture/recall stays usable and additional mutators remain off. |
/plur1bus setup recommended | Explicitly apply Recommended while preserving existing opt-outs and write-safety gates. |
/plur1bus start | Show read-only status and onboarding guidance; it does not change configuration. |
/plur1bus subcommands| Command | What it does |
|---|---|
/plur1bus skills review | Show open skill proposals. |
/plur1bus skills approve <id> | Approve a skill proposal. |
/plur1bus skills reject <id> | Reject a skill proposal. |
/plur1bus skills list | Show active skills. |
/plur1bus skills show <id> | Show proposal details. |
/plur1bus reminders list | List active reminders. |
/plur1bus reminders cancel <id> | Cancel a reminder. |
/plur1bus obsidian dashboards build | Build Obsidian dashboard pages. |
/plur1bus obsidian conflicts build | Build conflict report pages. |
/plur1bus doctor | Run diagnostics and show runtime status. |
/plur1bus internal proactive-check | Run proactive nudge detection manually. |
/plur1bus internal meta-reflect | Run meta-cognition reflection manually. |
/plur1bus internal afterthought | Run the delayed follow-up job manually (see below). |
When the last conversation ended 30–120 minutes ago with an open outcome (the user asked for details, or the topic was dropped mid-thread), the plugin can compose a short, casual follow-up message ("Mir ist zu … noch eingefallen…"). This is gated by the shared proactive governor budget, capped at one per day, and skipped for any topic already surfaced as an open thread today. Recommended cron: every 3 hours, run the exact command /plur1bus internal afterthought with announce delivery. The plugin command returns either a validated reply payload or OpenClaw's NO_REPLY suppression token. PLUR1BUS submits the exact allowlisted command through OpenClaw's public gateway-runtime dispatcher, so OpenClaw owns status finalization and at-most-once announce delivery without an outer carrier-model run. If the required native capability is unavailable, provisioning fails closed and leaves the feature job inactive. Custom prompts, surrounding whitespace, prefixes, and suffixes are never claimed.
Setting this cron up is automatic when its raw feature gates are explicitly enabled — see below.
node scripts/setup-feature-crons.mjs verifies the public native command-dispatch capability first. When healthy, it loads exactly one validated configuration snapshot with openclaw gateway call config.get --json, discovers bound agents, and idempotently plans up to ten jobs per agent plus one install-wide GC job. It fails closed without normal cron planning when the capability is absent, the gateway call fails, JSON is invalid, valid !== true, or sourceConfig/runtimeConfig is not a plain object. Custom prompts and unrelated jobs remain untouched. It never falls back to local config files or alternate raw/resolved fields.
The capability probe allows the host CLI 30 seconds per help call (7.8.0); a booting host with many plugins needs more than the former five. Ownership is by exact command: an operator-named job that runs /plur1bus internal <feature> for an agent is that agent's job, and only PLUR1BUS-named jobs are eligible for schedule migration. A non-delivery feature whose only owned jobs are disabled gets its best candidate re-enabled; the singleton collector is satisfied by a job on any agent; when openclaw agents list flags no default agent, main is treated as the default.
The two configuration views have separate roles: sourceConfig alone controls explicit raw feature gates and the raw skillMiner schedule; runtimeConfig alone controls effective bindings, accounts, and delivery. Runtime defaults cannot enable jobs. The eligible jobs are:
persona-evolve: personaVoice.enabled && skillMiner.enabled; daily 04:15 local time (7.12.38; before: Sunday), staggered five minutes per agent, overridable via personaVoice.cron/personaVoice.timezone; no delivery. The shipped weekly slots are migrated to daily on the next setup run; operator schedules stay.afterthought: afterthought.enabled && (skillMiner.enabled || merging.enabled); every 3 hours; exact-command announce delivery with a direct text/NO_REPLY result.consolidate-daily: dailyConsolidation.enabled; daily 04:00 in Europe/Berlin; no delivery.classify-recent: criticalPush.enabled; every 3 hours; safe announce delivery of approved pushes or NO_REPLY.rem-dream: merging.enabled; daily 01:15 in Europe/Berlin; no delivery.skill-miner: skillMiner.enabled; raw Croner-compatible cron/timezone after conservative syntax validation, defaulting to Sunday 03:00 in Europe/Berlin (timezone: null means local time). Invalid literals, descending ranges (including named month/day ranges), names, modifiers, or literal-step forms are ineligible.discover-semantic-links: obsidianBridge.enabled && obsidianBridge.graphLinks.semanticDiscovery.enabled; daily 02:00 in Europe/Berlin; no delivery.gc-run: gc.enabled; daily 04:45 in Europe/Berlin, after consolidate-daily has produced the candidates; no delivery. This one is a singleton — runGcJob iterates over every agent database itself, so exactly one job is planned regardless of how many agents the install has.Every job runs with --agent <agentId> --session isolated. Provisioning does not set model, fallback, token, auth, API, or other credential overrides, so OpenClaw's default LLM and per-agent credentials remain authoritative. The script remains idempotent and exit-0 for install safety, so it can run from any of these channels:
npm install/npm postinstall — fires when the plugin is installed via npm install (e.g. npm install -g @cyb3rb1ade/plur1bus-memory).gateway_start handler schedules a bounded reconciliation after the gateway
becomes reachable. Missing capabilities or invalid config leave exact owned
feature jobs inactive; retries use bounded backoff. Disable provisioning with
"featureCronSetup": { "auto": false }./plur1bus setup crons (optionally --agent <id>/--account <acct> to force single-agent mode).The /plur1bus doctor and /plur1bus status feature-cron hint is condition-derived, not "have we shown this before": it reads the marker file and only surfaces a hint when setup has never run, ran under an older plugin version, or ran but couldn't create everything it planned (some crons are still pending — e.g. no delivery target could be derived). It's silent once a current-version run reports nothing left to create.
bindings > 0 (i.e. an actual chat channel routes to them) get feature crons. Subagents (bindings === 0 — researchers, deep-divers, and other internal-use-only agents) are deliberately excluded; they have no chat to receive an automatic persona evolution or an afterthought delivery, and running these jobs against them would be pure compute waste.isDefault first, then most bindings, then alphabetically-first id) to avoid two crons double-firing against the same state files.plur1bus <feature> <agentId>. An existing job is owned only by an exact, case-sensitive agent id plus either its exact canonical name or exact first command line; missing or different agents are untouched. Every exact owned duplicate is inspected and reconciled, even when another duplicate is already safe.allowFrom. Delivery-required jobs use only a conservatively validated Telegram binding match.peer.id (including t.me/<handle>) or effective account/root defaultTo. Every relevant non-ACP binding must agree on channel, and an account inherits only when match.accountId is truly absent. Omitted accounts resolve in order from an explicit valid defaultAccount, accounts.default, one sole named account, or a root account proven by configured botToken/tokenFile; routing fields alone never invent a root default account. Unsupported providers, wildcard, placeholder, redaction, zero-id, disabled-account, explicit empty/missing account, mixed-account, and conflicting target/channel/account states are rejected. Existing delivery seeds require exact mode: "announce"; case or whitespace variants are unsafe. A job without a validated target is created disabled with --no-deliver; every unsafe owned delivery job is disabled and stripped of delivery. Non-delivery jobs retain only missing delivery or exact mode: "none"; every other delivery object is removed.openclaw agents list --json fails, is unparseable, or yields no bound agents, no cron is mutated. Passing a validated --agent <id> forces one explicit agent; missing, option-like, or invalid --agent/--account values fail closed, and --account without --agent is rejected.PLUR1BUS 7.7.1 requires Node.js 22.22 or newer and OpenClaw 2026.8.1 or newer.
On an older host the installer refuses the package instead of deploying it:
requires plugin API >=2026.8.1, but this OpenClaw runtime exposes <version>.
Install the published release through OpenClaw's package installer:
openclaw plugins install clawhub:@cyb3rb1ade/plur1bus-memory@7.5.3 \
--acknowledge-clawhub-risk --pin
The same release is on the npm-compatible registry, if your @cyb3rb1ade
scope already points there:
openclaw plugins install @cyb3rb1ade/plur1bus-memory@7.5.3 --pin
Or install the immutable GitHub Release tarball:
openclaw plugins install \
https://github.com/Cyb3rb1ade/openclaw-plur1bus-memory/releases/download/v7.5.3/cyb3rb1ade-plur1bus-memory-7.5.3.tgz
To build from this source checkout instead, produce a tarball and install that artifact rather than linking the directory; a source link is not an equivalent package-compatibility test:
npm ci
npm test
npm pack
openclaw plugins install \
npm-pack:/absolute/path/cyb3rb1ade-plur1bus-memory-7.5.3.tgz --force
Record the tarball's SHA-256 before transferring it. PLUR1BUS 7.5.3 never patches OpenClaw runtime files. Existing release artifacts remain unchanged and must not be relabelled as 7.5.0.
Restart the gateway after installing, so the new plugin version is loaded.
Then add a plugins.entries["memory-lancedb-namespaced"] block to your openclaw.json (see below).
Minimal config block in openclaw.json. This is an explicit override example:
the named gpt-4o-mini value and its credential are illustrative user choices,
not PLUR1BUS defaults.
{
"skills": {
"workshop": {
"autonomous": { "mode": "propose" }
}
},
"plugins": {
"slots": {
"memory": "memory-lancedb-namespaced"
},
"entries": {
"memory-lancedb-namespaced": {
"enabled": true,
"hooks": {
"allowConversationAccess": true
},
"config": {
"baseDbPath": "~/.openclaw/memory/lancedb-namespaced",
"obsidianBridge": {
"enabled": true,
"mode": "augment",
"vaultPath": "~/.openclaw/vault",
"backupBeforeApply": true,
"auditLog": true,
"requireVaultPathConfirmation": true
},
"emotion": {
"tier": "auto",
"t2": { "enabled": true },
"t3": {
"enabled": true,
"model": "gpt-4o-mini",
"apiKey": "${OPENAI_API_KEY}",
"escalationConfidence": 0.85,
"timeoutMs": 4000
},
"moodInfluence": 0.3,
"intensityHalfLifeFactor": 1.0,
"temperaments": {
"bernhardine": { "preset": "warm", "baseline": { "joy": 0.35, "trust": 0.5 }, "sensitivity": 1.5, "decayMultiplier": 1.3 }
}
},
"dailyConsolidation": {
"enabled": true
},
"dreaming": {
"enabled": false,
"narrative": { "enabled": true, "storeAsMemory": true }
},
"merging": {
"enabled": true,
"mode": "safe-versioned",
"autoApply": false
},
"reranker": {
"enabled": true,
"timeoutMs": 5000,
"fallbackOnError": true
},
"security": {
"allowChatConfigCommands": true,
"allowModelDestructiveMemoryOps": true,
"allowedUserIds": [],
"allowedChatIds": []
},
"runtime": {
"embeddingCacheEnabled": true,
"embeddingCacheMaxEntries": 128,
"embeddingCacheTtlMs": 300000,
"embeddingCacheScope": "agent",
"llmResultCacheEnabled": true,
"llmResultCacheTtlMs": 86400000,
"llmResultCacheMaxEntries": 256,
"llmResultCachePersist": false,
"llmResultCacheMaxBytes": 67108864,
"llmResultCacheMetrics": true
}
}
}
}
}
}
dreaming.enabled: false is the OpenClaw memory-core sidecar gate, not the
PLUR1BUS narrative toggle. Keep it false when PLUR1BUS owns consolidation or
REM; otherwise OpenClaw also loads memory-core dreaming. When skillMiner is
enabled, use Skill Workshop autonomous propose or off so both learning
systems share the governed proposal queue without independently applying
overlapping skills.
hooks.allowConversationAccess: true is mandatory for this trusted memory
plugin. It authorizes the official typed before_agent_reply hook used for
automatic recall and for the fail-closed admission boundary of exact feature
commands; OpenClaw otherwise withholds the conversation body. The installer
enforces this single permission even in preserve mode while keeping all
unrelated hook and feature choices unchanged.
All paths default to $HOME/.openclaw/... if omitted. OPENCLAW_CONFIG_PATH and OPENCLAW_HOME env vars override the lookup of the gateway config file used by the toggle commands.
PLUR1BUS ships a keyless fallback for both stages and recommends something stronger for each. The two choices are not equal in weight: the embedding model decides how well recall separates hits from noise, the reranker only refines the candidates the embedding already found.
Embedding. The fallback intfloat/multilingual-e5-small (fixed 384
dimensions) packs its vectors into a narrow cone. Texts that have nothing to do
with each other still land at a cosine similarity around 0.84, which is a recall
score of roughly 0.86 on the 1 / (1 + distance) scale PLUR1BUS uses. Hits and
background noise sit so close together that neither the ranking nor a threshold
can separate them cleanly, and the band 0.96 to 1.0 reserved for agent-marked
cards as well as the duplicate threshold of 0.95 lose their meaning. This is
exactly what the compatibility lab showed when it measured with E5 and no
reranker. The recommended model spreads similarities much wider:
jinaai/jina-embeddings-v3 (multilingual, Matryoshka dimensions from 32 to
1024, CC BY-NC 4.0 license consent required). With it, ranking and thresholds
do their job. Since 7.12.0 the installer's first option is Jina v5 Text Nano
(see the lab test below); OpenAI is the hosted alternative, Jina v3 stays
selectable for existing installs, and E5 is only the small keyless fallback. The embedding model is switched from the PLUR1BUS tab
as well (controlUi.writeActions: "all"): the button picks the target model,
model preparation downloads and verifies it, and the re-embedding migration
then runs from the same page with a dry run, a copy, and a separate switch.
Unlike the reranker switch, every card is embedded again along the way, so
it takes time and disk space, and the old generation stays available for
rollback.
Which dimension. Jina v3 is trained with Matryoshka representation learning, so the vector can be cut to 32, 64, 128, 256, 512, 768 or 1024 dimensions. The Jina paper (table 7) reports retrieval nDCG@10 of 63.35 at 1024, 63.30 at 768, 63.16 at 512, 62.72 at 256, 61.64 at 128, 58.54 at 64 and 52.54 at 32. Keep the PLUR1BUS default of 1024: at memory scale the vectors are small anyway (50,000 cards are about 200 MB of float32 vectors), and the duplicate threshold of 0.95 and the reserved band above 0.96 are calibrated on full-width similarities. 512 is the economical choice for very large stores or weak hardware; it halves storage and ANN cost for a loss of 0.2 points. Do not go below 256: from 128 down the recall loss becomes visible and the similarity spread compresses, which moves every threshold. The dimension is baked into the table, so changing it later means another re-embedding run.
Jina v5 Text Nano (7.11.0), an option with the lab test pending. The
EuroBERT-based nano model (239M parameters, 12 layers, 768 dimensions with
Matryoshka down to 32, 15 European languages including German, CC BY-NC 4.0)
runs as the upstream Q8 export at roughly a quarter of v3's compute and about
265 MB on disk; on the reference machine one card embeds in about 0.45 s and
the process grows by about 120 MB. Jina reports MMTEB 65.5 against 64.44 for
v3. Queries and documents are told apart by the Query: and Document:
prefixes. Whether it becomes the proposal for new installs is decided by the
PLUR1BUS lab test on real memory cards, not by the version number. That test
ran on 5 September 2026 (OpenClaw 2026.9.1, Transformers.js 4.2.0, 12 cores
shared with other work): 1,031 active cards of one agent, 72 real recall
queries from the gateway log, the production ranking (OpenAI
text-embedding-3-large, 3,072 dimensions) as reference, both models through
the real provider with the 512-token cap, in separate processes, back to back.
| Jina v5 Text Nano, 768d | Jina v3, 1024d | |
|---|---|---|
| Production top-1 found at rank 1 / within top 5 | 48.6 % / 70.8 % | 47.2 % / 73.6 % |
| Overlap of the top 5 with production | 47.2 % | 46.1 % |
| Median top-1 similarity / median noise band (ranks 11 to 50) | 0.615 / 0.333 | 0.721 / 0.541 |
| Margin top hit over noise, median / 10th percentile | 0.242 / 0.132 | 0.153 / 0.100 |
| Distinct-card pairs at or above 0.95 / 0.96 (production: 59 / 49) | 90 / 81 | 84 / 66 |
| Model load | 5.1 s | 14.1 s |
| One card in a batch of 8 (migration path) | 478 ms | 1,556 ms |
| One card alone (capture path) | 313 ms | 416 ms |
| One query (recall path) | 256 ms | 124 ms |
| Process growth after load / peak | 444 MB / 1.2 GB | 1,120 MB / 2.3 GB |
Reading it: on ranking quality the two are level within the noise of 72 queries. Nano separates hits from the noise band much more clearly (v3 compresses similarities into a narrow band around 0.54, which is what makes thresholds hard to set), embeds cards three times faster in batches, loads in a third of the time and needs half the memory; v3 answers a single query twice as fast, which at a quarter of a second is not a user-visible difference. Both models push more distinct pairs above the 0.95 duplicate threshold than the production model, mostly genuine near-duplicates. On these numbers the maintainer made Nano the proposal for new installs in 7.12.0 and recommends existing installs the migration from the dashboard: prepare the Nano target in Model Preparation, run the dry run, copy, then switch; the old generation stays for rollback. Nothing migrates on its own.
Reranking. The reranker reviews the 40 candidates the vector search returns
and removes the ANN noise. Two local models are pinned, both quantized ONNX
through Transformers.js: bge-reranker-v2-m3, which the installer proposes
and the runtime uses when nothing else is configured, and
jinaai/jina-reranker-v2-base-multilingual, available under the wizard's
advanced options and as a dashboard switch. Switching between them is a
runtime choice without any data migration (see controlUi.writeActions). The
two models differ in size and in what they are good at:
| Jina reranker v2 | BGE reranker v2-m3 | |
|---|---|---|
| Parameters, layers | 278M, 12 layers | 568M, 24 layers |
| Context per query+document | 1,024 tokens | 8,192 tokens |
| Quantized ONNX artifact | 280 MB | 570 MB |
| License | CC BY-NC 4.0 | Apache 2.0 |
| BEIR nDCG@10 (English) | 53.17 / 57.06 | 53.65 / 56.51 |
| MKQA (26 languages) | 54.83 / 67.90 | 54.17 / 67.88 |
| MIRACL nDCG@10 (18 languages) | 63.65 | 69.32 |
| MLDR recall@10 (long documents) | 68.95 | 59.73 |
| CodeSearchNet MRR@10 / CoIR nDCG@10 | 71.36 / 56.14 | 62.86 / 35.97 |
| ToolBench recall@3 | 77.75 | 78.46 |
Where two numbers are given, the first comes from the Jina model card and the second from the jina-reranker-v3 paper; the two evaluations use different candidate sets, so compare within a column pair, not across. On English and on the 26-language MKQA set the two are level. BGE is clearly ahead on MIRACL, the broad multilingual retrieval benchmark, and slightly ahead on tool retrieval. Jina is clearly ahead on long documents and on code, and at half the depth it reranks noticeably faster on CPU; Jina quotes up to 15x the document throughput of BGE with flash attention on a GPU. For PLUR1BUS memory cards, which are short German or English summaries, multilingual ranking quality is what counts, and long documents and code do not occur. The recommendation is therefore BGE: ahead or level on the benchmarks that matter here, Apache 2.0 without a license consent, at the price of roughly twice the CPU time per query, which is a fraction of a second for 40 short candidates on an ordinary server. Pick Jina only when a weak CPU runs into the reranker timeout of five seconds; the quality loss is small.
Sources: Jina model card, BGE model card, jina-reranker-v3 paper, table 2, Jina Reranker v2 announcement.
Settings for both stages are described in docs/configuration.md; the installer offers the preparation profiles for the local models.
Omitting namespaces preserves the legacy-flat layout exactly:
{baseDbPath}/{agentId}. Named routing is enabled only by supplying the strict
object explicitly:
{
"baseDbPath": "~/.openclaw/memory",
"namespaces": {
"activeWriteNamespace": "lancedb-local",
"activeRecallNamespaces": ["lancedb-local"],
"legacyReadOnlyNamespaces": ["lancedb-namespaced"],
"crossNamespaceRecall": true
}
}
An explicit baseDbPath may be the named root, as above, or the active writer
leaf (~/.openclaw/memory/lancedb-local); both forms resolve to the same
layout. Namespace identifiers must match
^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$. The writer must occur in active recall,
and active namespaces must be disjoint from legacy read-only namespaces.
Legacy namespaces participate only when crossNamespaceRecall is exactly
true; they are opened without table creation, schema migration, or mutation.
Every recalled table must use the configured embedding dimensions. Each table runs the existing recall pipeline. When multiple live tables participate, PLUR1BUS waits for all of them and performs one stable global score merge; the one-table path remains direct. Duplicate IDs and canonical heading/text are collapsed, canonical plus memory results share the configured output cap, and child decision traces are replayed through the existing trace caps. A namespace changes storage routing for the current agent only. Sharing between agents, workspaces, or users is separate ACL work owned by B13.
Chat models are selected per owning feature. If an optional feature model is
absent, PLUR1BUS uses the effective OpenClaw agent model and sends no model
property. Features never inherit merging.model, its endpoint, credential, or
headers. Existing feature/profile activation, budgets, confirmation gates,
rate limits, and fail-soft behavior remain unchanged; Safe produces zero
PLUR1BUS native/direct chat calls.
The four selection modes are openclaw-default (native with no model),
openclaw-override (feature-local model through OpenClaw), direct-override
(feature-local model plus direct transport), and unavailable. failed is the
stable diagnostic outcome when a selected transport rejects. Provider/model
metadata returned by OpenClaw may be recorded without credentials, prompts, or
headers. Native routes bypass the PLUR1BUS result cache; complete direct routes
retain exact caching.
Direct transport without a feature-local model fails closed and sends no
request. A configured credential that is unresolved is unavailable; it never
falls through to native OpenClaw host credentials and does not abort plugin
registration. runtime.llm.complete missing or unavailable is fail-soft and
does not select a hard-coded model.
A session-bound command capability omits agentId. Global hook, tool, and
background calls retain the target agent and require entry-level
llm.allowAgentIdOverride:true. A model-only native override requires
llm.allowModelOverride:true and obeys allowedModels. Installer preserve
never grants LLM trust, and neither Safe nor Recommended adds those entry-level
bits.
runtime.llm.complete resolves the effective primary selection and does not
execute the configured model fallback array in the installed runtime. PLUR1BUS
neither claims nor emulates a host fallback chain.
PLUR1BUS caches only exact, agent-scoped results from an explicit allowlist of deterministic internal LLM transformations. The default in-memory cache uses a 24-hour absolute TTL (llmResultCacheTtlMs: 86400000, clamped to 60 s–7 d) and holds 256 entries per plugin registration (llmResultCacheMaxEntries, clamped to at most 10,000). Optional prompt-free SQLite persistence is off by default; when enabled with llmResultCachePersist, it stores hashed keys, results, usage metadata, and timestamps under the memory database path without storing plaintext prompts, credentials, or headers. llmResultCacheMaxBytes defaults to 67,108,864 bytes and is clamped to at most 1 GiB; clamped values log a warning.
The six runtime settings are llmResultCacheEnabled (default true), llmResultCacheTtlMs (default 86400000), llmResultCacheMaxEntries (default 256), llmResultCachePersist (default false), llmResultCacheMaxBytes (default 67108864), and llmResultCacheMetrics (default true). Missing values come from the manifest; an explicit false remains authoritative.
Operational notes:
node:sqlite module available throughout the supported Node.js runtime range; if SQLite initialization is unavailable, the cache falls back to memory-only.0o700, file 0o600 under the memory database path). Responses may contain condensed memory content — enable persistence only where that is acceptable.temperature: 0 for determinism, and llm-call.js now actually forwards temperature to the provider (previously the setting was silently ignored). Existing configs that set temperature therefore change their effective provider behavior.Non-goals and bypasses:
wie wird das Wetter morgen?.The /state status section reports cache hit rate, memory/persistent hits, persistence state, and avoided input/output tokens. It intentionally reports token counts, not money.
emotion.t3 — the tier-3 emotion classifier uses the effective OpenClaw
agent model when its model is absent. A complete feature-local direct override
may instead provide its own model and transport. If neither route is available,
the classifier falls back to Tier-2 heuristics: it does not label cards, so
it never poisons results by marking everything fakt.
emotion.temperaments — per-agent emotional temperament. Ships with generic defaults only (main slightly more sensitive, everyone else balanced). Pick a preset via /plur1bus temperament <preset> (ausgewogen, warm, kühl, feurig, stoisch) — requires a gateway restart. Mood always derives from conversation content; the temperament only shapes how strongly and how long it swings. The current mood is written to .emotional-state.json (machine-readable, survives restarts) and .current-mood.txt (human-readable) in the agent workspace, injected as a mood line into the recall context, stamped on every memory card (moodContextAtCapture), and emotionally intense memories decay slower (intensityHalfLifeFactor).
security.allowedUserIds / security.allowedChatIds — identity-aware authorization for commands and destructive flows.
userId membership in allowedUserIds (chatId alone is never sufficient), plus allowedChatIds when that list exists./enable, /disable, /plur1bus setup, /forget, /correct and confirmation flows.security.allowChatConfigCommands (default true) — disables operator-level config mutating commands (/enable, /disable, /plur1bus setup) when set to false. Use this in shared channels if you want a hard stop on chat-driven writes. Writes are still guarded by a file lock.
security.allowModelDestructiveMemoryOps (default true) — keeps model-facing tools memory_forget and knowledge_update available unless you explicitly disable them.
scope values now support agent-private (default), workspace and user.
agent-private remains per-agent.workspace shares by workspace.user is owner-bound: der aufrufende userId wird gespeichert und bei Sichtbarkeit/Mutation geprüft./share <id> kopiert eine sichtbare Karte nach bestätigter, an Benutzer und
Chat gebundener Bestätigung in den Workspace-Pool. /share <id> --user nutzt
dieselbe Bestätigung, erzeugt aber einen nur für denselben Kanal, Account und
Benutzer sichtbaren User-Pool. Die Grammatik ist strikt: nur ein vollständiges
UUID-id, optional genau --user, oder /share confirm <nonce> sind gültig;
unbekannte oder doppelte Optionen werden vor jedem Store-, DB-, Embedding- oder
Provider-Zugriff abgelehnt. Eine Freigabe ist copy, never move: die private
Ursprungskarte bleibt unverändert, und die autorisierte Shared-Kopie enthält
einen kanonischen Origin-Verweis. Recall darf die optionalen Shared-Quellen
ergänzend lesen und dedupliziert den kanonischen Ursprung; sie ersetzen weder
primären Recall noch dessen ACL.
Physische Routen sind kein benutzergesteuerter Pfad: ihre Segmente sind höchstens
64 Zeichen lang und werden als .plur1bus-shared/workspaces/w-<62hex> oder
.plur1bus-shared/users/u-<62hex> abgelegt. Die Berechtigung bindet den
kanonischen Workspace konfliktablehnend (keine versteckte Alias-Priorität) und
den vollständigen Kanal+Account+Benutzer-Prinzipal. Fehlende oder abweichende
Bindungen sind nicht sichtbar und nicht mutierbar; fehlend und verweigert
werden gleich behandelt. /memory und /share --user verwenden den direkt
vom Host gelieferten Account.
Der aktuelle OpenClaw-Hook kann die optionale automatische User-Shared-Recall
Quelle ausschließlich bei aktiviertem autoRecall verwenden. Er benötigt
einen account-tragenden Session-Key, ein exaktes Host-Run-Ticket oder eine
konservative default-only Account-Topologie. Native und Slash-Kommandos prägen
absichtlich kein Route-Ticket, weil behandelte Kommandos den Prompt-Hook nicht
erreichen. Bei mehrdeutigen benannten/multi-account Main-, Group- oder
Channel-Turns wird nur diese optionale Quelle ausgelassen; andere Recall-Quellen
bleiben unberührt. Ein zuletzt gespeicherter Session-Route-Wert ist kein
turn-gebundener Account-Beweis.
Legacy rows that used the old workspace_shared scope remain in their
authoritative private table until an operator explicitly migrates them. Start
with the non-mutating audit:
/plur1bus migrate-legacy-shared
Use --report <name.json> for a fixed private report name, and resume a bounded
dry run with the opaque --cursor <token> returned by the previous run. After
reviewing the report, run --apply without a dry-run cursor; apply re-reads each
source row, writes and verifies an idempotent workspace copy, and only then
marks the legacy source. The command never deletes or re-scopes the source row:
workspace_shared legacy rows are not reinterpreted. The operation is bounded
per run to 250 rows, 4 MiB source bytes, 100 provider calls, and 60 seconds.
The opaque cursor pins source versions and dry-run mode; an unavailable or
changed pinned version, mode mismatch, checksum/binding failure, timeout, or
uncertain commit aborts the run and requires the documented continuation or a
restart without the cursor. Apply never accepts a dry-run cursor.
It is operator-destructive, so it requires the same user authorization as
/forget; cron identity does not bypass that gate. Reports are no-clobber
0600 JSON files below .plur1bus/migrations/ and exclude memory content,
vectors, evidence, and provenance.
The migration runs only through the destructively authorized initialized runtime command; there is no standalone DB/config/credential bootstrap. Multi-Namespace, Neo/Obsidian aliases, Semantic Lens, CRR, the OpenClaw default LLM, and per-agent credentials do not change under sharing or migration.
security.allowModelDestructiveMemoryOps (default true) — the model-facing tools memory_forget and knowledge_update mutate persistent memory/knowledge state. Set this flag to false if you want a hard opt-out for model-driven destructive memory writes.
Core memory (capture, recall, search) works from manifest-safe defaults without profile confirmation. Argument-less setup only lists the choices, and start is read-only status/onboarding guidance:
# In Telegram
/plur1bus setup
/plur1bus start
Apply a profile only by naming it explicitly:
/plur1bus setup safe
/plur1bus setup recommended
An explicit selection records setupProfile and featuresConfirmedAt. Recommended enables additional features while retaining merge and Obsidian safety gates; vault discovery alone never counts as confirmation.
LanceDB is the authoritative store: every memory card lives there first, indexed per agent for isolation. The Obsidian bridge mirrors cards into a Markdown vault so the user can read, link, and edit them with normal tools; LanceDB stays the source of truth and the bridge re-syncs on changes.
A daily consolidation job detects duplicates and generates merge proposals (never auto-applies). A critical-push classifier (run via the OpenClaw-managed cron as the exact command /plur1bus internal classify-recent) labels recently captured cards by sensitive entity type (person, relationship, birthday, money/account, health, access/password) using the configured chat model, and — when a per-agent daily threshold (maxPerDay) is not yet exceeded — emits a short confirmation message per critical card. The command handler converts returned pushMessages into a validated native command reply; OpenClaw's dispatcher owns finalization and delivery. Multiple push texts are combined in their original order; partial classifier failures are reported alongside successfully produced pushes. The per-day counter is enforced across runs, and each card is classified exactly once, so no card is pushed twice.
The recall pipeline runs embedding → LanceDB vector search → optional query refinement → temporal filter → canonical KNOWLEDGE.md search → score/status processing → graph spread and hydration → budget allocation → optional rerank → deduplication → ACL filtering → finalization. The caller may then append bounded Semantic Lens and Conversation Reactivation Recall results; neither replaces the primary recall.
npm install
npm test # full serialized Node test runner
No build step. ESM-only. Tests are unit-level and DB-free; the LanceDB adapter is mocked behind a thin interface.
Recalled memories are rendered as historical evidence, not as current user requests. A memory that contains an old imperative such as a download, send, write, delete, install, purchase, network action, or command must not trigger that action unless the current visible user turn asks for the same action.
The recall block uses escaped metadata attributes and wraps recalled text in quoted-evidence elements, so prompt boundaries stay explicit even when old memory text contains tool-like markup.
Version 6.x is a major upgrade. If you ran 5.x:
init(). New columns: status, versionNumber, previousVersion, supersededBy, updateSource, updateEvidence, reconsolidationConfidence, versionCreatedAt, updatedAt. Migration is idempotent and non-destructive./plur1bus setup safe or /plur1bus setup recommended only when you intentionally want to persist a profile; /plur1bus setup and /plur1bus start are non-mutating.merging.autoApply defaults to false. Merge candidates are written to merge-proposals.jsonl instead of being applied automatically. Set autoApply: true to restore 5.x behavior.mode: "apply" with safety gates (backups, audit log, vault path confirmation). Default is mode: "augment" (read-only). Confirm vault path explicitly before first write.reranker.timeoutMs, reranker.fallbackOnError, merging.autoApply, merging.mode, obsidianBridge.backupBeforeApply, obsidianBridge.auditLog, obsidianBridge.requireVaultPathConfirmation, obsidianBridge.morningReview.status, obsidianBridge.eveningReview.status, emotion.tier, emotion.t2.enabled, emotion.t3.enabled, emotion.t3.model, emotion.t3.apiKey, emotion.t3.escalationConfidence, emotion.t3.timeoutMs, emotion.moodInfluence, emotion.intensityHalfLifeFactor, emotion.temperaments.<agentId>.See v5_TO_v6_MIGRATION.md for the full migration guide.
MIT — see LICENSE.
312 commits
JavaScript
98.6%
Shell
1.4%
PLUR1BUS turns OpenClaw into an agent with long-term memory: a per-agent isolated LanceDB store as the source of truth, a mirrored Obsidian vault as a human-readable view, and a small set of background jobs that classify, consolidate, and (when warranted) notify.
PLUR1BUS 7.12.8 — verified on OpenClaw 2026.8.x, 2026.9.1 and 2026.9.2
Current source version: 7.12.8. PLUR1BUS 7.12.8 supports OpenClaw 2026.8.1
as its primary host target and is additionally verified against OpenClaw
2026.9.1; the declared compatibility floor is openclaw@2026.8.1 and plugin
API >=2026.8.1. The package is built and tested against the immutable build
baseline openclaw@2026.8.2; see the
compatibility contract for the full runtime
matrix and evidence. The upstream source base is the immutable official tag
v7.4.10, commit c0a8a4c28ff1cb9c632e185f21f4502d67d1b605.
PLUR1BUS registers its own "PLUR1BUS" tab in OpenClaw's Control UI
(/plugins/memory-lancedb-namespaced/control) — memory health, the workspace
matrix, provider status, and migration progress, all in the browser. It rides
on the OpenClaw Gateway's own port and authentication (no separate port, no
separate login); reach it through however you already reach your Gateway
(loopback, or a Tailscale/VPN front end), then open that tab.
By default, each agent gets its own LanceDB store under {baseDbPath}/{agentId}/ and a matching Obsidian vault folder for browsing. An explicit named-namespace configuration can read the same validated agent from multiple storage namespaces while keeping one active writer. The plugin captures conversation-derived memory cards automatically, runs a daily consolidator and a critical-push classifier as cron-driven background jobs, and exposes a small set of Telegram commands so the user can inspect, edit, or toggle behaviour without leaving the chat.
Persona evolution used to run once a week, hand the model only the current
profile and ask for "one small change"; the reply outcomes were merely a gate.
Now it runs daily (04:15, staggered per agent, personaVoice.cron/timezone
to override) with two brakes kept in .adaptive-learning/persona-evolution-state.json:
at least minDaysBetween days (default 2) since the last change, and at
least minOutcomes (default 10) new real reply outcomes since then, more than
half of them positive. Heartbeat turns no longer count. The model sees the
protected seed, the numbered learned lines, and up to six positive and six
negative excerpts (question, answer, the user's reaction) and answers with
ADD: - …, REPLACE <n>: - … or NONE. The managed block may hold 24 lines
(personaVoice.maxBullets, was 12 including the seed) and the injected
directive cap follows from that (lines × 130 + 80, default 3200 characters).
Existing weekly jobs are migrated to the daily slot on the next setup run.
Twenty-five releases in three days, all live-tested on a three-agent installation. The changelog has the details per version; the highlights:
before_prompt_build handlers one after another, each with its own 15 s
timeout. Reply-outcome tracking ran first and waited for up to twelve
sequential LanceDB updates, so recall started only after that timeout. The
DB work now runs in a serial per-agent queue after the turn's recall
(7.12.30). A new recall prelude log line reports where the pre-recall
time goes; the query embedding runs under a budget (neo.recall.global.embedTimeoutMs).entry.delivery; the resolver reads both the old and the new shape. Voice
notes and photos without a caption now produce a dispatch ticket, and the
fallback warning names the exact step and reason.vectors.<gen>.f32 with byte offsets in vector-index.json
instead of JSON number lists; a 200 MB candidate journal shrank to about
13 MB plus a 38 MB sidecar. Lossless; inline vectors keep being read.
scripts/migrate-neo-vectors-sidecar.mjs migrates existing stores.neo.recall.global). A metadata index (candidate-index.jsonl) keeps
the horizon independent of the journal cap and makes the search cost
60–140 ms after the first read.consolidate-daily now runs optimize() per agent
(dailyConsolidation.lancedbOptimize, versions older than 24 h are pruned).emotion-refine job refines rows the fast tiers were
unsure about, and always rows with importance ≥ 0.9.openclaw plur1bus-command --agent <id> --session <key> [--locale xx] "/…"
runs any chat command as operator against the same Neo store the hooks use.knowledge_update refuses to promote an invalidated memory into canonical
KNOWLEDGE.md, but afterwards removed only the keys it had actually
integrated. Anything unpromotable — invalidated (a soft delete sets both
status="deleted" and epistemicStatus="invalidated") or gone from the
table — stayed queued forever in .adaptive-learning/knowledge-pending.json
and kept inflating pendingCount, so the maintenance nudge told the agent
that N insights were waiting when some of them no longer existed. The queue
now drops those entries on the next run, before the "nothing to integrate"
exit. Pruning is restricted to the ids the query actually asked for, so a
capped query or a failed fetch never drops pending work.MEMORY.md and USER.md
before injecting them (classifyWorkspaceMemoryPaths). PLUR1BUS did not
implement it, the host answered unsupported, logged "excluding automatic
memory context: selected memory runtime does not support provenance
classification" and left both files out of every session — the agent only
saw its curated memory when it read the file by hand. The runtime now
classifies workspace memory paths with the same rules as memory-core
(curated roots and memory/**.md → agent, dreams → system, anything
outside the workspace or non-memory → untrusted).embedding.requestTimeoutMs, default 15 s) and no SDK-side retries. The
OpenAI SDK default of ten minutes let a stalled request hold a recall until
the 20-second worker timeout (started=yes elapsedMs=0 in the log).expiresAt, validFrom, validUntil,
updatedAt, remindAt, …) as JavaScript BigInt. The recall pipeline's
lifecycle gate accepted only numbers, so a BigInt 0n expiry counted as
"expired" and every candidate was rejected before scoring, ACL and
reranking. Auto-recall injected zero memories on every turn and
memory_recall reported "no results" while the rows were in the table with
scores around 0.5. projectRecallEntry now coerces all Int64-backed fields
to safe numbers and isRecallEntryLive accepts BigInt input. Direct
MemoryDB.search was unaffected, which is why manual checks kept passing.Gespräch became Gespra ch, läuft became la uft) and umlaut
stopwords such as über or für never matched. Combining marks are kept
and the text is recomposed to NFC.started=no), reports the wait time, the active slot count and
maxConcurrentRecall, and the job is dropped from the queue instead of
running uselessly later. This is the signal to watch when
runtime.maxConcurrentRecall is too low for the host.<temporal-context> block now states that transcript messages carry no
timestamps and compaction summaries no dates, and that no time or date may
be asserted for earlier conversation content unless it comes from the block,
a memory record's created-at/age attribute or a tool result. Agents had
been filling that gap with invented times (#133).scripts/run-semantic-discover-once.mjs used homedir() without importing
it from node:os and crashed unless PLUR1BUS_VAULT_PATH and
PLUR1BUS_DB_BASE were set. The import is in place.schicht15.maxPromotionsPerRun compared the lifetime number of promoted
cards against the limit, so a workspace that had ever promoted that many
was blocked for good (a live install sat at "7/3" since June with
KNOWLEDGE.md frozen). The limit now counts a 24-hour window; promotions
recorded before timestamps existed do not count, so a stuck workspace
unblocks on the next run. Dedup of already promoted cards is unchanged.criticalPush.hideTypes
(for example ["gesundheit", "geld_konto"]).listen EINVAL before the owner service starts. Linux
keeps its exact address; other Unix platforms elect the owner through a
deterministic exclusive loopback TCP claim that carries no data, while the
data plane stays the private filesystem socket with its 256-bit token. An
oversized macOS socket path (103 bytes) is diagnosed before any mutation./var is
/private/var on macOS), skip the four shared-memory integration paths on
hosts without stable fd-backed directory routing with an explicit reason, and
assert that such hosts reject shared writes without creating a shared root.
Two focused macOS CI jobs run next to the unchanged Linux suite.jinaai/jina-embeddings-v5-text-nano-retrieval as
option 1 and default; OpenAI is option 2, E5 the keyless fallback, Jina v3
stays selectable for existing installs. Non-interactive and dry runs need
PLUR1BUS_ACCEPT_NONCOMMERCIAL_LICENSE=1 to accept CC BY-NC 4.0 and fall
back to E5 otherwise, rather than failing or acknowledging a license silently.embedding.local.maxTokens, default 512 (32 to 8,192).
The same batch then costs +0.6 GB (nano) or +1.1 GB (v3) and 5 or 22 s.
Memory cards are summaries; raise the cap deliberately if long cards must be
embedded in full. The cap is part of the shared model pool identity.jinaai/jina-embeddings-v5-text-nano-retrieval
is pinned (five artifacts, about 265 MB, SHA-256 checked). A jina-v5
runtime branch validates the EuroBERT config, takes the graph's own
sentence_embedding (normalized last-token pooling) or pools over the
attention mask itself, truncates to 32 to 768 Matryoshka dimensions, and
distinguishes queries from documents by the published Query: and
Document: prefixes, refusing any other prefix. Targets jina-v5-nano-*
appear in the schema, the dimension planner, the dashboard switch (same
re-embedding migration), wizard option 4 and installer choice jina5, each
behind the CC BY-NC 4.0 acknowledgement. An option, not a proposal: the
default stays where it is until the PLUR1BUS lab test against v3 is in,
and existing configurations are untouched.memory_forget keeps type = "memory" while its
status becomes deleted. The classify-recent cron took such rows for
fresh candidates, typed them and pushed them as critical, while the review
path (which only reads active rows) never knew the references. Candidate
selection and the classifier now skip every row whose status is not
active; the run result reports them as skippedInactive.before_agent_reply only, which the host does not fire
for a Telegram chat message; the message went to the agent. The handler now
also listens on before_dispatch, where the host hands over the quoted text./plur1bus critical accept all, reject all, or several references in one
command handle every pending review of the authorized scope; unknown or
ambiguous references are reported, the rest is applied.main.telegram.default,
numbered when an account admits several users). The user id itself never
appears on the page.main, and main.dir for the pre-alias
directory identity of the same workspace), and an all-zero list says that
the partitions exist but nothing has been shared into them yet. Paths stay
off the page.openclaw plur1bus-obsidian detect|use|create|confirm --session <key>
called the host's RPC helper as (method, params); its signature is
(method, opts, params, extra), so every method saw an empty request and
nothing was printed. The CLI now passes the parameters in the right slot,
with the method's scope, and writes the result as JSON.vault_write; the discovery also needs semantic_index_write,
granted by the semantic-discovery confirm plan with a confirmed action.
The scheduled run now counts as the operator's standing confirmation; the
vault receipt, mode: apply and allowWrite stay required.plur1bus-obsidian use/confirm --session <key> and the gateway methods
ended with identity_binding_required: the operator-side session resolver
produced only agent and workspace, no user or conversation identity. A
direct-chat session key now resolves to the same identity-bound context
that conversation's chat commands get, so the receipt binds to that
conversation's user. Other session kinds keep the plain agent context.discover-semantic-links cron called the discoverer without a
mutation policy, which the policy layer reads as blocked, so scheduled
discovery never wrote a link. The handler now builds the same
receipt-bound policy per workspace that the bridge service uses. Without a
vault receipt it stays blocked by design.plur1bus-obsidian use/confirm, gateway
methods plur1bus.obsidian.prepare/confirm) failed with "Invalid agent
ID": the handlers read the parameters off the host's method context object
and did not accept the CLI's session name. Both are handled now, and the
agent id falls back to the key's agent:<id>: prefix.obsidianBridge.workspaces list the target probe only
looked at <OpenClaw home>/workspace; the vaults in workspace-<agent>
stayed invisible. Every workspace* directory is probed now.requireVaultPathConfirmation: false the bridge acts without a
receipt, so a configured target now reads "ready" instead of "not yet
confirmed", and the card says how many targets are configured.obsidianBridge.workspaces (entries
with id, agentId, path) or vaultPath. The reviews work without it
through the host's agent workspace; semantic-link discovery and the target
card do not.allow-scripts
only, so the browser silently blocked every native form submission there;
the 7.6.0 switches and the 7.8.0 Compact button only worked in a standalone
browser tab. A writable page now carries one nonce-bound script that posts
the form with fetch and reloads itself; the result shows up as the usual
banner. Read-only pages still carry no script. The script intercepts the
click on the submit button: a sandboxed frame refuses the submission before
it ever fires the submit event (7.8.1 listened there and never ran). The
action travels as a GET with the single-use token in the query, because the
host authenticates the frame's cookie for GET only and answers a POST from
the opaque origin with 401 (7.8.2 hit that). 7.8.4 routes the Compact
button through the db-adapter, which owns optimizeTable; 7.8.3 asked the
pool's raw store objects and failed.controlUi.writeActions: "all",
each private partition under "Cards by agent" gets a button that runs
LanceDB's fragment compaction (table.optimize()) for that partition in
the background, one at a time. The adapter had the primitive since August;
nothing ever called it. The row shows progress and the result.openclaw plur1bus-feature-cron --help takes longer than the old
five-second budget, so every run took the fail-closed branch and
safety-disabled the direct jobs. Budget is 30 s now.main is the default
agent when the host flags none._neo. Reserved
store directories are skipped by the health scan.DREAMS.md, in the host's entry
shape inside its managed diary block. Only the agent's private partition
writes there; shared partitions never do. dreaming.narrative.diary turns
it off. To give the diary one author, switch the host's own managed dreaming
off. The host reads that switch from the memory slot owner's entry, so it is
plugins.entries.memory-lancedb-namespaced.config.dreaming.enabled: false,
not memory-core's flag.The operator tab can now change two things, if you let it. controlUi.writeActions
is off by default, so nothing changes for an existing install.
reranker makes the reranking choice switchable from the page: local BGE,
local JinaAI (both keyless), Cohere when a key is configured, or off. That is a
runtime choice with no data migration behind it.all additionally exposes the embedding target and the re-embedding
migration: dry run, copy, and a separate switch. The confirmation token that
binds those steps stays inside the Gateway and never appears in the browser.Every change carries a single-use form token from the page render it was clicked
on, because the tab cookie the host mints is SameSite=None. While
writeActions is off the page renders no form, forbids form targets in its
Content-Security-Policy, and refuses POST outright.
Also fixed: the reranking card used to name jina as a reranker.provider
value. The schema rejects it. Jina reranking is a local model, selected through
reranker.local.model.
OPENAI_API_KEY (and OPENAI_API_KEY_FALLBACK for
the fallback embedder) when neither apiKey nor apiKeyEnv is set, which
is how a default install carries its key. The table now follows the same
rule, names the variable it found, and never reads its value. Keyless
providers show not required; an unconfigured optional fallback shows
optional.missing badge. They need no
key of their own, so the status says so instead of reading like a fault.reranker.apiKeyEnv was labelled reranker.apiKey, sending the reader to a
line that does not exist in their config.host_route is explained. The source had no legend entry, and the
missing help text claimed the feature stays off — untrue for exactly the
capabilities that report it.enabled: false still turns them off.apiKeyEnv too. A working Cohere reranker
configured through an environment variable was reported as missing. The five
capabilities that fall back to OpenClaw's own model route are now marked as
host-routed instead of looking unconfigured.cohere, jina and the local BGE fallback.gateway-runtime dispatcher capabilities. Missing
capabilities fail closed; PLUR1BUS never rewrites OpenClaw source, dist, or
node_modules files.2026.8.1/2026.8.2, while runtime behavior is guarded
by feature detection rather than version-string branches. OpenClaw
2026.9.1 stable is additionally verified (see the compatibility contract).observed, every other new write as explicit untrusted; nothing invents
trusted any more. The skill miner clusters observed | corroborated | trusted
plus valid pre-cutoff legacy rows and no longer applies a 30-day lookback, so
an existing install keeps mining its history instead of reporting scanned: 0.
The cutoff marker is written once, at the first upgrade, before the first write.SKILL.md is written first (tmp + fsync +
rename), then the evidence transitions; a partial failure stays
activation_partial and can be re-applied idempotently.updateCard, compaction, auto-capture, light-dream rewrite — checks the
tombstone registry before table.add. Same-text replay by the user is still
allowed.recall.globalInjectMaxChars, default 17000) trims
memories before time and reminder context, so a large recall can no longer
crowd the rest of the prompt out./plur1bus curation resolve <keep|drop> ends a
neo conflict without any hard filter, and /plur1bus curation drop-injected
demotes only injected behaviour conflicts after a preview and a nonce —
genuine conflicts are never touched.Condensed summary of 7.3.0–7.3.5; see the changelog for detail.
fakt was
missing from the type enum, so every classification run failed validation.maxMemoryCount; before this, garbage collection had no
scheduler at all.importance = 1.0 works again (7.3.4) — the value is the agent's reserved
manual core marker; it was silently ignored because the core score also demanded
an emotional intensity the agent cannot set. Core scores are now normalised to
the features that actually exist.thinking: off.sharp@0.35.3 close the dependency audit; PLUR1BUS now requires Node.js 22.22 or newer.npm audit reports 0 vulnerabilities.rem-dream when merging.enabled: true is explicitly authored, instead of shipping an enabled handler without a scheduler binding./state command fix — The top-level status command no longer crashes on an out-of-scope ctx reference.typescript is now declared as an optionalDependency so the code-index feature works out of the box without forcing TS on all users.buildInstallLogEvent now correctly passes featureMode instead of hardcoding "preserve"; dry-run vs. remote-target warnings are properly distinguished; dead code removed.jq subprocess calls consolidated into batch eval+@sh extracts.await on drainEmbeddingQueue() caused the unresolved Promise to be serialised as {} in postMessage; callers now receive correct drain results.statSync race condition fixed (file deleted between readdirSync and statSync); addQueryVector null-return guard added.symbols.find() in AST visitor replaced with a Map for O(1) lookup.openclaw.plugin.json version aligned with package.json.scope: "user" writes now require an authenticated user identity (user scope is owner-bound) and are filtered in recall/visibility checks..openclaw/extensions/emotional-state-injector/ files are included in the npm tarball; runtime activation still requires the OpenClaw plugin entry/allow config and a gateway restart..plur1bus/code-index.json and can render bounded <code-context> query output.PLUR1BUS can build a local JS/TS code index without CocoIndex:
npm run code-index -- /path/to/workspace
npm run code-index -- /path/to/workspace --query "/plur1bus code-index"
The index is written to .plur1bus/code-index.json and contains normalized files, symbols, import/call/register edges, and symbol chunks. --query prints a bounded <code-context> block from the generated index. It uses the TypeScript Compiler API through the optional typescript dependency and keeps the PLUR1BUS schema independent of the parser implementation.
/plur1bus obsidian conflicts build renders them as Obsidian pages./plur1bus start onboarding — Read-only status and onboarding guidance for feature profiles and vault setup.scripts/auto-capture-lancedb.mjs gains workspaceKey field; schema migration is backward-compatible and idempotent.shouldSkipAutoRecallForInternalTurn prevents feedback loops when the gateway injects synthetic cron messages./mf +/-/~). Coverage-gap detection finds topics with few or weak memories.sessionThreshold (default: 50) or intervalDays (default: 7) is reached. Optional LLM-generated natural-language report._meta-cognition-state.json per workspace.minClusterSize, similarityThreshold, maxNudgesPerDay.gpt-4o-mini is only an explicit override example.emotionTier to a specific tier or use auto for dynamic escalation.--explain flag for /memory) — Human-readable rationale per result: score breakdown, boost factors, temporal relevance./mf feedback for recall-quality improvement./correct treated as recall event — After safeUpdate() inserts the corrected card, applyRetrievalReinforcement refreshes lastRetrievedAt, increments retrievalCount, and boosts memoryStrength.getById(newId) races or fails, reinforcement is silently skipped; the correction itself is never rolled back.searchByTopic, getCard, and recall pipeline filter by ACL. Unauthorized access is logged./mf) — Thumbs-up/down/neutral feedback on any memory result. Persisted per workspace./share) — Copy any card into a workspace-shared pool with ACL protection.maxPromptMemories (default 12), dedup threshold 0.78, acronym recognition, canonicalMaxItems (default 5).halfLifeDaysMap: transient (60d), episodic (180d), longContext/project (600d)./memory, /forget, /correct accept any length. >6k chars are semantically compressed; >100k chars prompts for file/vault source.recommended profile (all features active, Obsidian/reviews marked pending_setup). Core memory works immediately; advanced features require explicit confirmation.merge-proposals.jsonl, never auto-applies.recommendation (review_only or apply_via_safe_reconsolidation), never modifies memory directly.memoryId + optional contentHash.mode: "apply" creates per-file backups, manifest (beforeHash/afterHash), and audit-log entry. Vault path confirmation required before first write.These features run after normal recall and only append results; they never replace the primary recall result and never write memory data.
Reads a precomputed .plur1bus/semantic-lens-index.json from the workspace and adds a small number of community/bridge/faded memories that normal recall may have missed.
enabled: false in schema.maxLensMemories: 3, maxBridgeMemories: 2, maxFadedMemories: 1, maxCommunities: 2.MVP reactivation hook that appends a <memory-reactivation> block when a conversation appears to resume after an idle gap, a compaction, or a continuation signal.
enabled: false in schema; visibleHints: false.maxReactivationMemories: 3, maxFadedReactivationMemories: 1, maxOpenThreads: 3, maxCommunities: 2.Record notes can contain an idempotent managed block (id="graph-links") with wikilink edges. The block is regenerated, not appended, and conflicts with manual edits are reported.
explicit (memoryIds/sourceRefs), type (type-based rules), semantic (precomputed link index).semanticDiscovery builds .plur1bus/link-index.json from memory mirrors + vectors behind a confirmation gate; it is not auto-applied.Memory mirrors use technical filter tags, not semantic memory tags:
plur1bus/memoryplur1bus/agent/<id>plur1bus/workspace/<id>plur1bus/category/<cat>plur1bus/scope/<scope>These tags are used for vault filtering and graph grouping; they do not carry semantic memory content.
| Command | What it does |
|---|---|
/state | Status snapshot: memory card count, sync state, last plausibility run, any open issues with reason + fix hint. |
/memory <query> | Search the agent's memory via the recall pipeline. Accepts queries of any length. Add --explain for result rationale. |
/forget <text> | Forget a memory card. Archive-first guarantee — the card is JSON-archived before deletion. |
/correct <old> zu <new> | Update a memory card. Archive-first guarantee. Accepts zu, →, or -> as separator. |
/mf <id> + / - / ~ | Feedback on a memory result: 👍 positive, 👎 negative, ~ neutral. Persisted per workspace. |
/share <id> | Copy a memory card into the workspace-shared pool. ACL-protected. |
/enable <feature> | Turn on a whitelisted feature (vaultSync, kritischPush, dailyConsolidation). |
/disable <feature> | Turn off the same. Writes atomically into openclaw.json; gateway restart required. |
/plur1bus setup | List the available profile choices without changing configuration. |
/plur1bus setup safe | Explicitly apply the Safe profile; core capture/recall stays usable and additional mutators remain off. |
/plur1bus setup recommended | Explicitly apply Recommended while preserving existing opt-outs and write-safety gates. |
/plur1bus start | Show read-only status and onboarding guidance; it does not change configuration. |
/plur1bus subcommands| Command | What it does |
|---|---|
/plur1bus skills review | Show open skill proposals. |
/plur1bus skills approve <id> | Approve a skill proposal. |
/plur1bus skills reject <id> | Reject a skill proposal. |
/plur1bus skills list | Show active skills. |
/plur1bus skills show <id> | Show proposal details. |
/plur1bus reminders list | List active reminders. |
/plur1bus reminders cancel <id> | Cancel a reminder. |
/plur1bus obsidian dashboards build | Build Obsidian dashboard pages. |
/plur1bus obsidian conflicts build | Build conflict report pages. |
/plur1bus doctor | Run diagnostics and show runtime status. |
/plur1bus internal proactive-check | Run proactive nudge detection manually. |
/plur1bus internal meta-reflect | Run meta-cognition reflection manually. |
/plur1bus internal afterthought | Run the delayed follow-up job manually (see below). |
When the last conversation ended 30–120 minutes ago with an open outcome (the user asked for details, or the topic was dropped mid-thread), the plugin can compose a short, casual follow-up message ("Mir ist zu … noch eingefallen…"). This is gated by the shared proactive governor budget, capped at one per day, and skipped for any topic already surfaced as an open thread today. Recommended cron: every 3 hours, run the exact command /plur1bus internal afterthought with announce delivery. The plugin command returns either a validated reply payload or OpenClaw's NO_REPLY suppression token. PLUR1BUS submits the exact allowlisted command through OpenClaw's public gateway-runtime dispatcher, so OpenClaw owns status finalization and at-most-once announce delivery without an outer carrier-model run. If the required native capability is unavailable, provisioning fails closed and leaves the feature job inactive. Custom prompts, surrounding whitespace, prefixes, and suffixes are never claimed.
Setting this cron up is automatic when its raw feature gates are explicitly enabled — see below.
node scripts/setup-feature-crons.mjs verifies the public native command-dispatch capability first. When healthy, it loads exactly one validated configuration snapshot with openclaw gateway call config.get --json, discovers bound agents, and idempotently plans up to ten jobs per agent plus one install-wide GC job. It fails closed without normal cron planning when the capability is absent, the gateway call fails, JSON is invalid, valid !== true, or sourceConfig/runtimeConfig is not a plain object. Custom prompts and unrelated jobs remain untouched. It never falls back to local config files or alternate raw/resolved fields.
The capability probe allows the host CLI 30 seconds per help call (7.8.0); a booting host with many plugins needs more than the former five. Ownership is by exact command: an operator-named job that runs /plur1bus internal <feature> for an agent is that agent's job, and only PLUR1BUS-named jobs are eligible for schedule migration. A non-delivery feature whose only owned jobs are disabled gets its best candidate re-enabled; the singleton collector is satisfied by a job on any agent; when openclaw agents list flags no default agent, main is treated as the default.
The two configuration views have separate roles: sourceConfig alone controls explicit raw feature gates and the raw skillMiner schedule; runtimeConfig alone controls effective bindings, accounts, and delivery. Runtime defaults cannot enable jobs. The eligible jobs are:
persona-evolve: personaVoice.enabled && skillMiner.enabled; daily 04:15 local time (7.12.38; before: Sunday), staggered five minutes per agent, overridable via personaVoice.cron/personaVoice.timezone; no delivery. The shipped weekly slots are migrated to daily on the next setup run; operator schedules stay.afterthought: afterthought.enabled && (skillMiner.enabled || merging.enabled); every 3 hours; exact-command announce delivery with a direct text/NO_REPLY result.consolidate-daily: dailyConsolidation.enabled; daily 04:00 in Europe/Berlin; no delivery.classify-recent: criticalPush.enabled; every 3 hours; safe announce delivery of approved pushes or NO_REPLY.rem-dream: merging.enabled; daily 01:15 in Europe/Berlin; no delivery.skill-miner: skillMiner.enabled; raw Croner-compatible cron/timezone after conservative syntax validation, defaulting to Sunday 03:00 in Europe/Berlin (timezone: null means local time). Invalid literals, descending ranges (including named month/day ranges), names, modifiers, or literal-step forms are ineligible.discover-semantic-links: obsidianBridge.enabled && obsidianBridge.graphLinks.semanticDiscovery.enabled; daily 02:00 in Europe/Berlin; no delivery.gc-run: gc.enabled; daily 04:45 in Europe/Berlin, after consolidate-daily has produced the candidates; no delivery. This one is a singleton — runGcJob iterates over every agent database itself, so exactly one job is planned regardless of how many agents the install has.Every job runs with --agent <agentId> --session isolated. Provisioning does not set model, fallback, token, auth, API, or other credential overrides, so OpenClaw's default LLM and per-agent credentials remain authoritative. The script remains idempotent and exit-0 for install safety, so it can run from any of these channels:
npm install/npm postinstall — fires when the plugin is installed via npm install (e.g. npm install -g @cyb3rb1ade/plur1bus-memory).gateway_start handler schedules a bounded reconciliation after the gateway
becomes reachable. Missing capabilities or invalid config leave exact owned
feature jobs inactive; retries use bounded backoff. Disable provisioning with
"featureCronSetup": { "auto": false }./plur1bus setup crons (optionally --agent <id>/--account <acct> to force single-agent mode).The /plur1bus doctor and /plur1bus status feature-cron hint is condition-derived, not "have we shown this before": it reads the marker file and only surfaces a hint when setup has never run, ran under an older plugin version, or ran but couldn't create everything it planned (some crons are still pending — e.g. no delivery target could be derived). It's silent once a current-version run reports nothing left to create.
bindings > 0 (i.e. an actual chat channel routes to them) get feature crons. Subagents (bindings === 0 — researchers, deep-divers, and other internal-use-only agents) are deliberately excluded; they have no chat to receive an automatic persona evolution or an afterthought delivery, and running these jobs against them would be pure compute waste.isDefault first, then most bindings, then alphabetically-first id) to avoid two crons double-firing against the same state files.plur1bus <feature> <agentId>. An existing job is owned only by an exact, case-sensitive agent id plus either its exact canonical name or exact first command line; missing or different agents are untouched. Every exact owned duplicate is inspected and reconciled, even when another duplicate is already safe.allowFrom. Delivery-required jobs use only a conservatively validated Telegram binding match.peer.id (including t.me/<handle>) or effective account/root defaultTo. Every relevant non-ACP binding must agree on channel, and an account inherits only when match.accountId is truly absent. Omitted accounts resolve in order from an explicit valid defaultAccount, accounts.default, one sole named account, or a root account proven by configured botToken/tokenFile; routing fields alone never invent a root default account. Unsupported providers, wildcard, placeholder, redaction, zero-id, disabled-account, explicit empty/missing account, mixed-account, and conflicting target/channel/account states are rejected. Existing delivery seeds require exact mode: "announce"; case or whitespace variants are unsafe. A job without a validated target is created disabled with --no-deliver; every unsafe owned delivery job is disabled and stripped of delivery. Non-delivery jobs retain only missing delivery or exact mode: "none"; every other delivery object is removed.openclaw agents list --json fails, is unparseable, or yields no bound agents, no cron is mutated. Passing a validated --agent <id> forces one explicit agent; missing, option-like, or invalid --agent/--account values fail closed, and --account without --agent is rejected.PLUR1BUS 7.7.1 requires Node.js 22.22 or newer and OpenClaw 2026.8.1 or newer.
On an older host the installer refuses the package instead of deploying it:
requires plugin API >=2026.8.1, but this OpenClaw runtime exposes <version>.
Install the published release through OpenClaw's package installer:
openclaw plugins install clawhub:@cyb3rb1ade/plur1bus-memory@7.5.3 \
--acknowledge-clawhub-risk --pin
The same release is on the npm-compatible registry, if your @cyb3rb1ade
scope already points there:
openclaw plugins install @cyb3rb1ade/plur1bus-memory@7.5.3 --pin
Or install the immutable GitHub Release tarball:
openclaw plugins install \
https://github.com/Cyb3rb1ade/openclaw-plur1bus-memory/releases/download/v7.5.3/cyb3rb1ade-plur1bus-memory-7.5.3.tgz
To build from this source checkout instead, produce a tarball and install that artifact rather than linking the directory; a source link is not an equivalent package-compatibility test:
npm ci
npm test
npm pack
openclaw plugins install \
npm-pack:/absolute/path/cyb3rb1ade-plur1bus-memory-7.5.3.tgz --force
Record the tarball's SHA-256 before transferring it. PLUR1BUS 7.5.3 never patches OpenClaw runtime files. Existing release artifacts remain unchanged and must not be relabelled as 7.5.0.
Restart the gateway after installing, so the new plugin version is loaded.
Then add a plugins.entries["memory-lancedb-namespaced"] block to your openclaw.json (see below).
Minimal config block in openclaw.json. This is an explicit override example:
the named gpt-4o-mini value and its credential are illustrative user choices,
not PLUR1BUS defaults.
{
"skills": {
"workshop": {
"autonomous": { "mode": "propose" }
}
},
"plugins": {
"slots": {
"memory": "memory-lancedb-namespaced"
},
"entries": {
"memory-lancedb-namespaced": {
"enabled": true,
"hooks": {
"allowConversationAccess": true
},
"config": {
"baseDbPath": "~/.openclaw/memory/lancedb-namespaced",
"obsidianBridge": {
"enabled": true,
"mode": "augment",
"vaultPath": "~/.openclaw/vault",
"backupBeforeApply": true,
"auditLog": true,
"requireVaultPathConfirmation": true
},
"emotion": {
"tier": "auto",
"t2": { "enabled": true },
"t3": {
"enabled": true,
"model": "gpt-4o-mini",
"apiKey": "${OPENAI_API_KEY}",
"escalationConfidence": 0.85,
"timeoutMs": 4000
},
"moodInfluence": 0.3,
"intensityHalfLifeFactor": 1.0,
"temperaments": {
"bernhardine": { "preset": "warm", "baseline": { "joy": 0.35, "trust": 0.5 }, "sensitivity": 1.5, "decayMultiplier": 1.3 }
}
},
"dailyConsolidation": {
"enabled": true
},
"dreaming": {
"enabled": false,
"narrative": { "enabled": true, "storeAsMemory": true }
},
"merging": {
"enabled": true,
"mode": "safe-versioned",
"autoApply": false
},
"reranker": {
"enabled": true,
"timeoutMs": 5000,
"fallbackOnError": true
},
"security": {
"allowChatConfigCommands": true,
"allowModelDestructiveMemoryOps": true,
"allowedUserIds": [],
"allowedChatIds": []
},
"runtime": {
"embeddingCacheEnabled": true,
"embeddingCacheMaxEntries": 128,
"embeddingCacheTtlMs": 300000,
"embeddingCacheScope": "agent",
"llmResultCacheEnabled": true,
"llmResultCacheTtlMs": 86400000,
"llmResultCacheMaxEntries": 256,
"llmResultCachePersist": false,
"llmResultCacheMaxBytes": 67108864,
"llmResultCacheMetrics": true
}
}
}
}
}
}
dreaming.enabled: false is the OpenClaw memory-core sidecar gate, not the
PLUR1BUS narrative toggle. Keep it false when PLUR1BUS owns consolidation or
REM; otherwise OpenClaw also loads memory-core dreaming. When skillMiner is
enabled, use Skill Workshop autonomous propose or off so both learning
systems share the governed proposal queue without independently applying
overlapping skills.
hooks.allowConversationAccess: true is mandatory for this trusted memory
plugin. It authorizes the official typed before_agent_reply hook used for
automatic recall and for the fail-closed admission boundary of exact feature
commands; OpenClaw otherwise withholds the conversation body. The installer
enforces this single permission even in preserve mode while keeping all
unrelated hook and feature choices unchanged.
All paths default to $HOME/.openclaw/... if omitted. OPENCLAW_CONFIG_PATH and OPENCLAW_HOME env vars override the lookup of the gateway config file used by the toggle commands.
PLUR1BUS ships a keyless fallback for both stages and recommends something stronger for each. The two choices are not equal in weight: the embedding model decides how well recall separates hits from noise, the reranker only refines the candidates the embedding already found.
Embedding. The fallback intfloat/multilingual-e5-small (fixed 384
dimensions) packs its vectors into a narrow cone. Texts that have nothing to do
with each other still land at a cosine similarity around 0.84, which is a recall
score of roughly 0.86 on the 1 / (1 + distance) scale PLUR1BUS uses. Hits and
background noise sit so close together that neither the ranking nor a threshold
can separate them cleanly, and the band 0.96 to 1.0 reserved for agent-marked
cards as well as the duplicate threshold of 0.95 lose their meaning. This is
exactly what the compatibility lab showed when it measured with E5 and no
reranker. The recommended model spreads similarities much wider:
jinaai/jina-embeddings-v3 (multilingual, Matryoshka dimensions from 32 to
1024, CC BY-NC 4.0 license consent required). With it, ranking and thresholds
do their job. Since 7.12.0 the installer's first option is Jina v5 Text Nano
(see the lab test below); OpenAI is the hosted alternative, Jina v3 stays
selectable for existing installs, and E5 is only the small keyless fallback. The embedding model is switched from the PLUR1BUS tab
as well (controlUi.writeActions: "all"): the button picks the target model,
model preparation downloads and verifies it, and the re-embedding migration
then runs from the same page with a dry run, a copy, and a separate switch.
Unlike the reranker switch, every card is embedded again along the way, so
it takes time and disk space, and the old generation stays available for
rollback.
Which dimension. Jina v3 is trained with Matryoshka representation learning, so the vector can be cut to 32, 64, 128, 256, 512, 768 or 1024 dimensions. The Jina paper (table 7) reports retrieval nDCG@10 of 63.35 at 1024, 63.30 at 768, 63.16 at 512, 62.72 at 256, 61.64 at 128, 58.54 at 64 and 52.54 at 32. Keep the PLUR1BUS default of 1024: at memory scale the vectors are small anyway (50,000 cards are about 200 MB of float32 vectors), and the duplicate threshold of 0.95 and the reserved band above 0.96 are calibrated on full-width similarities. 512 is the economical choice for very large stores or weak hardware; it halves storage and ANN cost for a loss of 0.2 points. Do not go below 256: from 128 down the recall loss becomes visible and the similarity spread compresses, which moves every threshold. The dimension is baked into the table, so changing it later means another re-embedding run.
Jina v5 Text Nano (7.11.0), an option with the lab test pending. The
EuroBERT-based nano model (239M parameters, 12 layers, 768 dimensions with
Matryoshka down to 32, 15 European languages including German, CC BY-NC 4.0)
runs as the upstream Q8 export at roughly a quarter of v3's compute and about
265 MB on disk; on the reference machine one card embeds in about 0.45 s and
the process grows by about 120 MB. Jina reports MMTEB 65.5 against 64.44 for
v3. Queries and documents are told apart by the Query: and Document:
prefixes. Whether it becomes the proposal for new installs is decided by the
PLUR1BUS lab test on real memory cards, not by the version number. That test
ran on 5 September 2026 (OpenClaw 2026.9.1, Transformers.js 4.2.0, 12 cores
shared with other work): 1,031 active cards of one agent, 72 real recall
queries from the gateway log, the production ranking (OpenAI
text-embedding-3-large, 3,072 dimensions) as reference, both models through
the real provider with the 512-token cap, in separate processes, back to back.
| Jina v5 Text Nano, 768d | Jina v3, 1024d | |
|---|---|---|
| Production top-1 found at rank 1 / within top 5 | 48.6 % / 70.8 % | 47.2 % / 73.6 % |
| Overlap of the top 5 with production | 47.2 % | 46.1 % |
| Median top-1 similarity / median noise band (ranks 11 to 50) | 0.615 / 0.333 | 0.721 / 0.541 |
| Margin top hit over noise, median / 10th percentile | 0.242 / 0.132 | 0.153 / 0.100 |
| Distinct-card pairs at or above 0.95 / 0.96 (production: 59 / 49) | 90 / 81 | 84 / 66 |
| Model load | 5.1 s | 14.1 s |
| One card in a batch of 8 (migration path) | 478 ms | 1,556 ms |
| One card alone (capture path) | 313 ms | 416 ms |
| One query (recall path) | 256 ms | 124 ms |
| Process growth after load / peak | 444 MB / 1.2 GB | 1,120 MB / 2.3 GB |
Reading it: on ranking quality the two are level within the noise of 72 queries. Nano separates hits from the noise band much more clearly (v3 compresses similarities into a narrow band around 0.54, which is what makes thresholds hard to set), embeds cards three times faster in batches, loads in a third of the time and needs half the memory; v3 answers a single query twice as fast, which at a quarter of a second is not a user-visible difference. Both models push more distinct pairs above the 0.95 duplicate threshold than the production model, mostly genuine near-duplicates. On these numbers the maintainer made Nano the proposal for new installs in 7.12.0 and recommends existing installs the migration from the dashboard: prepare the Nano target in Model Preparation, run the dry run, copy, then switch; the old generation stays for rollback. Nothing migrates on its own.
Reranking. The reranker reviews the 40 candidates the vector search returns
and removes the ANN noise. Two local models are pinned, both quantized ONNX
through Transformers.js: bge-reranker-v2-m3, which the installer proposes
and the runtime uses when nothing else is configured, and
jinaai/jina-reranker-v2-base-multilingual, available under the wizard's
advanced options and as a dashboard switch. Switching between them is a
runtime choice without any data migration (see controlUi.writeActions). The
two models differ in size and in what they are good at:
| Jina reranker v2 | BGE reranker v2-m3 | |
|---|---|---|
| Parameters, layers | 278M, 12 layers | 568M, 24 layers |
| Context per query+document | 1,024 tokens | 8,192 tokens |
| Quantized ONNX artifact | 280 MB | 570 MB |
| License | CC BY-NC 4.0 | Apache 2.0 |
| BEIR nDCG@10 (English) | 53.17 / 57.06 | 53.65 / 56.51 |
| MKQA (26 languages) | 54.83 / 67.90 | 54.17 / 67.88 |
| MIRACL nDCG@10 (18 languages) | 63.65 | 69.32 |
| MLDR recall@10 (long documents) | 68.95 | 59.73 |
| CodeSearchNet MRR@10 / CoIR nDCG@10 | 71.36 / 56.14 | 62.86 / 35.97 |
| ToolBench recall@3 | 77.75 | 78.46 |
Where two numbers are given, the first comes from the Jina model card and the second from the jina-reranker-v3 paper; the two evaluations use different candidate sets, so compare within a column pair, not across. On English and on the 26-language MKQA set the two are level. BGE is clearly ahead on MIRACL, the broad multilingual retrieval benchmark, and slightly ahead on tool retrieval. Jina is clearly ahead on long documents and on code, and at half the depth it reranks noticeably faster on CPU; Jina quotes up to 15x the document throughput of BGE with flash attention on a GPU. For PLUR1BUS memory cards, which are short German or English summaries, multilingual ranking quality is what counts, and long documents and code do not occur. The recommendation is therefore BGE: ahead or level on the benchmarks that matter here, Apache 2.0 without a license consent, at the price of roughly twice the CPU time per query, which is a fraction of a second for 40 short candidates on an ordinary server. Pick Jina only when a weak CPU runs into the reranker timeout of five seconds; the quality loss is small.
Sources: Jina model card, BGE model card, jina-reranker-v3 paper, table 2, Jina Reranker v2 announcement.
Settings for both stages are described in docs/configuration.md; the installer offers the preparation profiles for the local models.
Omitting namespaces preserves the legacy-flat layout exactly:
{baseDbPath}/{agentId}. Named routing is enabled only by supplying the strict
object explicitly:
{
"baseDbPath": "~/.openclaw/memory",
"namespaces": {
"activeWriteNamespace": "lancedb-local",
"activeRecallNamespaces": ["lancedb-local"],
"legacyReadOnlyNamespaces": ["lancedb-namespaced"],
"crossNamespaceRecall": true
}
}
An explicit baseDbPath may be the named root, as above, or the active writer
leaf (~/.openclaw/memory/lancedb-local); both forms resolve to the same
layout. Namespace identifiers must match
^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$. The writer must occur in active recall,
and active namespaces must be disjoint from legacy read-only namespaces.
Legacy namespaces participate only when crossNamespaceRecall is exactly
true; they are opened without table creation, schema migration, or mutation.
Every recalled table must use the configured embedding dimensions. Each table runs the existing recall pipeline. When multiple live tables participate, PLUR1BUS waits for all of them and performs one stable global score merge; the one-table path remains direct. Duplicate IDs and canonical heading/text are collapsed, canonical plus memory results share the configured output cap, and child decision traces are replayed through the existing trace caps. A namespace changes storage routing for the current agent only. Sharing between agents, workspaces, or users is separate ACL work owned by B13.
Chat models are selected per owning feature. If an optional feature model is
absent, PLUR1BUS uses the effective OpenClaw agent model and sends no model
property. Features never inherit merging.model, its endpoint, credential, or
headers. Existing feature/profile activation, budgets, confirmation gates,
rate limits, and fail-soft behavior remain unchanged; Safe produces zero
PLUR1BUS native/direct chat calls.
The four selection modes are openclaw-default (native with no model),
openclaw-override (feature-local model through OpenClaw), direct-override
(feature-local model plus direct transport), and unavailable. failed is the
stable diagnostic outcome when a selected transport rejects. Provider/model
metadata returned by OpenClaw may be recorded without credentials, prompts, or
headers. Native routes bypass the PLUR1BUS result cache; complete direct routes
retain exact caching.
Direct transport without a feature-local model fails closed and sends no
request. A configured credential that is unresolved is unavailable; it never
falls through to native OpenClaw host credentials and does not abort plugin
registration. runtime.llm.complete missing or unavailable is fail-soft and
does not select a hard-coded model.
A session-bound command capability omits agentId. Global hook, tool, and
background calls retain the target agent and require entry-level
llm.allowAgentIdOverride:true. A model-only native override requires
llm.allowModelOverride:true and obeys allowedModels. Installer preserve
never grants LLM trust, and neither Safe nor Recommended adds those entry-level
bits.
runtime.llm.complete resolves the effective primary selection and does not
execute the configured model fallback array in the installed runtime. PLUR1BUS
neither claims nor emulates a host fallback chain.
PLUR1BUS caches only exact, agent-scoped results from an explicit allowlist of deterministic internal LLM transformations. The default in-memory cache uses a 24-hour absolute TTL (llmResultCacheTtlMs: 86400000, clamped to 60 s–7 d) and holds 256 entries per plugin registration (llmResultCacheMaxEntries, clamped to at most 10,000). Optional prompt-free SQLite persistence is off by default; when enabled with llmResultCachePersist, it stores hashed keys, results, usage metadata, and timestamps under the memory database path without storing plaintext prompts, credentials, or headers. llmResultCacheMaxBytes defaults to 67,108,864 bytes and is clamped to at most 1 GiB; clamped values log a warning.
The six runtime settings are llmResultCacheEnabled (default true), llmResultCacheTtlMs (default 86400000), llmResultCacheMaxEntries (default 256), llmResultCachePersist (default false), llmResultCacheMaxBytes (default 67108864), and llmResultCacheMetrics (default true). Missing values come from the manifest; an explicit false remains authoritative.
Operational notes:
node:sqlite module available throughout the supported Node.js runtime range; if SQLite initialization is unavailable, the cache falls back to memory-only.0o700, file 0o600 under the memory database path). Responses may contain condensed memory content — enable persistence only where that is acceptable.temperature: 0 for determinism, and llm-call.js now actually forwards temperature to the provider (previously the setting was silently ignored). Existing configs that set temperature therefore change their effective provider behavior.Non-goals and bypasses:
wie wird das Wetter morgen?.The /state status section reports cache hit rate, memory/persistent hits, persistence state, and avoided input/output tokens. It intentionally reports token counts, not money.
emotion.t3 — the tier-3 emotion classifier uses the effective OpenClaw
agent model when its model is absent. A complete feature-local direct override
may instead provide its own model and transport. If neither route is available,
the classifier falls back to Tier-2 heuristics: it does not label cards, so
it never poisons results by marking everything fakt.
emotion.temperaments — per-agent emotional temperament. Ships with generic defaults only (main slightly more sensitive, everyone else balanced). Pick a preset via /plur1bus temperament <preset> (ausgewogen, warm, kühl, feurig, stoisch) — requires a gateway restart. Mood always derives from conversation content; the temperament only shapes how strongly and how long it swings. The current mood is written to .emotional-state.json (machine-readable, survives restarts) and .current-mood.txt (human-readable) in the agent workspace, injected as a mood line into the recall context, stamped on every memory card (moodContextAtCapture), and emotionally intense memories decay slower (intensityHalfLifeFactor).
security.allowedUserIds / security.allowedChatIds — identity-aware authorization for commands and destructive flows.
userId membership in allowedUserIds (chatId alone is never sufficient), plus allowedChatIds when that list exists./enable, /disable, /plur1bus setup, /forget, /correct and confirmation flows.security.allowChatConfigCommands (default true) — disables operator-level config mutating commands (/enable, /disable, /plur1bus setup) when set to false. Use this in shared channels if you want a hard stop on chat-driven writes. Writes are still guarded by a file lock.
security.allowModelDestructiveMemoryOps (default true) — keeps model-facing tools memory_forget and knowledge_update available unless you explicitly disable them.
scope values now support agent-private (default), workspace and user.
agent-private remains per-agent.workspace shares by workspace.user is owner-bound: der aufrufende userId wird gespeichert und bei Sichtbarkeit/Mutation geprüft./share <id> kopiert eine sichtbare Karte nach bestätigter, an Benutzer und
Chat gebundener Bestätigung in den Workspace-Pool. /share <id> --user nutzt
dieselbe Bestätigung, erzeugt aber einen nur für denselben Kanal, Account und
Benutzer sichtbaren User-Pool. Die Grammatik ist strikt: nur ein vollständiges
UUID-id, optional genau --user, oder /share confirm <nonce> sind gültig;
unbekannte oder doppelte Optionen werden vor jedem Store-, DB-, Embedding- oder
Provider-Zugriff abgelehnt. Eine Freigabe ist copy, never move: die private
Ursprungskarte bleibt unverändert, und die autorisierte Shared-Kopie enthält
einen kanonischen Origin-Verweis. Recall darf die optionalen Shared-Quellen
ergänzend lesen und dedupliziert den kanonischen Ursprung; sie ersetzen weder
primären Recall noch dessen ACL.
Physische Routen sind kein benutzergesteuerter Pfad: ihre Segmente sind höchstens
64 Zeichen lang und werden als .plur1bus-shared/workspaces/w-<62hex> oder
.plur1bus-shared/users/u-<62hex> abgelegt. Die Berechtigung bindet den
kanonischen Workspace konfliktablehnend (keine versteckte Alias-Priorität) und
den vollständigen Kanal+Account+Benutzer-Prinzipal. Fehlende oder abweichende
Bindungen sind nicht sichtbar und nicht mutierbar; fehlend und verweigert
werden gleich behandelt. /memory und /share --user verwenden den direkt
vom Host gelieferten Account.
Der aktuelle OpenClaw-Hook kann die optionale automatische User-Shared-Recall
Quelle ausschließlich bei aktiviertem autoRecall verwenden. Er benötigt
einen account-tragenden Session-Key, ein exaktes Host-Run-Ticket oder eine
konservative default-only Account-Topologie. Native und Slash-Kommandos prägen
absichtlich kein Route-Ticket, weil behandelte Kommandos den Prompt-Hook nicht
erreichen. Bei mehrdeutigen benannten/multi-account Main-, Group- oder
Channel-Turns wird nur diese optionale Quelle ausgelassen; andere Recall-Quellen
bleiben unberührt. Ein zuletzt gespeicherter Session-Route-Wert ist kein
turn-gebundener Account-Beweis.
Legacy rows that used the old workspace_shared scope remain in their
authoritative private table until an operator explicitly migrates them. Start
with the non-mutating audit:
/plur1bus migrate-legacy-shared
Use --report <name.json> for a fixed private report name, and resume a bounded
dry run with the opaque --cursor <token> returned by the previous run. After
reviewing the report, run --apply without a dry-run cursor; apply re-reads each
source row, writes and verifies an idempotent workspace copy, and only then
marks the legacy source. The command never deletes or re-scopes the source row:
workspace_shared legacy rows are not reinterpreted. The operation is bounded
per run to 250 rows, 4 MiB source bytes, 100 provider calls, and 60 seconds.
The opaque cursor pins source versions and dry-run mode; an unavailable or
changed pinned version, mode mismatch, checksum/binding failure, timeout, or
uncertain commit aborts the run and requires the documented continuation or a
restart without the cursor. Apply never accepts a dry-run cursor.
It is operator-destructive, so it requires the same user authorization as
/forget; cron identity does not bypass that gate. Reports are no-clobber
0600 JSON files below .plur1bus/migrations/ and exclude memory content,
vectors, evidence, and provenance.
The migration runs only through the destructively authorized initialized runtime command; there is no standalone DB/config/credential bootstrap. Multi-Namespace, Neo/Obsidian aliases, Semantic Lens, CRR, the OpenClaw default LLM, and per-agent credentials do not change under sharing or migration.
security.allowModelDestructiveMemoryOps (default true) — the model-facing tools memory_forget and knowledge_update mutate persistent memory/knowledge state. Set this flag to false if you want a hard opt-out for model-driven destructive memory writes.
Core memory (capture, recall, search) works from manifest-safe defaults without profile confirmation. Argument-less setup only lists the choices, and start is read-only status/onboarding guidance:
# In Telegram
/plur1bus setup
/plur1bus start
Apply a profile only by naming it explicitly:
/plur1bus setup safe
/plur1bus setup recommended
An explicit selection records setupProfile and featuresConfirmedAt. Recommended enables additional features while retaining merge and Obsidian safety gates; vault discovery alone never counts as confirmation.
LanceDB is the authoritative store: every memory card lives there first, indexed per agent for isolation. The Obsidian bridge mirrors cards into a Markdown vault so the user can read, link, and edit them with normal tools; LanceDB stays the source of truth and the bridge re-syncs on changes.
A daily consolidation job detects duplicates and generates merge proposals (never auto-applies). A critical-push classifier (run via the OpenClaw-managed cron as the exact command /plur1bus internal classify-recent) labels recently captured cards by sensitive entity type (person, relationship, birthday, money/account, health, access/password) using the configured chat model, and — when a per-agent daily threshold (maxPerDay) is not yet exceeded — emits a short confirmation message per critical card. The command handler converts returned pushMessages into a validated native command reply; OpenClaw's dispatcher owns finalization and delivery. Multiple push texts are combined in their original order; partial classifier failures are reported alongside successfully produced pushes. The per-day counter is enforced across runs, and each card is classified exactly once, so no card is pushed twice.
The recall pipeline runs embedding → LanceDB vector search → optional query refinement → temporal filter → canonical KNOWLEDGE.md search → score/status processing → graph spread and hydration → budget allocation → optional rerank → deduplication → ACL filtering → finalization. The caller may then append bounded Semantic Lens and Conversation Reactivation Recall results; neither replaces the primary recall.
npm install
npm test # full serialized Node test runner
No build step. ESM-only. Tests are unit-level and DB-free; the LanceDB adapter is mocked behind a thin interface.
Recalled memories are rendered as historical evidence, not as current user requests. A memory that contains an old imperative such as a download, send, write, delete, install, purchase, network action, or command must not trigger that action unless the current visible user turn asks for the same action.
The recall block uses escaped metadata attributes and wraps recalled text in quoted-evidence elements, so prompt boundaries stay explicit even when old memory text contains tool-like markup.
Version 6.x is a major upgrade. If you ran 5.x:
init(). New columns: status, versionNumber, previousVersion, supersededBy, updateSource, updateEvidence, reconsolidationConfidence, versionCreatedAt, updatedAt. Migration is idempotent and non-destructive./plur1bus setup safe or /plur1bus setup recommended only when you intentionally want to persist a profile; /plur1bus setup and /plur1bus start are non-mutating.merging.autoApply defaults to false. Merge candidates are written to merge-proposals.jsonl instead of being applied automatically. Set autoApply: true to restore 5.x behavior.mode: "apply" with safety gates (backups, audit log, vault path confirmation). Default is mode: "augment" (read-only). Confirm vault path explicitly before first write.reranker.timeoutMs, reranker.fallbackOnError, merging.autoApply, merging.mode, obsidianBridge.backupBeforeApply, obsidianBridge.auditLog, obsidianBridge.requireVaultPathConfirmation, obsidianBridge.morningReview.status, obsidianBridge.eveningReview.status, emotion.tier, emotion.t2.enabled, emotion.t3.enabled, emotion.t3.model, emotion.t3.apiKey, emotion.t3.escalationConfidence, emotion.t3.timeoutMs, emotion.moodInfluence, emotion.intensityHalfLifeFactor, emotion.temperaments.<agentId>.See v5_TO_v6_MIGRATION.md for the full migration guide.
MIT — see LICENSE.
312 commits
JavaScript
98.6%
Shell
1.4%