@remnic/plugin-openclaw 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Joshua Warren
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -21,8 +21,13 @@ Add the plugin to your `openclaw.json`:
21
21
  ```jsonc
22
22
  {
23
23
  "plugins": {
24
- "allow": ["openclaw-engram"],
25
- "slots": { "memory": "openclaw-engram" }
24
+ "allow": ["openclaw-remnic"],
25
+ "slots": { "memory": "openclaw-remnic" },
26
+ "entries": {
27
+ "openclaw-remnic": {
28
+ "package": "@remnic/plugin-openclaw"
29
+ }
30
+ }
26
31
  }
27
32
  }
28
33
  ```
@@ -38,13 +43,215 @@ launchctl kickstart -k gui/501/ai.openclaw.gateway
38
43
  This plugin hooks into the OpenClaw gateway lifecycle:
39
44
 
40
45
  - **`gateway_start`** -- initializes the Remnic memory engine
41
- - **`before_agent_start`** -- injects relevant memories into the agent's context
46
+ - **`before_agent_start`** / **`before_prompt_build`** -- injects relevant memories into the agent's context
42
47
  - **`agent_end`** -- buffers the conversation turn for extraction
43
- - **Tools** -- registers `memory_search`, `memory_stats`, and other agent tools
48
+ - **`before_compaction`** / **`after_compaction`** -- saves checkpoints and triggers session reset on context compaction
49
+ - **`before_reset`** -- bounded flush of the in-flight buffer before OpenClaw discards a session
50
+ - **`commands.list`** -- exposes Remnic slash-command descriptors to the command palette
51
+ - **`session_start`** / **`session_end`** -- session lifecycle tracking
52
+ - **`before_tool_call`** / **`after_tool_call`** -- tool usage observation for analytics
53
+ - **`llm_output`** -- LLM token usage tracking
54
+ - **`subagent_spawning`** / **`subagent_ended`** -- subagent lifecycle observation
55
+ - **Tools** -- registers `memory_search`, `memory_get`, `memory_stats`, and other agent tools
44
56
  - **Commands** -- provides CLI commands for memory management
45
57
 
46
58
  All memory processing uses [`@remnic/core`](https://www.npmjs.com/package/@remnic/core). Data stays on your local filesystem as plain markdown files.
47
59
 
60
+ ## Slot Selection
61
+
62
+ Remnic is an exclusive memory-slot plugin. When `plugins.slots.memory` points
63
+ to another plugin, Remnic now validates that mismatch and either errors or
64
+ loads passively depending on `slotBehavior`:
65
+
66
+ ```jsonc
67
+ {
68
+ "plugins": {
69
+ "entries": {
70
+ "openclaw-remnic": {
71
+ "config": {
72
+ "slotBehavior": {
73
+ "requireExclusiveMemorySlot": true,
74
+ "onSlotMismatch": "error"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ Passive mode keeps the tool/service surface available but skips prompt
84
+ injection and extraction hooks so two memory plugins do not race each other.
85
+
86
+ ## Supported OpenClaw Memory Features
87
+
88
+ Remnic supports the following OpenClaw memory integration points:
89
+
90
+ ### Memory Prompt Injection
91
+
92
+ | Feature | Status | Since |
93
+ |---------|--------|-------|
94
+ | `before_agent_start` hook (legacy) | Supported | 2025.x |
95
+ | `before_prompt_build` hook (new SDK) | Supported | 2026.3.22 |
96
+ | `registerMemoryPromptSection()` (structured builder) | Supported | 2026.3.22 |
97
+ | `registerMemoryCapability()` (unified capability) | Supported | 2026.4.5 |
98
+
99
+ ### Public Artifacts (memory-wiki bridge)
100
+
101
+ | Feature | Status | Since |
102
+ |---------|--------|-------|
103
+ | `publicArtifacts.listArtifacts()` | Supported | 2026.4.5 |
104
+
105
+ When `registerMemoryCapability` is available, Remnic registers a `publicArtifacts` provider that exposes wiki-safe memory files:
106
+
107
+ - **facts/** -- extracted knowledge (dated subdirectories)
108
+ - **entities/** -- entity knowledge graph
109
+ - **corrections/** -- fact corrections
110
+ - **artifacts/** -- structured artifacts
111
+ - **profile.md** -- agent identity summary
112
+
113
+ Private runtime state (state/, questions/, transcripts/, archive/, buffers) is never exposed.
114
+
115
+ With this feature, `openclaw wiki status` reports Remnic artifacts, and `memory-wiki` bridge mode can discover and ingest them.
116
+
117
+ ### Session Lifecycle
118
+
119
+ | Feature | Status | Since |
120
+ |---------|--------|-------|
121
+ | `session_start` / `session_end` hooks | Supported | 2026.3.22 |
122
+ | `before_compaction` / `after_compaction` hooks | Supported | 2026.3.22 |
123
+ | `before_reset` hook | Supported | 2026.4.10 |
124
+ | `commands.list` runtime discovery | Supported | 2026.4.10 |
125
+ | `api.resetSession()` (compaction reset) | Supported | 2026.3.22 |
126
+ | Checkpoint saves before compaction | Supported | 2026.3.22 |
127
+
128
+ Reset handling is configurable:
129
+
130
+ ```jsonc
131
+ {
132
+ "plugins": {
133
+ "entries": {
134
+ "openclaw-remnic": {
135
+ "config": {
136
+ "flushOnResetEnabled": true,
137
+ "beforeResetTimeoutMs": 2000
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ The reset path clears per-session prompt caches and workspace override state.
146
+ If `flushOnResetEnabled` is true, Remnic also attempts a bounded extraction
147
+ flush before the reset completes.
148
+
149
+ Session-scoped recall controls are exposed through OpenClaw's command
150
+ discovery surface:
151
+
152
+ - `remnic off` / `remnic on`
153
+ - `remnic status`
154
+ - `remnic clear`
155
+ - `remnic stats`
156
+ - `remnic flush`
157
+
158
+ When verbose mode is enabled, Remnic prints its recall decision header inline
159
+ and can optionally persist JSONL recall transcripts under
160
+ `<memoryDir>/state/plugins/openclaw-remnic/transcripts/`.
161
+
162
+ ### Observation Hooks
163
+
164
+ | Feature | Status | Since |
165
+ |---------|--------|-------|
166
+ | `before_tool_call` / `after_tool_call` | Supported | 2026.3.22 |
167
+ | `llm_output` (token tracking) | Supported | 2026.3.22 |
168
+ | `subagent_spawning` / `subagent_ended` | Supported | 2026.3.22 |
169
+
170
+ ### Dreaming
171
+
172
+ OpenClaw's dreaming feature (background memory consolidation) is handled by OpenClaw's built-in `memory-core` extension. Remnic implements its own consolidation pipeline (extraction, deduplication, graph maintenance, hourly summaries) that runs independently of OpenClaw's dreaming system. The two systems are complementary -- Remnic's consolidation handles the heavy memory extraction, while OpenClaw's dreaming (if enabled alongside Remnic) can further organize knowledge.
173
+
174
+ The plugin manifest now accepts the OpenClaw `dreaming` config block directly
175
+ so newer runtimes do not reject the config at validation time, and the OpenClaw
176
+ adapter now injects recent diary entries as `## Recent Dreams (Remnic)` when
177
+ the journal contains entries. The adapter also imports `DREAMS.md` entries into
178
+ Remnic storage as `memoryKind: "dream"` with stable provenance so file-watch
179
+ replays stay idempotent:
180
+
181
+ ```jsonc
182
+ {
183
+ "plugins": {
184
+ "entries": {
185
+ "openclaw-remnic": {
186
+ "config": {
187
+ "dreaming": {
188
+ "enabled": false,
189
+ "journalPath": "DREAMS.md",
190
+ "maxEntries": 500,
191
+ "injectRecentCount": 3,
192
+ "minIntervalMinutes": 120,
193
+ "narrativeModel": "gpt-5.2",
194
+ "narrativePromptStyle": "reflective",
195
+ "watchFile": true
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ When Remnic's consolidation pass produces a reflective multi-session summary and
205
+ the interval gate is satisfied, the OpenClaw adapter appends a new dream entry
206
+ back to `DREAMS.md` through the shared writer using the OpenAI Responses API.
207
+
208
+ The shared `@remnic/core` surface parsers also understand `HEARTBEAT.md`. The
209
+ OpenClaw adapter imports those entries as `memoryKind: "procedural"`, gates
210
+ normal recall during heartbeat-triggered runs, injects the active heartbeat plus
211
+ `## Previous Runs`, and skips episodic buffering for heartbeat turns by default.
212
+ Detection can use explicit runtime metadata, a documented heuristic fallback, or
213
+ `auto` to prefer runtime metadata and fall back when needed. All of that logic
214
+ stays in the OpenClaw adapter; standalone/core remains host-agnostic.
215
+
216
+ ## Codex Compatibility
217
+
218
+ Remnic now advertises and parses a dedicated `codexCompat` block for bundled
219
+ Codex-provider safety work:
220
+
221
+ ```jsonc
222
+ {
223
+ "plugins": {
224
+ "entries": {
225
+ "openclaw-remnic": {
226
+ "config": {
227
+ "codexCompat": {
228
+ "enabled": false,
229
+ "threadIdBufferKeying": true,
230
+ "compactionFlushMode": "auto",
231
+ "fingerprintDedup": true
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ `codexCompat.enabled` defaults to `false`, so operators only opt into bundled
241
+ Codex thread buffering and compaction behavior when they explicitly enable it.
242
+
243
+ This governs Remnic's own buffering and extraction behavior only. Remnic still
244
+ uses its own extraction auth path; OpenClaw's bundled Codex provider auth does
245
+ not replace Remnic's extraction credentials.
246
+
247
+ ### Bridge Mode
248
+
249
+ | Feature | Status |
250
+ |---------|--------|
251
+ | Embedded mode (in-process EMO + HTTP :4318) | Supported |
252
+ | Delegate mode (connects to running daemon) | Supported |
253
+ | Auto-detection (daemon running = delegate) | Supported |
254
+
48
255
  ## Standalone usage
49
256
 
50
257
  If you're not using OpenClaw, use [`@remnic/cli`](https://www.npmjs.com/package/@remnic/cli) or [`@remnic/server`](https://www.npmjs.com/package/@remnic/server) instead.
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  FallbackLlmClient
3
- } from "./chunk-BIBYVWVY.js";
3
+ } from "./chunk-3SA5F4WT.js";
4
4
  import {
5
5
  listJsonFiles
6
6
  } from "./chunk-5LE4HTVL.js";
7
7
  import {
8
8
  log
9
- } from "./chunk-DMGIUDBO.js";
9
+ } from "./chunk-UFU5GGGA.js";
10
10
  import "./chunk-MLKGABMK.js";
11
11
 
12
12
  // ../remnic-core/src/calibration.ts
@@ -6,10 +6,10 @@ import {
6
6
  stitchCausalChain,
7
7
  validateCausalEdge,
8
8
  writeChainIndex
9
- } from "./chunk-HSBPDYF4.js";
10
- import "./chunk-JGEKL3WH.js";
9
+ } from "./chunk-BXTMZDRT.js";
10
+ import "./chunk-YHH3SXKD.js";
11
11
  import "./chunk-5LE4HTVL.js";
12
- import "./chunk-DMGIUDBO.js";
12
+ import "./chunk-UFU5GGGA.js";
13
13
  import "./chunk-MLKGABMK.js";
14
14
  export {
15
15
  makeEdgeId,
@@ -1,20 +1,25 @@
1
+ import {
2
+ buildExtensionsBlockForConsolidation,
3
+ runPostConsolidationMaterialize
4
+ } from "./chunk-J2FCINY7.js";
5
+ import {
6
+ FallbackLlmClient
7
+ } from "./chunk-3SA5F4WT.js";
8
+ import "./chunk-5VTGFKKU.js";
1
9
  import {
2
10
  readChainIndex,
3
11
  resolveChainsDir
4
- } from "./chunk-HSBPDYF4.js";
12
+ } from "./chunk-BXTMZDRT.js";
5
13
  import {
6
14
  isRecord
7
- } from "./chunk-JGEKL3WH.js";
8
- import {
9
- FallbackLlmClient
10
- } from "./chunk-BIBYVWVY.js";
15
+ } from "./chunk-YHH3SXKD.js";
11
16
  import {
12
17
  listJsonFiles,
13
18
  readJsonFile
14
19
  } from "./chunk-5LE4HTVL.js";
15
20
  import {
16
21
  log
17
- } from "./chunk-DMGIUDBO.js";
22
+ } from "./chunk-UFU5GGGA.js";
18
23
  import "./chunk-MLKGABMK.js";
19
24
 
20
25
  // ../remnic-core/src/causal-consolidation.ts
@@ -159,7 +164,13 @@ async function deriveCausalPromotionCandidates(options) {
159
164
  if (trajectories.length < options.config.minRecurrence) return [];
160
165
  const chainsDir = resolveChainsDir(options.memoryDir, options.causalTrajectoryStoreDir);
161
166
  const chainIndex = await readChainIndex(chainsDir);
162
- const context = formatCausalContext(trajectories, chainIndex);
167
+ let context = formatCausalContext(trajectories, chainIndex);
168
+ if (options.pluginConfig) {
169
+ const extBlock = await buildExtensionsBlockForConsolidation(options.pluginConfig);
170
+ if (extBlock.length > 0) {
171
+ context += "\n\n" + extBlock;
172
+ }
173
+ }
163
174
  const llm = new FallbackLlmClient(options.gatewayConfig);
164
175
  if (!llm.isAvailable(options.gatewayAgentId)) {
165
176
  log.debug("[cmc] no LLM available for consolidation \u2014 skipping");
@@ -199,7 +210,11 @@ async function synthesizeCausalPreferencesViaLlm(options) {
199
210
  return null;
200
211
  }
201
212
  }
213
+ async function materializeAfterCausalConsolidation(options) {
214
+ return runPostConsolidationMaterialize("[cmc]", options);
215
+ }
202
216
  export {
203
217
  deriveCausalPromotionCandidates,
218
+ materializeAfterCausalConsolidation,
204
219
  synthesizeCausalPreferencesViaLlm
205
220
  };
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  searchCausalTrajectories
3
- } from "./chunk-BZ27H3BL.js";
3
+ } from "./chunk-RMFPW4VK.js";
4
4
  import {
5
5
  readChainIndex,
6
6
  resolveChainsDir
7
- } from "./chunk-HSBPDYF4.js";
8
- import "./chunk-JGEKL3WH.js";
7
+ } from "./chunk-BXTMZDRT.js";
8
+ import "./chunk-YHH3SXKD.js";
9
9
  import "./chunk-5LE4HTVL.js";
10
10
  import {
11
11
  log
12
- } from "./chunk-DMGIUDBO.js";
12
+ } from "./chunk-UFU5GGGA.js";
13
13
  import "./chunk-MLKGABMK.js";
14
14
 
15
15
  // ../remnic-core/src/causal-retrieval.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  appendEdge
3
- } from "./chunk-H3SKMYPU.js";
3
+ } from "./chunk-Z7GRLVK3.js";
4
4
  import "./chunk-MLKGABMK.js";
5
5
 
6
6
  // ../remnic-core/src/causal-trajectory-graph.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log
3
- } from "./chunk-DMGIUDBO.js";
3
+ } from "./chunk-UFU5GGGA.js";
4
4
 
5
5
  // ../remnic-core/src/json-extract.ts
6
6
  function stripCodeFences(text) {