@hyperspell/openclaw-hyperspell 0.17.1 → 0.19.0
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 +46 -0
- package/dist/commands/preview.js +93 -0
- package/dist/commands/purge-channel.js +54 -0
- package/dist/commands/setup.js +51 -0
- package/dist/commands/slash.js +18 -0
- package/dist/config.js +10 -0
- package/dist/hooks/auto-context.js +88 -6
- package/dist/hooks/auto-trace.js +30 -2
- package/dist/hooks/emotional-state.js +101 -15
- package/dist/hooks/hot-buffer.js +122 -14
- package/dist/hooks/mood-weather.js +113 -0
- package/dist/hooks/startup-orientation.js +89 -54
- package/dist/index.js +42 -5
- package/dist/lib/eval-matchers.js +49 -0
- package/dist/lib/exclude-channels.js +70 -0
- package/dist/lib/filters.js +6 -4
- package/dist/lib/ranking.js +42 -13
- package/dist/lib/sender.js +18 -6
- package/dist/lib/speaker-tracker.js +56 -0
- package/dist/tools/emotional-arc.js +60 -0
- package/dist/tools/remember.js +27 -1
- package/dist/tools/search.js +15 -1
- package/openclaw.plugin.json +16 -1
- package/package.json +2 -2
package/dist/tools/remember.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { channelIdFromCtx } from "../lib/exclude-channels.js";
|
|
2
3
|
import { getDefaultWriteScope, resolveRole, resolveUser, routeWrite, } from "../lib/sender.js";
|
|
4
|
+
import { resolveCurrentSessionId } from "../lib/session.js";
|
|
5
|
+
import { isMultiSpeaker } from "../lib/speaker-tracker.js";
|
|
3
6
|
import { log } from "../logger.js";
|
|
4
7
|
export function createRememberToolFactory(client, cfg) {
|
|
5
8
|
const scopingEnabled = !!cfg.multiUser?.scoping;
|
|
@@ -21,6 +24,22 @@ export function createRememberToolFactory(client, cfg) {
|
|
|
21
24
|
scope: Type.Optional(Type.String({ description: scopeDescription })),
|
|
22
25
|
}),
|
|
23
26
|
async execute(_toolCallId, params) {
|
|
27
|
+
// Decline silently-wrong writes in multi-speaker sessions with no multiUser
|
|
28
|
+
// config. Without per-sender routing the memory would land under cfg.userId
|
|
29
|
+
// regardless of who asked — contaminating the primary user's store with
|
|
30
|
+
// another person's data (attribution gap 1, issue #59 follow-up).
|
|
31
|
+
const sessionId = resolveCurrentSessionId(undefined, ctx);
|
|
32
|
+
if (isMultiSpeaker(sessionId, ctx?.is_group_chat === true) && !cfg.multiUser) {
|
|
33
|
+
log.warn("remember tool: declining write — multi-speaker session with no multiUser config; cannot attribute memory to current speaker");
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
text: "I can't store this memory right now — this is a multi-speaker session and without per-user memory configuration I have no way to attribute it to the right person. The memory would land in the primary user's store regardless of who asked. To fix this, add a `multiUser` config block to the plugin settings.",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
24
43
|
const resolved = resolveUser(ctx, cfg);
|
|
25
44
|
// Scope resolution: explicit param > role default > global default > "private"
|
|
26
45
|
const scope = params.scope ??
|
|
@@ -67,12 +86,19 @@ export function createRememberToolFactory(client, cfg) {
|
|
|
67
86
|
userId = resolved?.userId;
|
|
68
87
|
}
|
|
69
88
|
log.debug(`remember tool: "${params.text.slice(0, 50)}..." date=${params.date ?? "now"} userId=${userId} scope=${scope}`);
|
|
89
|
+
// Tag with the originating conversation so purge-channel can find this
|
|
90
|
+
// memory if the channel is quarantined later (the tool is already
|
|
91
|
+
// suppressed in channels that are quarantined NOW).
|
|
92
|
+
const channelId = channelIdFromCtx(ctx);
|
|
70
93
|
try {
|
|
71
94
|
await client.addMemory(params.text, {
|
|
72
95
|
title: params.title,
|
|
73
96
|
date: params.date,
|
|
74
97
|
collection,
|
|
75
|
-
metadata: {
|
|
98
|
+
metadata: {
|
|
99
|
+
source: "openclaw_tool",
|
|
100
|
+
...(channelId ? { openclaw_channel_id: channelId } : {}),
|
|
101
|
+
},
|
|
76
102
|
userId,
|
|
77
103
|
scope: scopingEnabled ? scope : undefined,
|
|
78
104
|
});
|
package/dist/tools/search.js
CHANGED
|
@@ -2,6 +2,8 @@ import { Type } from "@sinclair/typebox";
|
|
|
2
2
|
import { mergeWithExclude } from "../lib/filters.js";
|
|
3
3
|
import { classifySearchError, logSearchError, searchErrorToolText, } from "../lib/search-error.js";
|
|
4
4
|
import { buildScopeFilter, getCanReadScopes, resolveUser } from "../lib/sender.js";
|
|
5
|
+
import { resolveCurrentSessionId } from "../lib/session.js";
|
|
6
|
+
import { isMultiSpeaker } from "../lib/speaker-tracker.js";
|
|
5
7
|
import { log } from "../logger.js";
|
|
6
8
|
export function createSearchToolFactory(client, cfg) {
|
|
7
9
|
const scopingEnabled = !!cfg.multiUser?.scoping;
|
|
@@ -27,6 +29,15 @@ export function createSearchToolFactory(client, cfg) {
|
|
|
27
29
|
const limit = params.limit ?? 5;
|
|
28
30
|
const resolved = resolveUser(ctx, cfg);
|
|
29
31
|
const userId = params.userId ?? resolved?.userId;
|
|
32
|
+
// Warn when searching in a multi-speaker session with no multiUser config:
|
|
33
|
+
// results reflect the primary user's full store, not the current speaker's
|
|
34
|
+
// personal space. The search proceeds but the result carries the caveat so
|
|
35
|
+
// the agent can qualify its answer (attribution gap 1, issue #59 follow-up).
|
|
36
|
+
const sessionId = resolveCurrentSessionId(undefined, ctx);
|
|
37
|
+
const multiSpeakerNoConfig = isMultiSpeaker(sessionId, ctx?.is_group_chat === true) && !cfg.multiUser;
|
|
38
|
+
if (multiSpeakerNoConfig) {
|
|
39
|
+
log.warn("search tool: multi-speaker session with no multiUser config — results reflect primary user's full store, not current speaker's personal space");
|
|
40
|
+
}
|
|
30
41
|
// Build scope filter: intersect requested scope (if any) with caller's canRead
|
|
31
42
|
let filter;
|
|
32
43
|
if (scopingEnabled) {
|
|
@@ -70,7 +81,10 @@ export function createSearchToolFactory(client, cfg) {
|
|
|
70
81
|
return `${i + 1}. Source: ${doc.source}\n Title: ${title}\n Summary: ${summary}\n Relevance: ${relevance}`;
|
|
71
82
|
})
|
|
72
83
|
.join("\n\n");
|
|
73
|
-
const
|
|
84
|
+
const caveat = multiSpeakerNoConfig
|
|
85
|
+
? "\n\n⚠️ Note: This is a multi-speaker session without per-user memory config. These results reflect the primary user's store, not the current speaker's personal space. Use these results with that limitation in mind."
|
|
86
|
+
: "";
|
|
87
|
+
const text = `Found ${documents.length} memories:\n\n${formattedDocs}${caveat}`;
|
|
74
88
|
return {
|
|
75
89
|
content: [{ type: "text", text }],
|
|
76
90
|
details: { count: documents.length, documents },
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"id": "openclaw-hyperspell",
|
|
3
3
|
"name": "Hyperspell",
|
|
4
4
|
"description": "Context and memory for your AI agents — search across Notion, Slack, Google Drive, and more",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.19.0",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"contracts": {
|
|
8
8
|
"tools": [
|
|
9
9
|
"hyperspell_search",
|
|
10
10
|
"hyperspell_remember",
|
|
11
|
+
"hyperspell_emotional_arc",
|
|
11
12
|
"hyperspell_network_scan",
|
|
12
13
|
"hyperspell_network_write",
|
|
13
14
|
"hyperspell_network_complete"
|
|
@@ -46,6 +47,11 @@
|
|
|
46
47
|
"help": "Maintain emotional continuity across sessions — remembers not just what happened, but how it felt",
|
|
47
48
|
"advanced": true
|
|
48
49
|
},
|
|
50
|
+
"excludeChannels": {
|
|
51
|
+
"label": "Excluded Channels",
|
|
52
|
+
"help": "Conversation/channel ids fully quarantined from memory: no context injection, no memory writes, no memory tools in those sessions. Threads inside an excluded channel inherit the quarantine. Forward-only; already-synced content is not removed (see purge-channel CLI command).",
|
|
53
|
+
"advanced": true
|
|
54
|
+
},
|
|
49
55
|
"startupOrientation": {
|
|
50
56
|
"label": "Startup Orientation",
|
|
51
57
|
"help": "Inject recent-interactions and unfinished-loops blocks once per session at startup. Costs two extra calls + ~500–800 tokens of injection per session; off by default.",
|
|
@@ -133,6 +139,15 @@
|
|
|
133
139
|
"emotionalContext": {
|
|
134
140
|
"type": "boolean"
|
|
135
141
|
},
|
|
142
|
+
"moodWeatherChance": {
|
|
143
|
+
"type": "number",
|
|
144
|
+
"minimum": 0,
|
|
145
|
+
"maximum": 1
|
|
146
|
+
},
|
|
147
|
+
"excludeChannels": {
|
|
148
|
+
"type": "array",
|
|
149
|
+
"items": { "type": "string" }
|
|
150
|
+
},
|
|
136
151
|
"relationshipId": {
|
|
137
152
|
"type": "string"
|
|
138
153
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperspell/openclaw-hyperspell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw Hyperspell memory plugin",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"check-types": "tsc --noEmit",
|
|
39
39
|
"lint": "bunx @biomejs/biome ci .",
|
|
40
40
|
"lint:fix": "bunx @biomejs/biome check --write .",
|
|
41
|
-
"test": "node --test --experimental-strip-types client.test.ts lib/sender.test.ts lib/filters.test.ts lib/search-error.test.ts lib/session.test.ts lib/ranking.test.ts tools/search.test.ts config.test.ts sync/markdown.test.ts hooks/auto-trace.test.ts hooks/auto-context.test.ts hooks/hot-buffer.test.ts hooks/emotional-state.test.ts hooks/startup-orientation.test.ts"
|
|
41
|
+
"test": "node --test --experimental-strip-types client.test.ts lib/sender.test.ts lib/filters.test.ts lib/search-error.test.ts lib/session.test.ts lib/exclude-channels.test.ts lib/ranking.test.ts lib/eval-matchers.test.ts tools/search.test.ts tools/emotional-arc.test.ts commands/preview.test.ts commands/purge-channel.test.ts config.test.ts sync/markdown.test.ts hooks/auto-trace.test.ts hooks/auto-context.test.ts hooks/hot-buffer.test.ts hooks/emotional-state.test.ts hooks/startup-orientation.test.ts hooks/mood-weather.test.ts eval/eval.test.ts"
|
|
42
42
|
},
|
|
43
43
|
"openclaw": {
|
|
44
44
|
"extensions": [
|