@unblocklabs/unblock-memory 0.3.4 → 0.3.6
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/dist/src/people-tools.js +35 -13
- package/dist/src/slack-directory.d.ts +17 -7
- package/dist/src/slack-directory.js +72 -44
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
package/dist/src/people-tools.js
CHANGED
|
@@ -2,24 +2,43 @@ import { jsonResult } from "openclaw/plugin-sdk/agent-runtime";
|
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { Value } from "typebox/value";
|
|
4
4
|
import { renderPeopleWhisper } from "./people-hooks.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createOpenClawSlackDirectory, syncSlackDirectory, } from "./slack-directory.js";
|
|
6
6
|
const nonEmpty = Type.String({ pattern: "\\S", maxLength: 1000 });
|
|
7
|
-
const
|
|
8
|
-
Type.Object({
|
|
7
|
+
const inspectParameters = Type.Union([
|
|
8
|
+
Type.Object({
|
|
9
|
+
view: Type.Literal("person"),
|
|
10
|
+
personId: Type.String({
|
|
11
|
+
pattern: "\\S",
|
|
12
|
+
maxLength: 1000,
|
|
13
|
+
description: "PeopleSQL person ID. Use identity when only Slack IDs are known.",
|
|
14
|
+
}),
|
|
15
|
+
}, { additionalProperties: false }),
|
|
9
16
|
Type.Object({
|
|
10
17
|
view: Type.Literal("person"),
|
|
11
18
|
identity: Type.Object({
|
|
12
19
|
provider: Type.Literal("slack"),
|
|
13
|
-
accountScope:
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
accountScope: Type.String({
|
|
21
|
+
pattern: "\\S",
|
|
22
|
+
maxLength: 1000,
|
|
23
|
+
description: "Configured Slack account ID, for example default.",
|
|
24
|
+
}),
|
|
25
|
+
externalId: Type.String({
|
|
26
|
+
pattern: "\\S",
|
|
27
|
+
maxLength: 1000,
|
|
28
|
+
description: "Exact Slack user ID.",
|
|
29
|
+
}),
|
|
30
|
+
}, {
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
description: "Exact Slack identity for the person to inspect.",
|
|
33
|
+
}),
|
|
16
34
|
}, { additionalProperties: false }),
|
|
17
|
-
]);
|
|
18
|
-
const inspectParameters = Type.Union([
|
|
19
|
-
personSelector,
|
|
20
35
|
Type.Object({
|
|
21
36
|
view: Type.Literal("todos"),
|
|
22
|
-
limit: Type.Optional(Type.Integer({
|
|
37
|
+
limit: Type.Optional(Type.Integer({
|
|
38
|
+
minimum: 1,
|
|
39
|
+
maximum: 100,
|
|
40
|
+
description: "Maximum actionable todos to return.",
|
|
41
|
+
})),
|
|
23
42
|
}, { additionalProperties: false }),
|
|
24
43
|
]);
|
|
25
44
|
const updateParameters = Type.Union([
|
|
@@ -85,7 +104,7 @@ function createInspectTool(stores, config, ctx) {
|
|
|
85
104
|
return {
|
|
86
105
|
name: "memory_people_inspect",
|
|
87
106
|
label: "Inspect People Memory",
|
|
88
|
-
description: "Inspect one exact
|
|
107
|
+
description: "Inspect one person by PeopleSQL personId or exact Slack identity, or list bounded actionable people todos.",
|
|
89
108
|
parameters: inspectParameters,
|
|
90
109
|
async execute(_toolCallId, raw) {
|
|
91
110
|
const input = Value.Parse(inspectParameters, raw);
|
|
@@ -174,7 +193,7 @@ function createSyncTool(stores, reader, ctx) {
|
|
|
174
193
|
},
|
|
175
194
|
};
|
|
176
195
|
}
|
|
177
|
-
export function registerPeopleTools(api, stores, config, directoryReader
|
|
196
|
+
export function registerPeopleTools(api, stores, config, directoryReader) {
|
|
178
197
|
api.registerTool((ctx) => createInspectTool(stores, config, ctx), {
|
|
179
198
|
names: ["memory_people_inspect"],
|
|
180
199
|
optional: true,
|
|
@@ -183,7 +202,10 @@ export function registerPeopleTools(api, stores, config, directoryReader = openC
|
|
|
183
202
|
names: ["memory_people_update"],
|
|
184
203
|
optional: true,
|
|
185
204
|
});
|
|
186
|
-
api.registerTool((ctx) => createSyncTool(stores, directoryReader
|
|
205
|
+
api.registerTool((ctx) => createSyncTool(stores, directoryReader ??
|
|
206
|
+
createOpenClawSlackDirectory({
|
|
207
|
+
getConfig: () => ctx.getRuntimeConfig?.() ?? ctx.runtimeConfig ?? ctx.config,
|
|
208
|
+
}), ctx), {
|
|
187
209
|
names: ["memory_people_sync"],
|
|
188
210
|
optional: true,
|
|
189
211
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/plugin-entry";
|
|
1
2
|
import type { PeopleStore } from "./people-store.js";
|
|
2
3
|
type SlackDirectoryEntry = {
|
|
3
4
|
id: string;
|
|
@@ -11,13 +12,22 @@ export type SlackDirectoryReader = {
|
|
|
11
12
|
limit: number;
|
|
12
13
|
}): Promise<readonly SlackDirectoryEntry[]>;
|
|
13
14
|
};
|
|
14
|
-
type
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}>;
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
type SlackAccountInspector = (params: {
|
|
16
|
+
channelId: "slack";
|
|
17
|
+
cfg: OpenClawConfig;
|
|
18
|
+
accountId: string;
|
|
19
|
+
}) => Promise<Record<string, unknown> | null>;
|
|
20
|
+
type SlackRequest = (input: string | URL, init: {
|
|
21
|
+
headers: {
|
|
22
|
+
authorization: string;
|
|
23
|
+
};
|
|
24
|
+
signal: AbortSignal;
|
|
25
|
+
}) => Promise<Pick<Response, "json" | "ok" | "status">>;
|
|
26
|
+
export declare function createOpenClawSlackDirectory(params: {
|
|
27
|
+
getConfig: () => OpenClawConfig | undefined;
|
|
28
|
+
inspectAccount?: SlackAccountInspector;
|
|
29
|
+
request?: SlackRequest;
|
|
30
|
+
}): SlackDirectoryReader;
|
|
21
31
|
export declare function syncSlackDirectory(params: {
|
|
22
32
|
store: PeopleStore;
|
|
23
33
|
reader: SlackDirectoryReader;
|
|
@@ -1,55 +1,83 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
const execFileAsync = promisify(execFile);
|
|
1
|
+
import { inspectReadOnlyChannelAccount } from "openclaw/plugin-sdk/directory-runtime";
|
|
4
2
|
function text(value, maxLength) {
|
|
5
3
|
return typeof value === "string" && value.trim() ? value.trim().slice(0, maxLength) : undefined;
|
|
6
4
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
function record(value) {
|
|
6
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
7
|
+
? value
|
|
8
|
+
: undefined;
|
|
9
|
+
}
|
|
10
|
+
function slackToken(account) {
|
|
11
|
+
return text(account.userToken, 10_000) ?? text(account.botToken, 10_000);
|
|
12
|
+
}
|
|
13
|
+
function slackEntry(value) {
|
|
14
|
+
const member = record(value);
|
|
15
|
+
const id = text(member?.id, 200);
|
|
16
|
+
if (!id)
|
|
17
|
+
return undefined;
|
|
18
|
+
const profile = record(member?.profile);
|
|
19
|
+
return {
|
|
20
|
+
id,
|
|
21
|
+
name: text(profile?.display_name, 500) ??
|
|
22
|
+
text(profile?.real_name, 500) ??
|
|
23
|
+
text(member?.real_name, 500) ??
|
|
24
|
+
text(member?.name, 500),
|
|
25
|
+
handle: text(member?.name, 200),
|
|
26
|
+
avatarUrl: text(profile?.image_512, 2_000) ??
|
|
27
|
+
text(profile?.image_192, 2_000) ??
|
|
28
|
+
text(profile?.image_72, 2_000),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function createOpenClawSlackDirectory(params) {
|
|
32
|
+
const inspectAccount = params.inspectAccount ?? inspectReadOnlyChannelAccount;
|
|
33
|
+
const request = params.request ?? fetch;
|
|
11
34
|
return {
|
|
12
35
|
async listUsers({ accountId, limit }) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
const cfg = params.getConfig();
|
|
37
|
+
if (!cfg)
|
|
38
|
+
throw new Error("OpenClaw runtime config is unavailable");
|
|
39
|
+
const account = await inspectAccount({ channelId: "slack", cfg, accountId });
|
|
40
|
+
const token = account ? slackToken(account) : undefined;
|
|
41
|
+
if (!token) {
|
|
42
|
+
throw new Error(`Slack credentials for account "${accountId}" are unavailable in the active runtime snapshot`);
|
|
43
|
+
}
|
|
44
|
+
const entries = [];
|
|
45
|
+
const cursors = new Set();
|
|
46
|
+
let cursor;
|
|
47
|
+
while (entries.length < limit) {
|
|
48
|
+
const url = new URL("https://slack.com/api/users.list");
|
|
49
|
+
url.searchParams.set("limit", String(Math.min(limit, 200)));
|
|
50
|
+
if (cursor)
|
|
51
|
+
url.searchParams.set("cursor", cursor);
|
|
52
|
+
const response = await request(url, {
|
|
53
|
+
headers: { authorization: `Bearer ${token}` },
|
|
54
|
+
signal: AbortSignal.timeout(30_000),
|
|
55
|
+
});
|
|
56
|
+
const payload = record(await response.json());
|
|
57
|
+
if (!response.ok || payload?.ok !== true) {
|
|
58
|
+
const detail = text(payload?.error, 200) ?? `HTTP ${response.status}`;
|
|
59
|
+
throw new Error(`Slack directory request failed: ${detail}`);
|
|
60
|
+
}
|
|
61
|
+
const members = Array.isArray(payload.members) ? payload.members : [];
|
|
62
|
+
for (const member of members) {
|
|
63
|
+
const entry = slackEntry(member);
|
|
64
|
+
if (entry)
|
|
65
|
+
entries.push(entry);
|
|
66
|
+
if (entries.length === limit)
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const next = text(record(payload.response_metadata)?.next_cursor, 2_000);
|
|
70
|
+
if (!next)
|
|
71
|
+
break;
|
|
72
|
+
if (cursors.has(next))
|
|
73
|
+
throw new Error("Slack directory returned a repeated cursor");
|
|
74
|
+
cursors.add(next);
|
|
75
|
+
cursor = next;
|
|
76
|
+
}
|
|
77
|
+
return entries;
|
|
49
78
|
},
|
|
50
79
|
};
|
|
51
80
|
}
|
|
52
|
-
export const openClawSlackDirectory = createOpenClawSlackDirectory();
|
|
53
81
|
export async function syncSlackDirectory(params) {
|
|
54
82
|
const entries = await params.reader.listUsers({
|
|
55
83
|
accountId: params.accountId,
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "unblock-memory",
|
|
3
3
|
"name": "Unblock Memory",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.6",
|
|
5
5
|
"description": "Indexes, retrieves, and analyzes configured workspace memory with existing QMD vectors.",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"activation": { "onStartup": false },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unblocklabs/unblock-memory",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "Workspace-native memory for OpenClaw, powered by QMD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"typebox": "1.3.6"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
+
"@openclaw/ai": "2026.8.1-beta.3",
|
|
44
45
|
"@openclaw/plugin-inspector": "^0.3.10",
|
|
45
46
|
"@types/node": "^24.6.0",
|
|
46
47
|
"@types/picomatch": "^4.0.2",
|