@unblocklabs/unblock-memory 0.3.6 → 0.3.8

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.
@@ -1,7 +1,10 @@
1
1
  import { jsonResult } from "openclaw/plugin-sdk/agent-runtime";
2
+ import { resolveAgentDir } from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
2
3
  import { Type } from "typebox";
3
4
  import { Value } from "typebox/value";
4
5
  import { renderPeopleWhisper } from "./people-hooks.js";
6
+ import { nextPeopleRefinement } from "./people-refinement.js";
7
+ import { PERSON_DOSSIER_SCHEMA } from "./people-store.js";
5
8
  import { createOpenClawSlackDirectory, syncSlackDirectory, } from "./slack-directory.js";
6
9
  const nonEmpty = Type.String({ pattern: "\\S", maxLength: 1000 });
7
10
  const inspectParameters = Type.Union([
@@ -40,13 +43,30 @@ const inspectParameters = Type.Union([
40
43
  description: "Maximum actionable todos to return.",
41
44
  })),
42
45
  }, { additionalProperties: false }),
46
+ Type.Object({
47
+ view: Type.Literal("refinement_next"),
48
+ evidenceLimit: Type.Optional(Type.Integer({ minimum: 1, maximum: 50 })),
49
+ }, { additionalProperties: false }),
43
50
  ]);
44
51
  const updateParameters = Type.Union([
45
52
  Type.Object({
46
- action: Type.Literal("set_policy"),
53
+ action: Type.Literal("set_injection"),
54
+ personId: nonEmpty,
55
+ enabled: Type.Boolean(),
56
+ }, { additionalProperties: false }),
57
+ Type.Object({
58
+ action: Type.Literal("replace_dossier"),
59
+ personId: nonEmpty,
60
+ dossier: Type.Optional(PERSON_DOSSIER_SCHEMA),
61
+ consumedEvidenceLocators: Type.Optional(Type.Array(Type.String({ pattern: "^session:.+:event:\\d+$", maxLength: 1000 }), {
62
+ minItems: 1,
63
+ maxItems: 50,
64
+ uniqueItems: true,
65
+ })),
66
+ }, { additionalProperties: false }),
67
+ Type.Object({
68
+ action: Type.Literal("delete_dossier"),
47
69
  personId: nonEmpty,
48
- refinementEnabled: Type.Optional(Type.Boolean()),
49
- injectionEnabled: Type.Optional(Type.Boolean()),
50
70
  }, { additionalProperties: false }),
51
71
  Type.Object({
52
72
  action: Type.Literal("set_company"),
@@ -74,7 +94,7 @@ const syncParameters = Type.Object({
74
94
  }, { additionalProperties: false });
75
95
  function context(ctx) {
76
96
  const cfg = ctx.getRuntimeConfig?.() ?? ctx.runtimeConfig ?? ctx.config;
77
- return cfg && ctx.agentId ? { agentId: ctx.agentId } : undefined;
97
+ return cfg && ctx.agentId ? { agentId: ctx.agentId, cfg } : undefined;
78
98
  }
79
99
  function personView(stores, agentId, selector, maxChars) {
80
100
  const store = stores.get(agentId);
@@ -99,18 +119,35 @@ function personView(stores, agentId, selector, maxChars) {
99
119
  }
100
120
  function createInspectTool(stores, config, ctx) {
101
121
  const active = context(ctx);
102
- if (!active || ctx.senderIsOwner !== true)
122
+ if (!active)
103
123
  return null;
104
124
  return {
105
125
  name: "memory_people_inspect",
106
126
  label: "Inspect People Memory",
107
- description: "Inspect one person by PeopleSQL personId or exact Slack identity, or list bounded actionable people todos.",
127
+ description: "Inspect a person, list actionable people todos, or read the next person's unseen interaction evidence for dossier refinement.",
108
128
  parameters: inspectParameters,
109
129
  async execute(_toolCallId, raw) {
110
130
  const input = Value.Parse(inspectParameters, raw);
111
131
  if (input.view === "person") {
112
132
  return jsonResult(personView(stores, active.agentId, input, config.whisperer.maxChars));
113
133
  }
134
+ if (input.view === "refinement_next") {
135
+ try {
136
+ const refinement = nextPeopleRefinement({
137
+ store: stores.get(active.agentId),
138
+ agentId: active.agentId,
139
+ agentDatabasePath: `${resolveAgentDir(active.cfg, active.agentId)}/openclaw-agent.sqlite`,
140
+ evidenceLimit: input.evidenceLimit,
141
+ });
142
+ return jsonResult(refinement ? { status: "ok", refinement } : { status: "empty" });
143
+ }
144
+ catch (error) {
145
+ return jsonResult({
146
+ status: "unavailable",
147
+ error: error instanceof Error ? error.message : String(error),
148
+ });
149
+ }
150
+ }
114
151
  return jsonResult({
115
152
  status: "ok",
116
153
  todos: stores.get(active.agentId).listTodos(input.limit ?? 20),
@@ -120,27 +157,40 @@ function createInspectTool(stores, config, ctx) {
120
157
  }
121
158
  function createUpdateTool(stores, ctx) {
122
159
  const active = context(ctx);
123
- if (!active || ctx.senderIsOwner !== true)
160
+ if (!active)
124
161
  return null;
125
162
  return {
126
163
  name: "memory_people_update",
127
164
  label: "Update People Memory",
128
- description: "Apply one validated people policy, company, todo, deletion, or restoration action.",
165
+ description: "Update a dossier, one person's injection preference, company, todo, or person status.",
129
166
  parameters: updateParameters,
130
167
  async execute(_toolCallId, raw) {
131
- if (ctx.senderIsOwner !== true)
132
- return jsonResult({ status: "forbidden", error: "owner authorization required" });
133
168
  const input = Value.Parse(updateParameters, raw);
134
169
  const store = stores.get(active.agentId);
135
- if (input.action === "set_policy") {
136
- if (input.refinementEnabled === undefined && input.injectionEnabled === undefined) {
170
+ if (input.action === "set_injection") {
171
+ const person = store.setInjection(input.personId, input.enabled);
172
+ return jsonResult(person ? { status: "ok", person } : { status: "not_found" });
173
+ }
174
+ if (input.action === "replace_dossier") {
175
+ if (input.dossier === undefined && input.consumedEvidenceLocators === undefined) {
137
176
  return jsonResult({
138
177
  status: "invalid",
139
- error: "set_policy requires at least one policy value",
178
+ error: "replace_dossier requires a dossier or consumed evidence locators",
140
179
  });
141
180
  }
142
- const person = store.setPolicies(input.personId, input);
143
- return jsonResult(person ? { status: "ok", person } : { status: "not_found" });
181
+ try {
182
+ const dossier = store.replaceDossier(input.personId, input.dossier, input.consumedEvidenceLocators);
183
+ return jsonResult({ status: "ok", dossier });
184
+ }
185
+ catch (error) {
186
+ if (error instanceof Error && error.message.startsWith("person not found:")) {
187
+ return jsonResult({ status: "not_found" });
188
+ }
189
+ throw error;
190
+ }
191
+ }
192
+ if (input.action === "delete_dossier") {
193
+ return jsonResult(store.deleteDossier(input.personId) ? { status: "ok" } : { status: "not_found" });
144
194
  }
145
195
  if (input.action === "set_company") {
146
196
  const company = store.setCompany(input.personId, {
@@ -165,7 +215,7 @@ function createUpdateTool(stores, ctx) {
165
215
  }
166
216
  function createSyncTool(stores, reader, ctx) {
167
217
  const active = context(ctx);
168
- if (!active || ctx.senderIsOwner !== true)
218
+ if (!active)
169
219
  return null;
170
220
  return {
171
221
  name: "memory_people_sync",
@@ -174,8 +224,6 @@ function createSyncTool(stores, reader, ctx) {
174
224
  parameters: syncParameters,
175
225
  async execute(_toolCallId, raw) {
176
226
  const input = Value.Parse(syncParameters, raw);
177
- if (ctx.senderIsOwner !== true)
178
- return jsonResult({ status: "forbidden", error: "owner authorization required" });
179
227
  try {
180
228
  return jsonResult(await syncSlackDirectory({
181
229
  store: stores.get(active.agentId),
@@ -196,11 +244,9 @@ function createSyncTool(stores, reader, ctx) {
196
244
  export function registerPeopleTools(api, stores, config, directoryReader) {
197
245
  api.registerTool((ctx) => createInspectTool(stores, config, ctx), {
198
246
  names: ["memory_people_inspect"],
199
- optional: true,
200
247
  });
201
248
  api.registerTool((ctx) => createUpdateTool(stores, ctx), {
202
249
  names: ["memory_people_update"],
203
- optional: true,
204
250
  });
205
251
  api.registerTool((ctx) => createSyncTool(stores, directoryReader ??
206
252
  createOpenClawSlackDirectory({
@@ -2,7 +2,6 @@ import { Type } from "typebox";
2
2
  import { Value } from "typebox/value";
3
3
  import { jsonResult } from "openclaw/plugin-sdk/agent-runtime";
4
4
  import { resolveConfig } from "./config.js";
5
- import { registerPeopleCli } from "./people-cli.js";
6
5
  import { registerPeopleHooks } from "./people-hooks.js";
7
6
  import { PeopleStores } from "./people-store.js";
8
7
  import { registerPeopleTools } from "./people-tools.js";
@@ -386,7 +385,6 @@ export function resolveFlushPlan(params = {}) {
386
385
  }
387
386
  export function registerUnblockMemory(api) {
388
387
  const config = resolveConfig(api.pluginConfig);
389
- registerPeopleCli(api, config.people);
390
388
  if (api.registrationMode === "cli-metadata")
391
389
  return;
392
390
  const runtime = new QmdMemoryRuntime(config.corpora, {
@@ -1,18 +1,11 @@
1
1
  {
2
2
  "id": "unblock-memory",
3
3
  "name": "Unblock Memory",
4
- "version": "0.3.6",
4
+ "version": "0.3.8",
5
5
  "description": "Indexes, retrieves, and analyzes configured workspace memory with existing QMD vectors.",
6
6
  "kind": "memory",
7
7
  "activation": { "onStartup": false },
8
8
  "skills": ["./skills"],
9
- "cliCommands": [
10
- {
11
- "name": "unblock-memory",
12
- "description": "Unblock Memory administration",
13
- "hasSubcommands": true
14
- }
15
- ],
16
9
  "contracts": {
17
10
  "tools": [
18
11
  "memory_search",
@@ -37,8 +30,8 @@
37
30
  "memory_fetch_cluster": { "replaySafe": true },
38
31
  "memory_list_maintenance_tasks": { "replaySafe": true },
39
32
  "memory_update_maintenance_task": { "sideEffecting": true },
40
- "memory_people_inspect": { "replaySafe": true, "optional": true },
41
- "memory_people_update": { "sideEffecting": true, "optional": true },
33
+ "memory_people_inspect": { "replaySafe": true },
34
+ "memory_people_update": { "sideEffecting": true },
42
35
  "memory_people_sync": { "sideEffecting": true, "optional": true }
43
36
  },
44
37
  "uiHints": {
@@ -147,11 +140,11 @@
147
140
  "enabled": { "type": "boolean", "default": false },
148
141
  "refinement": {
149
142
  "type": "object",
143
+ "description": "Deprecated compatibility field; accepted but ignored.",
150
144
  "additionalProperties": false,
151
145
  "properties": {
152
- "maxPeoplePerRun": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10 }
153
- },
154
- "default": { "maxPeoplePerRun": 10 }
146
+ "maxPeoplePerRun": { "type": "integer", "minimum": 1, "maximum": 50 }
147
+ }
155
148
  },
156
149
  "whisperer": {
157
150
  "type": "object",
@@ -173,7 +166,6 @@
173
166
  },
174
167
  "default": {
175
168
  "enabled": false,
176
- "refinement": { "maxPeoplePerRun": 10 },
177
169
  "whisperer": { "enabled": false, "maxChars": 1200 },
178
170
  "todos": { "maxOpen": 1000 }
179
171
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unblocklabs/unblock-memory",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Workspace-native memory for OpenClaw, powered by QMD",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,4 +0,0 @@
1
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
2
- import type { UnblockMemoryConfig } from "./config.js";
3
- import { type PeopleRefinementRunner } from "./people-refinement.js";
4
- export declare function registerPeopleCli(api: OpenClawPluginApi, config: UnblockMemoryConfig["people"], runner?: PeopleRefinementRunner): void;
@@ -1,47 +0,0 @@
1
- import { join } from "node:path";
2
- import { resolveAgentDir } from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
3
- import { PeopleStores } from "./people-store.js";
4
- import { codexPeopleRefinementRunner, refinePeople, } from "./people-refinement.js";
5
- export function registerPeopleCli(api, config, runner = codexPeopleRefinementRunner) {
6
- api.registerCli(({ program, config: openClawConfig }) => {
7
- const root = program.command("unblock-memory").description("Unblock Memory administration");
8
- const people = root.command("people").description("Maintain the agent-local people store");
9
- people
10
- .command("refine")
11
- .description("Refine stale enabled people with Codex")
12
- .requiredOption("--agent <id>", "Agent id")
13
- .action(async (options) => {
14
- const agentId = options.agent.trim();
15
- if (!agentId)
16
- throw new Error("--agent must be a non-empty string");
17
- if (!config.enabled)
18
- throw new Error("PeopleSQL is disabled");
19
- const stores = new PeopleStores({
20
- maxOpenTodos: config.todos.maxOpen,
21
- maxBlurbChars: config.whisperer.maxChars,
22
- });
23
- try {
24
- const summary = await refinePeople({
25
- store: stores.get(agentId),
26
- agentId,
27
- agentDatabasePath: join(resolveAgentDir(openClawConfig, agentId), "openclaw-agent.sqlite"),
28
- candidateLimit: config.refinement.maxPeoplePerRun,
29
- maxBlurbChars: config.whisperer.maxChars,
30
- runner,
31
- });
32
- process.stdout.write(`${JSON.stringify(summary)}\n`);
33
- }
34
- finally {
35
- stores.closeAll();
36
- }
37
- });
38
- }, {
39
- descriptors: [
40
- {
41
- name: "unblock-memory",
42
- description: "Unblock Memory administration",
43
- hasSubcommands: true,
44
- },
45
- ],
46
- });
47
- }