OpenClaw¶
The OpenClaw plugin in adapters/openclaw/
gives any OpenClaw agent the learning loop: it recalls learned context before each turn, records the turn after it, and
adds three tools. It needs no changes to your agents.
Requirements¶
- OpenClaw
>=2026.10.0(the plugin is type-checked againstopenclaw@2026.10.4). - Node.js
>=24.16.0 <25or>=26.1.0(the plugin'senginesfield; Node 25 is not supported). - A reachable Myelinate gateway and, outside dev mode, an API key with the
learnscope.
Install¶
From npm (@myelinate/openclaw-plugin, published with
signed provenance):
openclaw plugins install @myelinate/openclaw-plugin --force --accept-capabilities
openclaw config set plugins.entries.myelinate.config.gatewayUrl "https://learner.internal:8080"
openclaw config set plugins.entries.myelinate.hooks.allowConversationAccess true
openclaw plugins enable myelinate
What the flags do:
--forceconfirms a source outside OpenClaw's ClawHub registry for a non-interactive install. Review the package first (npm view @myelinate/openclaw-plugin).--accept-capabilitiesrecords consent for the capabilities the plugin declares (the three tools).
To run the plugin from a checkout instead (for development), build it, then link the directory so git pull plus a
rebuild updates it:
cd adapters/openclaw && npm install --no-audit --no-fund && npm run build && cd ../..
openclaw plugins install ./adapters/openclaw --link --force --accept-capabilities
# after changes: npm run build in adapters/openclaw, then openclaw plugins reload myelinate
hooks.allowConversationAccess is required. The plugin uses the before_prompt_build and agent_end hooks, and
OpenClaw only lets non-bundled plugins use conversation hooks with this grant. Note that it sits under hooks, not
under the plugin's config.
Check the result:
inspect --runtime should list the hooks before_prompt_build and agent_end and the tools skill_view, remember
and rate_last_turn.
Instead of config set, you can merge the entry from
examples/openclaw-config.json5:
openclaw config patch --file examples/openclaw-config.json5 --dry-run
openclaw config patch --file examples/openclaw-config.json5
{
plugins: {
entries: {
myelinate: {
enabled: true,
hooks: { allowConversationAccess: true },
config: {
gatewayUrl: "http://localhost:8080",
agentId: "openclaw",
perUserMemory: true,
timeoutMs: 4000,
},
},
},
},
}
Configuration¶
Plugin config lives under plugins.entries.myelinate.config (schema in openclaw.plugin.json):
| Field | Type | Default | Description |
|---|---|---|---|
gatewayUrl |
string | (required) | Myelinate base URL, e.g. https://learner.internal:8080. A path prefix is kept, so a gateway behind https://host/myelinate works. |
apiKey |
string | none | Tenant API key (myl_...) with the learn scope. Prefer the MYELINATE_API_KEY env var. |
agentId |
string | openclaw |
Knowledge namespace. OpenClaw agents with the same agentId share skills and memories. |
perUserMemory |
boolean | true |
Scope preferences and memories to the message sender. |
timeoutMs |
number | 4000 |
Timeout per gateway request. Requests are retried twice on network errors, 5xx and 429. |
Environment variables, used when the config field is not set:
| Variable | Used for |
|---|---|
MYELINATE_URL |
gatewayUrl (falls back to http://localhost:8080) |
MYELINATE_API_KEY |
apiKey |
They must be set in the environment of the OpenClaw gateway process. Because gatewayUrl is required by the schema,
set it in config; the env fallback mainly matters for the API key.
To give different OpenClaw agents separate knowledge, run them with different agentIds (one plugin config per
OpenClaw instance). To share a skill library with agents in other frameworks, use the same agentId there.
What it does¶
Hooks¶
| Hook | Behaviour |
|---|---|
before_prompt_build |
Takes the current user message, calls recall with agentId, the sender as user_id (when perUserMemory is on) and the OpenClaw session key as session_id. If anything was learned, it prepends <learned-context>…</learned-context> to the turn's context. |
agent_end |
Records the turn: the user and assistant messages (last 40), tool calls with results and errors, skills loaded with skill_view, duration, the recall_id, and the outcome. |
The outcome is failure when OpenClaw reports the run as unsuccessful or with an error, and unknown otherwise.
Unknown outcomes are judged by the gateway's LLM (when configured) and refined by rate_last_turn feedback.
Tools¶
| Tool | Parameters | Behaviour |
|---|---|---|
skill_view |
name |
Loads a learned skill's full text (the ones listed under "Other learned skills"). The skill is credited with this turn's outcome. |
remember |
content, kind (fact | preference | lesson, default fact) |
Saves a durable memory, scoped to the sender when perUserMemory is on. |
rate_last_turn |
score (0–1), comment?, correction? |
Records the user's verdict on the previous answer when they clearly approve or complain. Finds the previous episode in memory, or via GET /v1/episodes/latest for the session (so it works across restarts and replicas). |
rate_last_turn is the plugin's main feedback channel. A correction ("no, the staging cluster is stg-2") is the
most useful thing a user can give; see what to send.
Fail-open¶
Every gateway call fails open. If recall fails the turn runs without learned context and the plugin logs
[myelinate] recall failed, continuing without learned context. If recording fails it logs
[myelinate] failed to record episode. The turn itself is never blocked beyond timeoutMs per request (plus retries).
Zero-code alternative: diagnostics-otel¶
OpenClaw's bundled diagnostics-otel plugin can export traces, which Myelinate can ingest to record turns without this
plugin: point its http/protobuf trace exporter at the gateway's /v1/traces. It does not inject
learned context. See OpenTelemetry. Use this plugin when you want agents to use what was
learned.
Troubleshooting¶
| Symptom | Fix |
|---|---|
Hooks don't run; inspect --runtime shows no hooks, or doctor reports a blocked conversation hook |
Set plugins.entries.myelinate.hooks.allowConversationAccess to true (under hooks, not config). Also check hooks.allowPromptInjection is not false and, if you use plugins.allow, that it includes myelinate. |
Plugin fails to load: Cannot find module '@sinclair/typebox' |
Run npm install in adapters/openclaw (linked installs use the directory as is). |
| Unsupported Node version | Use Node 24.16+ (not 25) or 26.1+. |
Config validation error on myelinate |
The schema rejects unknown keys and requires gatewayUrl. Allowed keys: gatewayUrl, apiKey, agentId, perUserMemory, timeoutMs. |
Log: recall failed ... myelinate 401 / 403 |
Missing or wrong API key, or the key lacks the learn scope. |
Log: recall failed ... TimeoutError / fetch failed |
Gateway unreachable from the OpenClaw host, or slower than timeoutMs. Check curl $MYELINATE_URL/healthz. |
| No learned context injected | Normal until something has been learned, and for the 5% holdout runs. Check with curl -X POST $MYELINATE_URL/v1/recall ... using the same agent_id. |
rate_last_turn says "No previous turn to rate." |
No episode was recorded for this session yet (first turn, or recording failed). |
| Changed plugin source, nothing happens | openclaw plugins reload myelinate. |
To develop against the plugin, type-check it with npm run typecheck in adapters/openclaw. The plugin's REST
client (client.ts) is reusable outside OpenClaw; see TypeScript.