@unblocklabs/unblock-memory 0.3.11 → 0.3.12
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/README.md +14 -3
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.js +7 -1
- package/dist/src/plugin.js +6 -0
- package/dist/src/runtime.d.ts +2 -0
- package/dist/src/runtime.js +20 -1
- package/openclaw.plugin.json +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -254,9 +254,20 @@ role-labeled, timestamped speaker messages; filtering metadata remains in the
|
|
|
254
254
|
session manifest. The projected file modification time matches the session
|
|
255
255
|
start time for meaningful chronological cluster reads. Session results include
|
|
256
256
|
provider, chat type, conversation identity, and start time as an ISO 8601 timestamp. They
|
|
257
|
-
participate in the same search and clustering index as file memory.
|
|
258
|
-
|
|
259
|
-
`
|
|
257
|
+
participate in the same search and clustering index as file memory. The plugin
|
|
258
|
+
automatically refreshes each configured agent's sessions every 15 minutes while
|
|
259
|
+
the Gateway runs. Set `syncIntervalMinutes` on the `sessions` corpus to an integer
|
|
260
|
+
from `1` to `1440`, or `0` for manual-only syncing. For example:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{ "name": "sessions", "kind": "sessions", "syncIntervalMinutes": 15 }
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The first refresh runs after one interval, not during startup. Restart the
|
|
267
|
+
Gateway after changing the interval. Refreshes are incremental; an already-running
|
|
268
|
+
sync is skipped, and failures are visible through `memory_sync_status` and retried
|
|
269
|
+
at the next interval. `memory_sync_sessions` still provides an immediate manual
|
|
270
|
+
refresh. Syncing and embedding run inside the Gateway process, without an LLM turn.
|
|
260
271
|
|
|
261
272
|
Indexes live at `~/.openclaw/agents/<agentId>/unblock-memory/index.sqlite` (or the
|
|
262
273
|
equivalent configured OpenClaw state directory). Durable agent-supplied event
|
package/dist/src/config.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ type SessionCorpusConfig = {
|
|
|
15
15
|
kind: "sessions";
|
|
16
16
|
chatTypes: readonly ChatType[];
|
|
17
17
|
maxExpandedTokens: number;
|
|
18
|
+
syncIntervalMinutes: number;
|
|
18
19
|
};
|
|
19
20
|
export type CorpusConfig = FileCorpusConfig | SkillCorpusConfig | SessionCorpusConfig;
|
|
20
21
|
export declare const DEFAULT_CORPORA: readonly FileCorpusConfig[];
|
package/dist/src/config.js
CHANGED
|
@@ -61,7 +61,7 @@ function resolveCorpora(value) {
|
|
|
61
61
|
return { name: "skills", kind: "skills", paths: corpus.paths.map((path) => path.trim()) };
|
|
62
62
|
}
|
|
63
63
|
if (corpus.kind === "sessions") {
|
|
64
|
-
assertOnlyKeys(corpus, ["name", "kind", "chatTypes", "maxExpandedTokens"], `corpora[${index}]`);
|
|
64
|
+
assertOnlyKeys(corpus, ["name", "kind", "chatTypes", "maxExpandedTokens", "syncIntervalMinutes"], `corpora[${index}]`);
|
|
65
65
|
if (name !== "sessions") {
|
|
66
66
|
throw new Error('unblock-memory session corpus must be named "sessions"');
|
|
67
67
|
}
|
|
@@ -71,8 +71,14 @@ function resolveCorpora(value) {
|
|
|
71
71
|
!chatTypes.every((chatType) => CHAT_TYPES.includes(chatType))) {
|
|
72
72
|
throw new Error(`unblock-memory corpus sessions chatTypes must contain channel, group, or direct`);
|
|
73
73
|
}
|
|
74
|
+
const syncIntervalMinutes = corpus.syncIntervalMinutes ?? 15;
|
|
75
|
+
if (typeof syncIntervalMinutes !== "number" || !Number.isInteger(syncIntervalMinutes) ||
|
|
76
|
+
syncIntervalMinutes < 0 || syncIntervalMinutes > 1440) {
|
|
77
|
+
throw new Error("unblock-memory corpus sessions syncIntervalMinutes must be an integer between 0 and 1440");
|
|
78
|
+
}
|
|
74
79
|
return {
|
|
75
80
|
name: "sessions",
|
|
81
|
+
syncIntervalMinutes,
|
|
76
82
|
kind: "sessions",
|
|
77
83
|
chatTypes: [...new Set(chatTypes)],
|
|
78
84
|
maxExpandedTokens: positiveInteger(corpus.maxExpandedTokens, DEFAULT_SESSION_MAX_EXPANDED_TOKENS, "corpus sessions maxExpandedTokens", MAX_SESSION_MAX_EXPANDED_TOKENS),
|
package/dist/src/plugin.js
CHANGED
|
@@ -403,6 +403,12 @@ export function registerUnblockMemory(api) {
|
|
|
403
403
|
runtime,
|
|
404
404
|
};
|
|
405
405
|
api.registerMemoryCapability(capability);
|
|
406
|
+
if (config.corpora.some((corpus) => corpus.kind === "sessions" && corpus.syncIntervalMinutes > 0)) {
|
|
407
|
+
api.on("gateway_start", () => runtime.startSessionSyncSchedule(api.config, (error) => {
|
|
408
|
+
api.logger.warn(`unblock-memory scheduled session sync could not start: ${String(error)}`);
|
|
409
|
+
}));
|
|
410
|
+
api.on("gateway_stop", () => runtime.stopSessionSyncSchedule());
|
|
411
|
+
}
|
|
406
412
|
if (config.people.enabled) {
|
|
407
413
|
const peopleStores = new PeopleStores({
|
|
408
414
|
maxOpenTodos: config.people.todos.maxOpen,
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ type StoredRunningSessionSync = Extract<StoredSessionSyncStatus, {
|
|
|
40
40
|
export declare function recoverInterruptedSessionSync(directory: string, statusPath: string, stale: StoredRunningSessionSync): Promise<SessionSyncStatus>;
|
|
41
41
|
export declare class QmdMemoryRuntime implements MemoryPluginRuntimeContract {
|
|
42
42
|
#private;
|
|
43
|
+
startSessionSyncSchedule(cfg: OpenClawConfig, onError: (error: unknown) => void): void;
|
|
44
|
+
stopSessionSyncSchedule(): void;
|
|
43
45
|
constructor(corpora: readonly CorpusConfig[], options?: {
|
|
44
46
|
analysisExecutable?: string;
|
|
45
47
|
keepEmbeddingModelWarm?: boolean;
|
package/dist/src/runtime.js
CHANGED
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { resolveAgentDir, resolveAgentWorkspaceDir, resolveStateDir, } from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
|
|
5
|
-
import { resolveAgentIdentity } from "openclaw/plugin-sdk/agent-runtime";
|
|
5
|
+
import { listAgentIds, resolveAgentIdentity } from "openclaw/plugin-sdk/agent-runtime";
|
|
6
6
|
import { QmdMemoryManager } from "./manager.js";
|
|
7
7
|
import { resolveTimezone } from "./session-projector.js";
|
|
8
8
|
import { resolveConfiguredSkillPath, resolveSessionSource, resolveSources } from "./sources.js";
|
|
@@ -68,6 +68,24 @@ export class QmdMemoryRuntime {
|
|
|
68
68
|
#keepEmbeddingModelWarm;
|
|
69
69
|
#stateRoot;
|
|
70
70
|
#managers = new Map();
|
|
71
|
+
#sessionSyncTimer;
|
|
72
|
+
startSessionSyncSchedule(cfg, onError) {
|
|
73
|
+
this.stopSessionSyncSchedule();
|
|
74
|
+
const sessions = this.#corpora.find((corpus) => corpus.kind === "sessions");
|
|
75
|
+
if (!sessions?.syncIntervalMinutes)
|
|
76
|
+
return;
|
|
77
|
+
this.#sessionSyncTimer = setInterval(() => {
|
|
78
|
+
for (const agentId of listAgentIds(cfg)) {
|
|
79
|
+
void this.startSessionSync({ cfg, agentId }).catch(onError);
|
|
80
|
+
}
|
|
81
|
+
}, sessions.syncIntervalMinutes * 60_000);
|
|
82
|
+
this.#sessionSyncTimer.unref();
|
|
83
|
+
}
|
|
84
|
+
stopSessionSyncSchedule() {
|
|
85
|
+
if (this.#sessionSyncTimer)
|
|
86
|
+
clearInterval(this.#sessionSyncTimer);
|
|
87
|
+
this.#sessionSyncTimer = undefined;
|
|
88
|
+
}
|
|
71
89
|
constructor(corpora, options = {}) {
|
|
72
90
|
this.#corpora = corpora;
|
|
73
91
|
this.#analysisExecutable = options.analysisExecutable;
|
|
@@ -186,6 +204,7 @@ export class QmdMemoryRuntime {
|
|
|
186
204
|
await (await pending)?.close();
|
|
187
205
|
}
|
|
188
206
|
async closeAllMemorySearchManagers() {
|
|
207
|
+
this.stopSessionSyncSchedule();
|
|
189
208
|
const managers = [...this.#managers.values()];
|
|
190
209
|
this.#managers.clear();
|
|
191
210
|
await Promise.all(managers.map(async (pending) => (await pending).close()));
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "unblock-memory",
|
|
3
3
|
"name": "Unblock Memory",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.12",
|
|
5
5
|
"description": "Indexes, retrieves, and analyzes configured workspace memory with existing QMD vectors.",
|
|
6
6
|
"kind": "memory",
|
|
7
|
-
"activation": { "onStartup":
|
|
7
|
+
"activation": { "onStartup": true },
|
|
8
8
|
"skills": ["./skills"],
|
|
9
9
|
"contracts": {
|
|
10
10
|
"tools": [
|
|
@@ -94,6 +94,13 @@
|
|
|
94
94
|
"properties": {
|
|
95
95
|
"name": { "const": "sessions" },
|
|
96
96
|
"kind": { "const": "sessions" },
|
|
97
|
+
"syncIntervalMinutes": {
|
|
98
|
+
"type": "integer",
|
|
99
|
+
"minimum": 0,
|
|
100
|
+
"maximum": 1440,
|
|
101
|
+
"default": 15,
|
|
102
|
+
"description": "Refresh sessions every N minutes while the Gateway runs; 0 disables automatic sync. First refresh is after one interval."
|
|
103
|
+
},
|
|
97
104
|
"chatTypes": {
|
|
98
105
|
"type": "array",
|
|
99
106
|
"minItems": 1,
|