@hyperspell/openclaw-hyperspell 0.9.0 → 0.10.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/tools/search.ts CHANGED
@@ -1,98 +1,102 @@
1
1
  import { Type } from "@sinclair/typebox"
2
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
3
2
  import type { HyperspellClient } from "../client.ts"
4
3
  import type { HyperspellConfig } from "../config.ts"
4
+ import { resolveUser } from "../lib/sender.ts"
5
5
  import { log } from "../logger.ts"
6
6
 
7
- export function registerSearchTool(
8
- api: OpenClawPluginApi,
7
+ export function createSearchToolFactory(
9
8
  client: HyperspellClient,
10
- _cfg: HyperspellConfig,
11
- ): void {
12
- api.registerTool(
13
- {
14
- name: "hyperspell_search",
15
- label: "Memory Search",
16
- description:
17
- "Search through the user's connected sources (Notion, Slack, Gmail, Google Drive, etc.) for relevant information.",
18
- parameters: Type.Object({
19
- query: Type.String({ description: "Search query" }),
20
- limit: Type.Optional(
21
- Type.Number({ description: "Max results (default: 5)" }),
22
- ),
23
- after: Type.Optional(
24
- Type.String({ description: "Only return memories created on or after this date (ISO 8601 or YYYY-MM-DD)" }),
25
- ),
26
- before: Type.Optional(
27
- Type.String({ description: "Only return memories created before this date (ISO 8601 or YYYY-MM-DD)" }),
28
- ),
29
- }),
30
- async execute(
31
- _toolCallId: string,
32
- params: { query: string; limit?: number; after?: string; before?: string },
33
- ) {
34
- const limit = params.limit ?? 5
35
- log.debug(`search tool: query="${params.query}" limit=${limit} after=${params.after ?? "none"} before=${params.before ?? "none"}`)
9
+ cfg: HyperspellConfig,
10
+ ) {
11
+ return (ctx: Record<string, unknown>) => ({
12
+ name: "hyperspell_search",
13
+ label: "Memory Search",
14
+ description:
15
+ "Search through the user's connected sources (Notion, Slack, Gmail, Google Drive, etc.) for relevant information.",
16
+ parameters: Type.Object({
17
+ query: Type.String({ description: "Search query" }),
18
+ limit: Type.Optional(
19
+ Type.Number({ description: "Max results (default: 5)" }),
20
+ ),
21
+ after: Type.Optional(
22
+ Type.String({ description: "Only return memories created on or after this date (ISO 8601 or YYYY-MM-DD)" }),
23
+ ),
24
+ before: Type.Optional(
25
+ Type.String({ description: "Only return memories created before this date (ISO 8601 or YYYY-MM-DD)" }),
26
+ ),
27
+ userId: Type.Optional(
28
+ Type.String({
29
+ description:
30
+ "Search as a specific user (e.g. 'ben', 'shared'). Omit to search as current sender.",
31
+ }),
32
+ ),
33
+ }),
34
+ async execute(
35
+ _toolCallId: string,
36
+ params: { query: string; limit?: number; after?: string; before?: string; userId?: string },
37
+ ) {
38
+ const limit = params.limit ?? 5
39
+ // Resolve userId: explicit param > sender resolution > config default
40
+ const resolved = resolveUser(ctx, cfg)
41
+ const userId = params.userId ?? resolved?.userId
42
+ log.debug(
43
+ `search tool: query="${params.query}" limit=${limit} after=${params.after ?? "none"} before=${params.before ?? "none"} userId=${userId}`,
44
+ )
36
45
 
37
- try {
38
- const response = await client.searchRaw(params.query, { limit, after: params.after, before: params.before })
39
- const documents = (response.documents ?? []) as Array<{
40
- source: string
41
- resource_id: string
42
- score?: number
43
- summary?: string
44
- title?: string
45
- metadata?: Record<string, unknown>
46
- highlights?: Array<{ text: string }>
47
- data?: Array<{ text: string }>
48
- }>
46
+ try {
47
+ const response = await client.searchRaw(params.query, {
48
+ limit,
49
+ after: params.after,
50
+ before: params.before,
51
+ userId,
52
+ })
53
+ const documents = (response.documents ?? []) as Array<{
54
+ source: string
55
+ resource_id: string
56
+ score?: number
57
+ summary?: string
58
+ title?: string
59
+ metadata?: Record<string, unknown>
60
+ highlights?: Array<{ text: string }>
61
+ data?: Array<{ text: string }>
62
+ }>
49
63
 
50
- if (documents.length === 0) {
51
- return {
52
- content: [
53
- { type: "text" as const, text: "No relevant memories found." },
54
- ],
55
- }
64
+ if (documents.length === 0) {
65
+ return {
66
+ content: [
67
+ { type: "text" as const, text: "No relevant memories found." },
68
+ ],
56
69
  }
70
+ }
57
71
 
58
- const formattedDocs = documents
59
- .map((doc, i) => {
60
- const relevance = doc.score
61
- ? `${Math.round(doc.score * 100)}%`
62
- : "N/A"
63
- const title = doc.title || "(untitled)"
64
- const summary = doc.summary || "(no summary)"
65
- return `${i + 1}. Source: ${doc.source}\n Title: ${title}\n Summary: ${summary}\n Relevance: ${relevance}`
66
- })
67
- .join("\n\n")
72
+ const formattedDocs = documents
73
+ .map((doc, i) => {
74
+ const relevance = doc.score
75
+ ? `${Math.round(doc.score * 100)}%`
76
+ : "N/A"
77
+ const title = doc.title || "(untitled)"
78
+ const summary = doc.summary || "(no summary)"
79
+ return `${i + 1}. Source: ${doc.source}\n Title: ${title}\n Summary: ${summary}\n Relevance: ${relevance}`
80
+ })
81
+ .join("\n\n")
68
82
 
69
- const text = `Found ${documents.length} memories:\n\n${formattedDocs}`
83
+ const text = `Found ${documents.length} memories:\n\n${formattedDocs}`
70
84
 
71
- return {
72
- content: [
73
- {
74
- type: "text" as const,
75
- text,
76
- },
77
- ],
78
- details: {
79
- count: documents.length,
80
- documents,
85
+ return {
86
+ content: [{ type: "text" as const, text }],
87
+ details: { count: documents.length, documents },
88
+ }
89
+ } catch (err) {
90
+ log.error("search tool failed", err)
91
+ return {
92
+ content: [
93
+ {
94
+ type: "text" as const,
95
+ text: `Search failed: ${err instanceof Error ? err.message : String(err)}`,
81
96
  },
82
- }
83
- } catch (err) {
84
- log.error("search tool failed", err)
85
- return {
86
- content: [
87
- {
88
- type: "text" as const,
89
- text: `Search failed: ${err instanceof Error ? err.message : String(err)}`,
90
- },
91
- ],
92
- }
97
+ ],
93
98
  }
94
- },
99
+ }
95
100
  },
96
- { name: "hyperspell_search" },
97
- )
101
+ })
98
102
  }
@@ -48,6 +48,22 @@ declare module "openclaw/plugin-sdk" {
48
48
  },
49
49
  meta: { name: string },
50
50
  ): void
51
+ registerTool<T = unknown>(
52
+ factory: (ctx: Record<string, unknown>) => {
53
+ name: string
54
+ label: string
55
+ description: string
56
+ parameters: unknown
57
+ execute: (
58
+ toolCallId: string,
59
+ params: T,
60
+ ) => Promise<{
61
+ content: Array<{ type: "text"; text: string }>
62
+ details?: Record<string, unknown>
63
+ }>
64
+ },
65
+ meta: { name: string },
66
+ ): void
51
67
  on(event: string, handler: (event: Record<string, unknown>, ctx?: Record<string, unknown>) => Promise<{ prependContext?: string } | void> | void): void
52
68
  registerService(options: {
53
69
  id: string