@hyperspell/openclaw-hyperspell 0.1.1 → 0.3.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 +33 -15
- package/client.ts +102 -2
- package/commands/setup.ts +536 -0
- package/commands/slash.ts +34 -54
- package/config.ts +75 -3
- package/hooks/memory-sync.ts +63 -0
- package/index.ts +62 -3
- package/openclaw.plugin.json +25 -10
- package/package.json +8 -5
- package/tools/remember.ts +20 -8
- package/tools/search.ts +51 -31
- package/types/openclaw.d.ts +18 -0
package/README.md
CHANGED
|
@@ -10,9 +10,23 @@ OpenClaw plugin for [Hyperspell](https://hyperspell.com) - Context and memory fo
|
|
|
10
10
|
openclaw plugins install @hyperspell/openclaw-hyperspell
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Quick Start
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Run the interactive setup wizard:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
openclaw openclaw-hyperspell setup
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The setup wizard will guide you through:
|
|
22
|
+
1. Creating a Hyperspell account (if you don't have one)
|
|
23
|
+
2. Configuring your API key
|
|
24
|
+
3. Setting up your User ID for multi-tenant memory
|
|
25
|
+
4. Connecting your apps (Notion, Slack, Google Drive, etc.)
|
|
26
|
+
|
|
27
|
+
## Manual Configuration
|
|
28
|
+
|
|
29
|
+
Alternatively, add to your `openclaw.json`:
|
|
16
30
|
|
|
17
31
|
```json
|
|
18
32
|
{
|
|
@@ -22,6 +36,7 @@ Add to your `openclaw.json`:
|
|
|
22
36
|
"enabled": true,
|
|
23
37
|
"config": {
|
|
24
38
|
"apiKey": "${HYPERSPELL_API_KEY}",
|
|
39
|
+
"userId": "your-email",
|
|
25
40
|
"autoContext": true
|
|
26
41
|
}
|
|
27
42
|
}
|
|
@@ -37,12 +52,26 @@ Or set the environment variable:
|
|
|
37
52
|
export HYPERSPELL_API_KEY=hs_...
|
|
38
53
|
```
|
|
39
54
|
|
|
55
|
+
## CLI Commands
|
|
56
|
+
|
|
57
|
+
### `openclaw openclaw-hyperspell setup`
|
|
58
|
+
|
|
59
|
+
Interactive setup wizard that walks you through configuration.
|
|
60
|
+
|
|
61
|
+
### `openclaw openclaw-hyperspell status`
|
|
62
|
+
|
|
63
|
+
Check your current configuration and connection status.
|
|
64
|
+
|
|
65
|
+
### `openclaw openclaw-hyperspell connect`
|
|
66
|
+
|
|
67
|
+
Open the Hyperspell connect page to link your accounts (Notion, Slack, Google Drive, etc.)
|
|
68
|
+
|
|
40
69
|
### Options
|
|
41
70
|
|
|
42
71
|
| Option | Type | Default | Description |
|
|
43
72
|
|--------|------|---------|-------------|
|
|
44
73
|
| `apiKey` | string | `${HYPERSPELL_API_KEY}` | Hyperspell API key |
|
|
45
|
-
| `userId` | string | - | User ID
|
|
74
|
+
| `userId` | string | - | User ID (can be your email) |
|
|
46
75
|
| `autoContext` | boolean | `true` | Auto-inject relevant memories before each AI turn |
|
|
47
76
|
| `sources` | string | - | Comma-separated sources to search (e.g., `notion,slack`) |
|
|
48
77
|
| `maxResults` | number | `10` | Maximum memories per context injection |
|
|
@@ -58,16 +87,6 @@ Search your memories for relevant context.
|
|
|
58
87
|
/getcontext Q1 budget planning
|
|
59
88
|
```
|
|
60
89
|
|
|
61
|
-
### `/connect <source>`
|
|
62
|
-
|
|
63
|
-
Connect an account to Hyperspell. Opens the OAuth flow in your browser.
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
/connect notion
|
|
67
|
-
/connect slack
|
|
68
|
-
/connect google_drive
|
|
69
|
-
```
|
|
70
|
-
|
|
71
90
|
### `/remember <text>`
|
|
72
91
|
|
|
73
92
|
Save something to memory.
|
|
@@ -95,12 +114,11 @@ This ensures the AI always has access to relevant information from your connecte
|
|
|
95
114
|
|
|
96
115
|
## Available Sources
|
|
97
116
|
|
|
98
|
-
- `
|
|
117
|
+
- `vault` - User-created memories
|
|
99
118
|
- `notion` - Notion pages and databases
|
|
100
119
|
- `slack` - Slack messages
|
|
101
120
|
- `google_calendar` - Google Calendar events
|
|
102
121
|
- `google_mail` - Gmail messages
|
|
103
122
|
- `google_drive` - Google Drive files
|
|
104
123
|
- `box` - Box files
|
|
105
|
-
- `vault` - Vault documents
|
|
106
124
|
- `web_crawler` - Crawled web pages
|
package/client.ts
CHANGED
|
@@ -11,6 +11,11 @@ export type SearchResult = {
|
|
|
11
11
|
createdAt: string | null
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export type SearchWithAnswerResult = {
|
|
15
|
+
answer: string | null
|
|
16
|
+
documents: SearchResult[]
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
export type Integration = {
|
|
15
20
|
id: string
|
|
16
21
|
name: string
|
|
@@ -18,6 +23,13 @@ export type Integration = {
|
|
|
18
23
|
icon: string
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
export type Connection = {
|
|
27
|
+
id: string
|
|
28
|
+
integrationId: string
|
|
29
|
+
label: string | null
|
|
30
|
+
provider: HyperspellSource
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
export class HyperspellClient {
|
|
22
34
|
private client: Hyperspell
|
|
23
35
|
private config: HyperspellConfig
|
|
@@ -26,8 +38,9 @@ export class HyperspellClient {
|
|
|
26
38
|
this.config = config
|
|
27
39
|
this.client = new Hyperspell({
|
|
28
40
|
apiKey: config.apiKey,
|
|
41
|
+
userID: config.userId,
|
|
29
42
|
})
|
|
30
|
-
log.info(
|
|
43
|
+
log.info(`client initialized${config.userId ? ` for user ${config.userId}` : ""}`)
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
async search(
|
|
@@ -61,18 +74,89 @@ export class HyperspellClient {
|
|
|
61
74
|
return results
|
|
62
75
|
}
|
|
63
76
|
|
|
77
|
+
async searchRaw(
|
|
78
|
+
query: string,
|
|
79
|
+
options?: { limit?: number; sources?: HyperspellSource[] },
|
|
80
|
+
): Promise<Record<string, unknown>> {
|
|
81
|
+
const limit = options?.limit ?? this.config.maxResults
|
|
82
|
+
const sources =
|
|
83
|
+
options?.sources ?? (this.config.sources.length > 0 ? this.config.sources : undefined)
|
|
84
|
+
|
|
85
|
+
log.debugRequest("memories.search (raw)", { query, limit, sources })
|
|
86
|
+
|
|
87
|
+
const response = await this.client.memories.search({
|
|
88
|
+
query,
|
|
89
|
+
sources,
|
|
90
|
+
options: {
|
|
91
|
+
max_results: limit,
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
log.debugResponse("memories.search (raw)", { count: response.documents.length })
|
|
96
|
+
|
|
97
|
+
return response as unknown as Record<string, unknown>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async searchWithAnswer(
|
|
101
|
+
query: string,
|
|
102
|
+
options?: { limit?: number; sources?: HyperspellSource[] },
|
|
103
|
+
): Promise<SearchWithAnswerResult> {
|
|
104
|
+
const limit = options?.limit ?? this.config.maxResults
|
|
105
|
+
const sources =
|
|
106
|
+
options?.sources ?? (this.config.sources.length > 0 ? this.config.sources : undefined)
|
|
107
|
+
|
|
108
|
+
log.debugRequest("memories.search (with answer)", { query, limit, sources })
|
|
109
|
+
|
|
110
|
+
const response = await this.client.memories.search({
|
|
111
|
+
query,
|
|
112
|
+
sources,
|
|
113
|
+
answer: true,
|
|
114
|
+
options: {
|
|
115
|
+
max_results: limit,
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const documents: SearchResult[] = response.documents.map((doc) => ({
|
|
120
|
+
resourceId: doc.resource_id,
|
|
121
|
+
title: doc.title ?? null,
|
|
122
|
+
source: doc.source as HyperspellSource,
|
|
123
|
+
score: doc.score ?? null,
|
|
124
|
+
url: doc.metadata?.url as string | null ?? null,
|
|
125
|
+
createdAt: doc.metadata?.created_at as string | null ?? null,
|
|
126
|
+
}))
|
|
127
|
+
|
|
128
|
+
log.debugResponse("memories.search (with answer)", {
|
|
129
|
+
count: documents.length,
|
|
130
|
+
hasAnswer: !!response.answer,
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
answer: response.answer ?? null,
|
|
135
|
+
documents,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
64
139
|
async addMemory(
|
|
65
140
|
text: string,
|
|
66
|
-
options?: {
|
|
141
|
+
options?: {
|
|
142
|
+
title?: string
|
|
143
|
+
resourceId?: string
|
|
144
|
+
collection?: string
|
|
145
|
+
metadata?: Record<string, string | number | boolean>
|
|
146
|
+
},
|
|
67
147
|
): Promise<{ resourceId: string }> {
|
|
68
148
|
log.debugRequest("memories.add", {
|
|
69
149
|
textLength: text.length,
|
|
70
150
|
title: options?.title,
|
|
151
|
+
resourceId: options?.resourceId,
|
|
152
|
+
collection: options?.collection,
|
|
71
153
|
})
|
|
72
154
|
|
|
73
155
|
const result = await this.client.memories.add({
|
|
74
156
|
text,
|
|
75
157
|
title: options?.title,
|
|
158
|
+
resource_id: options?.resourceId,
|
|
159
|
+
collection: options?.collection,
|
|
76
160
|
metadata: {
|
|
77
161
|
...options?.metadata,
|
|
78
162
|
openclaw_source: "command",
|
|
@@ -110,4 +194,20 @@ export class HyperspellClient {
|
|
|
110
194
|
expiresAt: response.expires_at,
|
|
111
195
|
}
|
|
112
196
|
}
|
|
197
|
+
|
|
198
|
+
async listConnections(): Promise<Connection[]> {
|
|
199
|
+
log.debugRequest("connections.list", {})
|
|
200
|
+
|
|
201
|
+
const response = await this.client.connections.list()
|
|
202
|
+
|
|
203
|
+
const connections: Connection[] = response.connections.map((conn) => ({
|
|
204
|
+
id: conn.id,
|
|
205
|
+
integrationId: conn.integration_id,
|
|
206
|
+
label: conn.label,
|
|
207
|
+
provider: conn.provider as HyperspellSource,
|
|
208
|
+
}))
|
|
209
|
+
|
|
210
|
+
log.debugResponse("connections.list", { count: connections.length })
|
|
211
|
+
return connections
|
|
212
|
+
}
|
|
113
213
|
}
|