@etzelm/openclaw-memory-hindsight 0.1.1 → 0.1.2
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/CHANGELOG.md +15 -0
- package/README.md +10 -0
- package/dist/client.js +2 -0
- package/dist/config.js +8 -1
- package/dist/index.js +22 -7
- package/openclaw.plugin.json +10 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 (2026-11-06)
|
|
4
|
+
|
|
5
|
+
### Security
|
|
6
|
+
|
|
7
|
+
- Refuse plain http to a non-loopback `hindsightApiUrl` unless the new `allowInsecureHttp` option
|
|
8
|
+
is `true` (CWE-319, cleartext transmission). Before, 0.1.1 sent the bearer token and
|
|
9
|
+
conversation text unencrypted and only warned when a token was set. Config resolution now
|
|
10
|
+
throws an error naming `allowInsecureHttp`; loopback http and https are unchanged.
|
|
11
|
+
- With the opt-in set, the service start warning says both the bearer token (if any) and
|
|
12
|
+
conversation text go unencrypted, even when no token is configured.
|
|
13
|
+
- Every request to Hindsight is sent with `redirect: "error"`, so an https endpoint cannot answer
|
|
14
|
+
307/308 and move the body (conversation text) to plain http.
|
|
15
|
+
- Upgrade note: a config that used a remote `http://` URL must switch to https, a loopback
|
|
16
|
+
tunnel, or set `allowInsecureHttp: true`.
|
|
17
|
+
|
|
3
18
|
## 0.1.1 (2026-11-06)
|
|
4
19
|
|
|
5
20
|
First version published to npm and ClawHub (0.1.0 was never published). Pre-publish hardening
|
package/README.md
CHANGED
|
@@ -116,6 +116,7 @@ tool conflict.
|
|
|
116
116
|
| `excludeProviders` | `[]` | Message providers that never recall or retain (for example `heartbeat`) |
|
|
117
117
|
| `skipOperationalSessions` | `true` | Skip cron, heartbeat and subagent sessions |
|
|
118
118
|
| `agentBankMap` | `{}` | Agent id to bank id, so several agents share one owner bank |
|
|
119
|
+
| `allowInsecureHttp` | `false` | Allow plain http to a non-loopback `hindsightApiUrl` (token and conversation text unencrypted) |
|
|
119
120
|
|
|
120
121
|
Fixed values (not configurable): recall max tokens 1024, query cap 800 characters, recall
|
|
121
122
|
timeout 10 s, retain and reflect timeout 30 s, memory get timeout 10 s (each combined with the
|
|
@@ -158,6 +159,15 @@ as of the vendor plugin 1.2.0:
|
|
|
158
159
|
|
|
159
160
|
The vendor plugin has features this one does not port; see [Limitations](#limitations).
|
|
160
161
|
|
|
162
|
+
## Security
|
|
163
|
+
|
|
164
|
+
Every request carries the bearer token (if set) and, for recall and retain, conversation text. The
|
|
165
|
+
plugin refuses a plain `http://` `hindsightApiUrl` to any host other than `localhost`, `127.0.0.0/8`
|
|
166
|
+
or `::1`, and config resolution fails with an error that names `allowInsecureHttp`. Prefer `https://`
|
|
167
|
+
or a loopback tunnel. To accept the risk on a trusted network, set `allowInsecureHttp: true`; the
|
|
168
|
+
plugin then warns at service start that the token and conversation text travel unencrypted.
|
|
169
|
+
Redirects are refused, so an https endpoint cannot forward a request body to plain http.
|
|
170
|
+
|
|
161
171
|
## Compatibility
|
|
162
172
|
|
|
163
173
|
`openclaw.install.minHostVersion` and `openclaw.compat.pluginApi` are `>=2026.10.1`. Evidence
|
package/dist/client.js
CHANGED
|
@@ -41,6 +41,7 @@ export class HindsightClient {
|
|
|
41
41
|
const response = await this.fetchImpl(url, {
|
|
42
42
|
method: init.method,
|
|
43
43
|
headers: this.headers(hasBody),
|
|
44
|
+
redirect: "error",
|
|
44
45
|
...(hasBody ? { body: JSON.stringify(init.body) } : {}),
|
|
45
46
|
...(init.signal ? { signal: init.signal } : {}),
|
|
46
47
|
});
|
|
@@ -110,6 +111,7 @@ export class HindsightClient {
|
|
|
110
111
|
const response = await this.fetchImpl(`${apiUrl}/health`, {
|
|
111
112
|
method: "GET",
|
|
112
113
|
headers: this.headers(false),
|
|
114
|
+
redirect: "error",
|
|
113
115
|
signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),
|
|
114
116
|
});
|
|
115
117
|
return response.ok;
|
package/dist/config.js
CHANGED
|
@@ -27,6 +27,7 @@ const CONFIG_KEYS = [
|
|
|
27
27
|
"excludeProviders",
|
|
28
28
|
"skipOperationalSessions",
|
|
29
29
|
"agentBankMap",
|
|
30
|
+
"allowInsecureHttp",
|
|
30
31
|
];
|
|
31
32
|
function isRecord(value) {
|
|
32
33
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -160,8 +161,13 @@ export const hindsightConfigSchema = {
|
|
|
160
161
|
: readBool(value, "preferObservations", false);
|
|
161
162
|
const retainMission = readString(value, "retainMission");
|
|
162
163
|
const apiToken = readApiToken(value);
|
|
164
|
+
const apiUrl = normalizeApiUrl(readString(value, "hindsightApiUrl"));
|
|
165
|
+
const allowInsecureHttp = readBool(value, "allowInsecureHttp", false);
|
|
166
|
+
if (isPlainHttpRemote(apiUrl) && !allowInsecureHttp) {
|
|
167
|
+
throw new Error("hindsightApiUrl is plain http to a non-loopback host, so the bearer token and conversation text would travel unencrypted; use https or a loopback tunnel, or set allowInsecureHttp: true to accept the risk");
|
|
168
|
+
}
|
|
163
169
|
return {
|
|
164
|
-
apiUrl
|
|
170
|
+
apiUrl,
|
|
165
171
|
...(apiToken ? { apiToken } : {}),
|
|
166
172
|
memoryMode: readEnum(value, "memoryMode", MEMORY_MODES, "hybrid"),
|
|
167
173
|
autoRecall: readBool(value, "autoRecall", true),
|
|
@@ -175,6 +181,7 @@ export const hindsightConfigSchema = {
|
|
|
175
181
|
excludeProviders: readStringList(value, "excludeProviders") ?? [],
|
|
176
182
|
skipOperationalSessions: readBool(value, "skipOperationalSessions", true),
|
|
177
183
|
agentBankMap: readBankMap(value),
|
|
184
|
+
allowInsecureHttp,
|
|
178
185
|
};
|
|
179
186
|
},
|
|
180
187
|
};
|
package/dist/index.js
CHANGED
|
@@ -79,11 +79,17 @@ export default definePluginEntry({
|
|
|
79
79
|
return startupConfig;
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
|
-
const service = createHindsightService({
|
|
82
|
+
const service = createHindsightService({
|
|
83
|
+
config: currentConfig,
|
|
84
|
+
logger: api.logger,
|
|
85
|
+
});
|
|
83
86
|
const flushDeduper = createFlushDeduper();
|
|
84
87
|
const runStartBySession = new Map();
|
|
85
88
|
const capability = {
|
|
86
|
-
providerRuntime: createHindsightProviderRuntime({
|
|
89
|
+
providerRuntime: createHindsightProviderRuntime({
|
|
90
|
+
providerId: PLUGIN_ID,
|
|
91
|
+
service,
|
|
92
|
+
}),
|
|
87
93
|
providerFlushPlanResolver: createProviderFlushPlanResolver(currentConfig),
|
|
88
94
|
recallToolNames: RECALL_TOOL_NAMES,
|
|
89
95
|
};
|
|
@@ -119,7 +125,10 @@ export default definePluginEntry({
|
|
|
119
125
|
!config.autoRecall ||
|
|
120
126
|
config.memoryMode === "tools" ||
|
|
121
127
|
isIncognitoSessionKey(sessionKey) ||
|
|
122
|
-
!sessionUsesMemory(config, {
|
|
128
|
+
!sessionUsesMemory(config, {
|
|
129
|
+
sessionKey,
|
|
130
|
+
messageProvider: ctx.messageProvider,
|
|
131
|
+
})) {
|
|
123
132
|
return undefined;
|
|
124
133
|
}
|
|
125
134
|
const route = routeAudience(ctx.agentId, ctx.memoryAudience, config.agentBankMap);
|
|
@@ -149,7 +158,9 @@ export default definePluginEntry({
|
|
|
149
158
|
}
|
|
150
159
|
api.logger.info?.(`memory-hindsight: injecting ${results.length} memories`);
|
|
151
160
|
// Before the user message, outside the system prompt, so the cached prefix stays stable.
|
|
152
|
-
return {
|
|
161
|
+
return {
|
|
162
|
+
prependContext: buildRecallContext(results, config.recallPromptPreamble),
|
|
163
|
+
};
|
|
153
164
|
}
|
|
154
165
|
catch (error) {
|
|
155
166
|
api.logger.warn?.(`memory-hindsight: auto-recall skipped: ${formatErrorMessage(error)}`);
|
|
@@ -170,7 +181,10 @@ export default definePluginEntry({
|
|
|
170
181
|
!Array.isArray(event.messages) ||
|
|
171
182
|
event.messages.length === 0 ||
|
|
172
183
|
isIncognitoSessionKey(sessionKey) ||
|
|
173
|
-
!sessionUsesMemory(config, {
|
|
184
|
+
!sessionUsesMemory(config, {
|
|
185
|
+
sessionKey,
|
|
186
|
+
messageProvider: ctx.messageProvider,
|
|
187
|
+
})) {
|
|
174
188
|
return;
|
|
175
189
|
}
|
|
176
190
|
const route = routeAudience(ctx.agentId, ctx.memoryAudience, config.agentBankMap);
|
|
@@ -238,8 +252,9 @@ export default definePluginEntry({
|
|
|
238
252
|
if (hasUnresolvedToken(raw)) {
|
|
239
253
|
api.logger.warn("memory-hindsight: hindsightApiToken not resolved; requests go unauthenticated");
|
|
240
254
|
}
|
|
241
|
-
|
|
242
|
-
|
|
255
|
+
// Config parsing already refuses plain remote http without the opt-in; warn only when opted in.
|
|
256
|
+
if (config.allowInsecureHttp && isPlainHttpRemote(config.apiUrl)) {
|
|
257
|
+
api.logger.warn("memory-hindsight: allowInsecureHttp is set and hindsightApiUrl is plain http to a non-loopback host; the bearer token (if any) and conversation text are sent unencrypted");
|
|
243
258
|
}
|
|
244
259
|
api.logger.info(`memory-hindsight: initialized (mode: ${currentConfig().memoryMode})`);
|
|
245
260
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -45,6 +45,11 @@
|
|
|
45
45
|
"help": "Agent id to owner bank id, so several agents share one private bank (for example wb-dev and wb-dev2 to wb)",
|
|
46
46
|
"advanced": true
|
|
47
47
|
},
|
|
48
|
+
"allowInsecureHttp": {
|
|
49
|
+
"label": "Allow Insecure HTTP",
|
|
50
|
+
"help": "Permit plain http to a non-loopback host. Token and conversation text are sent unencrypted; prefer https or a loopback tunnel",
|
|
51
|
+
"advanced": true
|
|
52
|
+
},
|
|
48
53
|
"retainMission": {
|
|
49
54
|
"label": "Retain Mission",
|
|
50
55
|
"help": "Applied to each bank before its first write: what Hindsight should extract",
|
|
@@ -66,6 +71,11 @@
|
|
|
66
71
|
"type": "string",
|
|
67
72
|
"enum": ["hybrid", "context", "tools"]
|
|
68
73
|
},
|
|
74
|
+
"allowInsecureHttp": {
|
|
75
|
+
"type": "boolean",
|
|
76
|
+
"default": false,
|
|
77
|
+
"description": "Allow plain http to a non-loopback hindsightApiUrl; the bearer token and conversation text then travel unencrypted"
|
|
78
|
+
},
|
|
69
79
|
"autoRecall": {
|
|
70
80
|
"type": "boolean"
|
|
71
81
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etzelm/openclaw-memory-hindsight",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Native Hindsight memory engine for OpenClaw: memory slot provider runtime, auto-recall, auto-retain, pre-compaction flush and hindsight_* agent tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hindsight",
|