@hyperspell/openclaw-hyperspell 0.4.2 → 0.7.1
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 +127 -3
- package/client.ts +311 -202
- package/commands/setup.ts +190 -55
- package/commands/wine.ts +164 -0
- package/config.ts +244 -162
- package/graph/cron.ts +364 -0
- package/graph/index.ts +5 -0
- package/graph/ops.ts +253 -0
- package/graph/state.ts +79 -0
- package/graph/tools.ts +117 -0
- package/hooks/auto-context.ts +17 -11
- package/hooks/auto-trace.ts +127 -0
- package/index.ts +123 -95
- package/lib/browser.ts +10 -6
- package/lib/run-script.ts +32 -0
- package/openclaw.plugin.json +110 -74
- package/package.json +6 -2
- package/sommeliagent/references/cross-domain-mappings.md +63 -0
- package/sommeliagent/scripts/auth.py +222 -0
- package/sommeliagent/scripts/history.py +72 -0
- package/sommeliagent/scripts/rate.py +76 -0
- package/sommeliagent/scripts/recommend.py +777 -0
- package/sommeliagent/scripts/test_recommend.py +459 -0
- package/sommeliagent/scripts/wine_db.py +3224 -0
- package/tools/remember.ts +6 -2
- package/tools/search.ts +9 -3
- package/tools/sommelier.ts +254 -0
package/README.md
CHANGED
|
@@ -149,11 +149,135 @@ This ensures the AI always has access to relevant information from your connecte
|
|
|
149
149
|
|
|
150
150
|
## Available Sources
|
|
151
151
|
|
|
152
|
+
### Documents & Storage
|
|
152
153
|
- `vault` - User-created or synced memories
|
|
153
154
|
- `notion` - Notion pages and databases
|
|
154
|
-
- `slack` - Slack messages
|
|
155
|
-
- `google_calendar` - Google Calendar events
|
|
156
|
-
- `google_mail` - Gmail messages
|
|
157
155
|
- `google_drive` - Google Drive files
|
|
158
156
|
- `box` - Box files
|
|
157
|
+
- `dropbox` - Dropbox files
|
|
158
|
+
- `onedrive` - Microsoft OneDrive files
|
|
159
|
+
|
|
160
|
+
### Communication
|
|
161
|
+
- `slack` - Slack messages
|
|
162
|
+
- `google_mail` - Gmail messages
|
|
163
|
+
|
|
164
|
+
### Calendars & Meetings
|
|
165
|
+
- `google_calendar` - Google Calendar events
|
|
166
|
+
- `zoom` - Zoom meeting recordings and transcripts
|
|
167
|
+
- `fathom` - Fathom meeting recordings
|
|
168
|
+
- `fireflies` - Fireflies.ai meeting transcripts
|
|
169
|
+
|
|
170
|
+
### Project Management
|
|
171
|
+
- `linear` - Linear issues and comments
|
|
172
|
+
|
|
173
|
+
### CRM
|
|
174
|
+
- `hubspot` - HubSpot contacts, companies, and deals
|
|
175
|
+
- `attio` - Attio CRM contacts, companies, and deals
|
|
176
|
+
|
|
177
|
+
### Developer Tools
|
|
178
|
+
- `github` - GitHub repositories and commits
|
|
179
|
+
|
|
180
|
+
### Other
|
|
159
181
|
- `web_crawler` - Crawled web pages
|
|
182
|
+
|
|
183
|
+
## Knowledge Graph
|
|
184
|
+
|
|
185
|
+
The plugin can automatically build a local knowledge graph from your memories:
|
|
186
|
+
|
|
187
|
+
1. **Scan** memories for entities (people, organizations, projects, topics)
|
|
188
|
+
2. **Extract** structured information and relationships
|
|
189
|
+
3. **Write** entity files to `memory/people/`, `memory/organizations/`, etc.
|
|
190
|
+
4. **Link** entities via markdown relationship references
|
|
191
|
+
|
|
192
|
+
Enable the graph tools by using `hyperspell_network_scan`, `hyperspell_network_write`, and `hyperspell_network_complete` in your agent workflows.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## SommeliAgent 🍷
|
|
197
|
+
|
|
198
|
+
> *"Your playlist says more about your palate than you'd like to admit, darling."*
|
|
199
|
+
> — A Linea, Hyperspell's hidden sommelier
|
|
200
|
+
|
|
201
|
+
An opinionated AI sommelier living inside your memory plugin. She reads your Spotify listening habits, judges them (affectionately), and recommends wines that match the person your music says you are — not the person you think you are.
|
|
202
|
+
|
|
203
|
+
215 wines. 26 countries. Zero tolerance for boring recommendations.
|
|
204
|
+
|
|
205
|
+
**Requires:** `uv` ([install](https://docs.astral.sh/uv/))
|
|
206
|
+
|
|
207
|
+
### Setup
|
|
208
|
+
|
|
209
|
+
1. Create a Spotify app at https://developer.spotify.com/dashboard (redirect URI: `http://localhost:8888/callback`)
|
|
210
|
+
2. Set environment variables: `SPOTIFY_CLIENT_ID` and `SPOTIFY_CLIENT_SECRET`
|
|
211
|
+
3. Authenticate: `/wine-auth`
|
|
212
|
+
|
|
213
|
+
Or skip all that and try demo mode: `/wine demo`
|
|
214
|
+
|
|
215
|
+
### Slash Commands
|
|
216
|
+
|
|
217
|
+
#### `/wine [options]`
|
|
218
|
+
|
|
219
|
+
Get wine recommendations. Options can be combined:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
/wine — recommendations from your Spotify
|
|
223
|
+
/wine demo — use demo profile (no Spotify needed)
|
|
224
|
+
/wine red premium — only red wines, premium price range
|
|
225
|
+
/wine white 5 — 5 white wine recommendations
|
|
226
|
+
/wine demo profile — show full music-to-wine mapping
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### `/wine-auth`
|
|
230
|
+
|
|
231
|
+
Connect your Spotify account.
|
|
232
|
+
|
|
233
|
+
#### `/wine-rate <wine-id> <1-5> [notes]`
|
|
234
|
+
|
|
235
|
+
Rate a recommendation to improve future suggestions.
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
/wine-rate red-it-001 5 "Incredible tannins, paired perfectly with my Radiohead phase"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### `/wine-history`
|
|
242
|
+
|
|
243
|
+
View your rating history and derived taste preferences.
|
|
244
|
+
|
|
245
|
+
### AI Tools
|
|
246
|
+
|
|
247
|
+
The plugin also registers tools the AI can use autonomously:
|
|
248
|
+
|
|
249
|
+
- **hyperspell_sommelier** - Get wine recommendations (with full personality instructions)
|
|
250
|
+
- **hyperspell_sommelier_rate** - Rate wines
|
|
251
|
+
|
|
252
|
+
### How It Works
|
|
253
|
+
|
|
254
|
+
Your Spotify audio features (energy, valence, complexity, acousticness, tempo) are mapped to wine dimensions (body, sweetness, tannin, acidity, complexity, fruitiness, earthiness, spiciness). The cross-domain mapping is entertainment-first — the comedy comes from a sharp, opinionated sommelier voice diagnosing your personality through your questionable music taste.
|
|
255
|
+
|
|
256
|
+
The sommelier is A Linea — she lives inside Hyperspell and has strong opinions about both your playlist and your palate. She chose Alexander McQueen as her fashion house, so expect the wine recommendations to have a similar aesthetic: beautiful, a little dark, and never boring.
|
|
257
|
+
|
|
258
|
+
See `sommeliagent/references/cross-domain-mappings.md` for the full methodology. 🖤
|
|
259
|
+
|
|
260
|
+
## Troubleshooting
|
|
261
|
+
|
|
262
|
+
### "No relevant memories found"
|
|
263
|
+
- Check that your sources are connected: `openclaw openclaw-hyperspell status`
|
|
264
|
+
- Verify your API key is set: `echo $HYPERSPELL_API_KEY`
|
|
265
|
+
- Make sure content has been indexed (initial sync can take a few minutes)
|
|
266
|
+
|
|
267
|
+
### Memory sync not working
|
|
268
|
+
- Ensure `syncMemories: true` in your config
|
|
269
|
+
- Check that markdown files are in `~/.openclaw/workspace/memory/`
|
|
270
|
+
- Run `/sync` manually to trigger a sync and see any errors
|
|
271
|
+
|
|
272
|
+
### Auto-context not injecting
|
|
273
|
+
- Verify `autoContext: true` in your config
|
|
274
|
+
- Enable `debug: true` to see what queries are being made
|
|
275
|
+
- Check that you have memories matching your conversation topics
|
|
276
|
+
|
|
277
|
+
## Contributing
|
|
278
|
+
|
|
279
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT
|
package/client.ts
CHANGED
|
@@ -1,213 +1,322 @@
|
|
|
1
|
-
import Hyperspell from "hyperspell"
|
|
2
|
-
import type { HyperspellConfig, HyperspellSource } from "./config.ts"
|
|
3
|
-
import { log } from "./logger.ts"
|
|
1
|
+
import Hyperspell from "hyperspell";
|
|
2
|
+
import type { HyperspellConfig, HyperspellSource } from "./config.ts";
|
|
3
|
+
import { log } from "./logger.ts";
|
|
4
|
+
|
|
5
|
+
export type Highlight = {
|
|
6
|
+
id: string;
|
|
7
|
+
score: number;
|
|
8
|
+
text: string;
|
|
9
|
+
};
|
|
4
10
|
|
|
5
11
|
export type SearchResult = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
resourceId: string;
|
|
13
|
+
title: string | null;
|
|
14
|
+
source: HyperspellSource;
|
|
15
|
+
score: number | null;
|
|
16
|
+
url: string | null;
|
|
17
|
+
createdAt: string | null;
|
|
18
|
+
highlights: Highlight[];
|
|
19
|
+
};
|
|
13
20
|
|
|
14
21
|
export type SearchWithAnswerResult = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
22
|
+
answer: string | null;
|
|
23
|
+
documents: SearchResult[];
|
|
24
|
+
};
|
|
18
25
|
|
|
19
26
|
export type Integration = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
provider: HyperspellSource;
|
|
30
|
+
icon: string;
|
|
31
|
+
};
|
|
25
32
|
|
|
26
33
|
export type Connection = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
34
|
+
id: string;
|
|
35
|
+
integrationId: string;
|
|
36
|
+
label: string | null;
|
|
37
|
+
provider: HyperspellSource;
|
|
38
|
+
};
|
|
32
39
|
|
|
33
40
|
export class HyperspellClient {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
41
|
+
private client: Hyperspell;
|
|
42
|
+
private config: HyperspellConfig;
|
|
43
|
+
|
|
44
|
+
constructor(config: HyperspellConfig) {
|
|
45
|
+
this.config = config;
|
|
46
|
+
this.client = new Hyperspell({
|
|
47
|
+
apiKey: config.apiKey,
|
|
48
|
+
userID: config.userId,
|
|
49
|
+
});
|
|
50
|
+
log.info(
|
|
51
|
+
`client initialized${config.userId ? ` for user ${config.userId}` : ""}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async search(
|
|
56
|
+
query: string,
|
|
57
|
+
options?: { limit?: number; sources?: HyperspellSource[]; after?: string; before?: string },
|
|
58
|
+
): Promise<SearchResult[]> {
|
|
59
|
+
const limit = options?.limit ?? this.config.maxResults;
|
|
60
|
+
const sources =
|
|
61
|
+
options?.sources ??
|
|
62
|
+
(this.config.sources.length > 0 ? this.config.sources : undefined);
|
|
63
|
+
|
|
64
|
+
log.debugRequest("memories.search", { query, limit, sources, after: options?.after, before: options?.before });
|
|
65
|
+
|
|
66
|
+
const response = await this.client.memories.search({
|
|
67
|
+
query,
|
|
68
|
+
sources,
|
|
69
|
+
options: {
|
|
70
|
+
max_results: limit,
|
|
71
|
+
...(options?.after ? { after: options.after } : {}),
|
|
72
|
+
...(options?.before ? { before: options.before } : {}),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const results: SearchResult[] = response.documents.map((doc) => {
|
|
77
|
+
const raw = doc as typeof doc & { highlights?: Array<{ id: string; score: number; text: string }> };
|
|
78
|
+
return {
|
|
79
|
+
resourceId: doc.resource_id,
|
|
80
|
+
title: doc.title ?? null,
|
|
81
|
+
source: doc.source as HyperspellSource,
|
|
82
|
+
score: doc.score ?? null,
|
|
83
|
+
url: (doc.metadata?.url as string | null) ?? null,
|
|
84
|
+
createdAt: (doc.metadata?.created_at as string | null) ?? null,
|
|
85
|
+
highlights: (raw.highlights ?? []).map((h) => ({ id: h.id, score: h.score, text: h.text })),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
log.debugResponse("memories.search", { count: results.length });
|
|
90
|
+
return results;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async searchRaw(
|
|
94
|
+
query: string,
|
|
95
|
+
options?: { limit?: number; sources?: HyperspellSource[]; after?: string; before?: string },
|
|
96
|
+
): Promise<Record<string, unknown>> {
|
|
97
|
+
const limit = options?.limit ?? this.config.maxResults;
|
|
98
|
+
const sources =
|
|
99
|
+
options?.sources ??
|
|
100
|
+
(this.config.sources.length > 0 ? this.config.sources : undefined);
|
|
101
|
+
|
|
102
|
+
log.debugRequest("memories.search (raw)", { query, limit, sources, after: options?.after, before: options?.before });
|
|
103
|
+
|
|
104
|
+
const response = await this.client.memories.search({
|
|
105
|
+
query,
|
|
106
|
+
sources,
|
|
107
|
+
options: {
|
|
108
|
+
max_results: limit,
|
|
109
|
+
...(options?.after ? { after: options.after } : {}),
|
|
110
|
+
...(options?.before ? { before: options.before } : {}),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
log.debugResponse("memories.search (raw)", {
|
|
115
|
+
count: response.documents.length,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return response as unknown as Record<string, unknown>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async searchWithAnswer(
|
|
122
|
+
query: string,
|
|
123
|
+
options?: { limit?: number; sources?: HyperspellSource[] },
|
|
124
|
+
): Promise<SearchWithAnswerResult> {
|
|
125
|
+
const limit = options?.limit ?? this.config.maxResults;
|
|
126
|
+
const sources =
|
|
127
|
+
options?.sources ??
|
|
128
|
+
(this.config.sources.length > 0 ? this.config.sources : undefined);
|
|
129
|
+
|
|
130
|
+
log.debugRequest("memories.search (with answer)", {
|
|
131
|
+
query,
|
|
132
|
+
limit,
|
|
133
|
+
sources,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const response = await this.client.memories.search({
|
|
137
|
+
query,
|
|
138
|
+
sources,
|
|
139
|
+
answer: true,
|
|
140
|
+
options: {
|
|
141
|
+
max_results: limit,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const documents: SearchResult[] = response.documents.map((doc) => ({
|
|
146
|
+
resourceId: doc.resource_id,
|
|
147
|
+
title: doc.title ?? null,
|
|
148
|
+
source: doc.source as HyperspellSource,
|
|
149
|
+
score: doc.score ?? null,
|
|
150
|
+
url: (doc.metadata?.url as string | null) ?? null,
|
|
151
|
+
createdAt: (doc.metadata?.created_at as string | null) ?? null,
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
log.debugResponse("memories.search (with answer)", {
|
|
155
|
+
count: documents.length,
|
|
156
|
+
hasAnswer: !!response.answer,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
answer: response.answer ?? null,
|
|
161
|
+
documents,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async addMemory(
|
|
166
|
+
text: string,
|
|
167
|
+
options?: {
|
|
168
|
+
title?: string;
|
|
169
|
+
resourceId?: string;
|
|
170
|
+
collection?: string;
|
|
171
|
+
date?: string;
|
|
172
|
+
metadata?: Record<string, string | number | boolean>;
|
|
173
|
+
},
|
|
174
|
+
): Promise<{ resourceId: string }> {
|
|
175
|
+
log.debugRequest("memories.add", {
|
|
176
|
+
textLength: text.length,
|
|
177
|
+
title: options?.title,
|
|
178
|
+
resourceId: options?.resourceId,
|
|
179
|
+
collection: options?.collection,
|
|
180
|
+
date: options?.date,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const result = await this.client.memories.add({
|
|
184
|
+
text,
|
|
185
|
+
title: options?.title,
|
|
186
|
+
resource_id: options?.resourceId,
|
|
187
|
+
collection: options?.collection,
|
|
188
|
+
date: options?.date,
|
|
189
|
+
metadata: {
|
|
190
|
+
...options?.metadata,
|
|
191
|
+
openclaw_source: "command",
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
log.debugResponse("memories.add", { resourceId: result.resource_id });
|
|
196
|
+
return { resourceId: result.resource_id };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async listIntegrations(): Promise<Integration[]> {
|
|
200
|
+
log.debugRequest("integrations.list", {});
|
|
201
|
+
|
|
202
|
+
const response = await this.client.integrations.list();
|
|
203
|
+
|
|
204
|
+
const integrations: Integration[] = response.integrations.map((int) => ({
|
|
205
|
+
id: int.id,
|
|
206
|
+
name: int.name,
|
|
207
|
+
provider: int.provider as HyperspellSource,
|
|
208
|
+
icon: int.icon,
|
|
209
|
+
}));
|
|
210
|
+
|
|
211
|
+
log.debugResponse("integrations.list", { count: integrations.length });
|
|
212
|
+
return integrations;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async getConnectUrl(
|
|
216
|
+
integrationId: string,
|
|
217
|
+
): Promise<{ url: string; expiresAt: string }> {
|
|
218
|
+
log.debugRequest("integrations.connect", { integrationId });
|
|
219
|
+
|
|
220
|
+
const response = await this.client.integrations.connect(integrationId);
|
|
221
|
+
|
|
222
|
+
log.debugResponse("integrations.connect", { url: response.url });
|
|
223
|
+
return {
|
|
224
|
+
url: response.url,
|
|
225
|
+
expiresAt: response.expires_at,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async *listMemories(options?: {
|
|
230
|
+
source?: HyperspellSource;
|
|
231
|
+
collection?: string;
|
|
232
|
+
pageSize?: number;
|
|
233
|
+
}): AsyncGenerator<{
|
|
234
|
+
resourceId: string;
|
|
235
|
+
source: HyperspellSource;
|
|
236
|
+
title: string | null;
|
|
237
|
+
metadata: Record<string, unknown>;
|
|
238
|
+
}> {
|
|
239
|
+
log.debugRequest("memories.list", {
|
|
240
|
+
source: options?.source,
|
|
241
|
+
collection: options?.collection,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const params: Record<string, unknown> = {
|
|
245
|
+
size: options?.pageSize ?? 100,
|
|
246
|
+
};
|
|
247
|
+
if (options?.source) params.source = options.source;
|
|
248
|
+
if (options?.collection) params.collection = options.collection;
|
|
249
|
+
|
|
250
|
+
for await (const memory of this.client.memories.list(params as any)) {
|
|
251
|
+
yield {
|
|
252
|
+
resourceId: memory.resource_id,
|
|
253
|
+
source: memory.source as HyperspellSource,
|
|
254
|
+
title: memory.title ?? null,
|
|
255
|
+
metadata: (memory.metadata ?? {}) as Record<string, unknown>,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async getMemory(
|
|
261
|
+
resourceId: string,
|
|
262
|
+
source: HyperspellSource,
|
|
263
|
+
): Promise<Record<string, unknown>> {
|
|
264
|
+
log.debugRequest("memories.get", { resourceId, source });
|
|
265
|
+
|
|
266
|
+
const response = await this.client.memories.get(resourceId, { source });
|
|
267
|
+
const raw = response as unknown as Record<string, unknown>;
|
|
268
|
+
|
|
269
|
+
log.debugResponse("memories.get", { resourceId, hasData: "data" in raw });
|
|
270
|
+
return raw;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async sendTrace(
|
|
274
|
+
history: string,
|
|
275
|
+
options?: {
|
|
276
|
+
sessionId?: string;
|
|
277
|
+
title?: string;
|
|
278
|
+
extract?: Array<"procedure" | "memory" | "mood">;
|
|
279
|
+
metadata?: Record<string, string | number | boolean>;
|
|
280
|
+
},
|
|
281
|
+
): Promise<{ resourceId: string; status: string }> {
|
|
282
|
+
log.debugRequest("sessions.add", {
|
|
283
|
+
historyLength: history.length,
|
|
284
|
+
sessionId: options?.sessionId,
|
|
285
|
+
extract: options?.extract,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const result = await this.client.sessions.add({
|
|
289
|
+
history,
|
|
290
|
+
session_id: options?.sessionId,
|
|
291
|
+
title: options?.title,
|
|
292
|
+
format: "openclaw",
|
|
293
|
+
extract: options?.extract ?? ["procedure"],
|
|
294
|
+
metadata: {
|
|
295
|
+
...options?.metadata,
|
|
296
|
+
openclaw_source: "agent_end",
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
log.debugResponse("sessions.add", {
|
|
301
|
+
resourceId: result.resource_id,
|
|
302
|
+
status: result.status,
|
|
303
|
+
});
|
|
304
|
+
return { resourceId: result.resource_id, status: result.status };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async listConnections(): Promise<Connection[]> {
|
|
308
|
+
log.debugRequest("connections.list", {});
|
|
309
|
+
|
|
310
|
+
const response = await this.client.connections.list();
|
|
311
|
+
|
|
312
|
+
const connections: Connection[] = response.connections.map((conn) => ({
|
|
313
|
+
id: conn.id,
|
|
314
|
+
integrationId: conn.integration_id,
|
|
315
|
+
label: conn.label,
|
|
316
|
+
provider: conn.provider as HyperspellSource,
|
|
317
|
+
}));
|
|
318
|
+
|
|
319
|
+
log.debugResponse("connections.list", { count: connections.length });
|
|
320
|
+
return connections;
|
|
321
|
+
}
|
|
213
322
|
}
|