@basicmemory/openclaw-basic-memory 0.1.1 → 0.1.3
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/bm-client.ts +6 -30
- package/hooks/recall.ts +9 -10
- package/package.json +1 -1
- package/schema/task-schema.ts +10 -10
- package/tools/memory-provider.ts +21 -36
package/bm-client.ts
CHANGED
|
@@ -7,7 +7,6 @@ const DEFAULT_RETRY_DELAYS_MS = [500, 1000, 2000]
|
|
|
7
7
|
|
|
8
8
|
const REQUIRED_TOOLS = [
|
|
9
9
|
"search_notes",
|
|
10
|
-
"search_by_metadata",
|
|
11
10
|
"read_note",
|
|
12
11
|
"write_note",
|
|
13
12
|
"edit_note",
|
|
@@ -153,12 +152,6 @@ export interface SchemaDiffResult {
|
|
|
153
152
|
cardinality_changes: string[]
|
|
154
153
|
}
|
|
155
154
|
|
|
156
|
-
export interface MetadataSearchResult {
|
|
157
|
-
results: SearchResult[]
|
|
158
|
-
current_page: number
|
|
159
|
-
page_size: number
|
|
160
|
-
}
|
|
161
|
-
|
|
162
155
|
function getErrorMessage(err: unknown): string {
|
|
163
156
|
return err instanceof Error ? err.message : String(err)
|
|
164
157
|
}
|
|
@@ -502,25 +495,29 @@ export class BmClient {
|
|
|
502
495
|
}
|
|
503
496
|
|
|
504
497
|
async search(
|
|
505
|
-
query
|
|
498
|
+
query?: string,
|
|
506
499
|
limit = 10,
|
|
507
500
|
project?: string,
|
|
508
501
|
metadata?: {
|
|
509
502
|
filters?: Record<string, unknown>
|
|
510
503
|
tags?: string[]
|
|
511
504
|
status?: string
|
|
505
|
+
note_types?: string[]
|
|
506
|
+
entity_types?: string[]
|
|
512
507
|
},
|
|
513
508
|
): Promise<SearchResult[]> {
|
|
514
509
|
const args: Record<string, unknown> = {
|
|
515
|
-
query,
|
|
516
510
|
page: 1,
|
|
517
511
|
page_size: limit,
|
|
518
512
|
output_format: "json",
|
|
519
513
|
}
|
|
514
|
+
if (query) args.query = query
|
|
520
515
|
if (project) args.project = project
|
|
521
516
|
if (metadata?.filters) args.metadata_filters = metadata.filters
|
|
522
517
|
if (metadata?.tags) args.tags = metadata.tags
|
|
523
518
|
if (metadata?.status) args.status = metadata.status
|
|
519
|
+
if (metadata?.note_types) args.note_types = metadata.note_types
|
|
520
|
+
if (metadata?.entity_types) args.entity_types = metadata.entity_types
|
|
524
521
|
|
|
525
522
|
const payload = await this.callTool("search_notes", args)
|
|
526
523
|
|
|
@@ -809,27 +806,6 @@ export class BmClient {
|
|
|
809
806
|
return payload as unknown as SchemaDiffResult
|
|
810
807
|
}
|
|
811
808
|
|
|
812
|
-
async searchByMetadata(
|
|
813
|
-
filters: Record<string, unknown>,
|
|
814
|
-
limit = 20,
|
|
815
|
-
project?: string,
|
|
816
|
-
): Promise<MetadataSearchResult> {
|
|
817
|
-
const args: Record<string, unknown> = {
|
|
818
|
-
filters,
|
|
819
|
-
limit,
|
|
820
|
-
output_format: "json",
|
|
821
|
-
}
|
|
822
|
-
if (project) args.project = project
|
|
823
|
-
|
|
824
|
-
const payload = await this.callTool("search_by_metadata", args)
|
|
825
|
-
|
|
826
|
-
if (!isRecord(payload) || !Array.isArray(payload.results)) {
|
|
827
|
-
throw new Error("invalid search_by_metadata response")
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
return payload as unknown as MetadataSearchResult
|
|
831
|
-
}
|
|
832
|
-
|
|
833
809
|
async indexConversation(
|
|
834
810
|
userMessage: string,
|
|
835
811
|
assistantResponse: string,
|
package/hooks/recall.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BmClient,
|
|
3
|
-
MetadataSearchResult,
|
|
4
|
-
RecentResult,
|
|
5
|
-
} from "../bm-client.ts"
|
|
1
|
+
import type { BmClient, RecentResult, SearchResult } from "../bm-client.ts"
|
|
6
2
|
import type { BasicMemoryConfig } from "../config.ts"
|
|
7
3
|
import { log } from "../logger.ts"
|
|
8
4
|
|
|
@@ -11,14 +7,14 @@ import { log } from "../logger.ts"
|
|
|
11
7
|
* Returns empty string if nothing was found.
|
|
12
8
|
*/
|
|
13
9
|
export function formatRecallContext(
|
|
14
|
-
tasks:
|
|
10
|
+
tasks: SearchResult[],
|
|
15
11
|
recent: RecentResult[],
|
|
16
12
|
prompt: string,
|
|
17
13
|
): string {
|
|
18
14
|
const sections: string[] = []
|
|
19
15
|
|
|
20
|
-
if (tasks.
|
|
21
|
-
const taskLines = tasks.
|
|
16
|
+
if (tasks.length > 0) {
|
|
17
|
+
const taskLines = tasks.map((t) => {
|
|
22
18
|
const preview =
|
|
23
19
|
t.content.length > 120 ? `${t.content.slice(0, 120)}...` : t.content
|
|
24
20
|
return `- **${t.title}** — ${preview}`
|
|
@@ -46,7 +42,10 @@ export function buildRecallHandler(client: BmClient, cfg: BasicMemoryConfig) {
|
|
|
46
42
|
return async (_event: Record<string, unknown>) => {
|
|
47
43
|
try {
|
|
48
44
|
const [tasks, recent] = await Promise.all([
|
|
49
|
-
client.
|
|
45
|
+
client.search(undefined, 5, undefined, {
|
|
46
|
+
note_types: ["Task"],
|
|
47
|
+
status: "active",
|
|
48
|
+
}),
|
|
50
49
|
client.recentActivity("1d"),
|
|
51
50
|
])
|
|
52
51
|
|
|
@@ -54,7 +53,7 @@ export function buildRecallHandler(client: BmClient, cfg: BasicMemoryConfig) {
|
|
|
54
53
|
if (!context) return {}
|
|
55
54
|
|
|
56
55
|
log.debug(
|
|
57
|
-
`recall: ${tasks.
|
|
56
|
+
`recall: ${tasks.length} active tasks, ${recent.length} recent items`,
|
|
58
57
|
)
|
|
59
58
|
|
|
60
59
|
return { context }
|
package/package.json
CHANGED
package/schema/task-schema.ts
CHANGED
|
@@ -8,16 +8,16 @@ type: schema
|
|
|
8
8
|
entity: Task
|
|
9
9
|
version: 1
|
|
10
10
|
schema:
|
|
11
|
-
description: string, what needs to be done
|
|
12
|
-
status?(enum): [active, blocked, done, abandoned], current state
|
|
13
|
-
assigned_to?: string, who is working on this
|
|
14
|
-
steps?(array): string, ordered steps to complete
|
|
15
|
-
current_step?: integer, which step number we're on (1-indexed)
|
|
16
|
-
context?: string, key context needed to resume after memory loss
|
|
17
|
-
started?: string, when work began
|
|
18
|
-
completed?: string, when work finished
|
|
19
|
-
blockers?(array): string, what's preventing progress
|
|
20
|
-
parent_task?: Task, parent task if this is a subtask
|
|
11
|
+
description: "string, what needs to be done"
|
|
12
|
+
status?(enum): "[active, blocked, done, abandoned], current state"
|
|
13
|
+
assigned_to?: "string, who is working on this"
|
|
14
|
+
steps?(array): "string, ordered steps to complete"
|
|
15
|
+
current_step?: "integer, which step number we're on (1-indexed)"
|
|
16
|
+
context?: "string, key context needed to resume after memory loss"
|
|
17
|
+
started?: "string, when work began"
|
|
18
|
+
completed?: "string, when work finished"
|
|
19
|
+
blockers?(array): "string, what's preventing progress"
|
|
20
|
+
parent_task?: "Task, parent task if this is a subtask"
|
|
21
21
|
settings:
|
|
22
22
|
validation: warn
|
|
23
23
|
---
|
package/tools/memory-provider.ts
CHANGED
|
@@ -60,8 +60,8 @@ async function searchMemoryFile(
|
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Search for active tasks via BM knowledge graph.
|
|
63
|
-
* Uses
|
|
64
|
-
* Falls back to filesystem scan if
|
|
63
|
+
* Uses search_notes with note_types and status filters for precise querying.
|
|
64
|
+
* Falls back to filesystem scan if search fails.
|
|
65
65
|
*/
|
|
66
66
|
async function searchActiveTasks(
|
|
67
67
|
query: string,
|
|
@@ -69,44 +69,29 @@ async function searchActiveTasks(
|
|
|
69
69
|
workspaceDir: string,
|
|
70
70
|
memoryDir: string,
|
|
71
71
|
): Promise<string> {
|
|
72
|
-
// Try structured
|
|
72
|
+
// Try structured search first
|
|
73
73
|
try {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (filtered.length > 0) {
|
|
91
|
-
const matches: string[] = []
|
|
92
|
-
for (const r of filtered) {
|
|
93
|
-
const score = r.score ? ` (${r.score.toFixed(2)})` : ""
|
|
94
|
-
const preview =
|
|
95
|
-
(r.content ?? "").length > 200
|
|
96
|
-
? `${(r.content ?? "").slice(0, 200)}…`
|
|
97
|
-
: (r.content ?? "")
|
|
98
|
-
matches.push(
|
|
99
|
-
`- **${r.title}**${score} — ${r.file_path}\n > ${preview.replace(/\n/g, "\n > ")}`,
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
return matches.join("\n\n")
|
|
74
|
+
const results = await client.search(query || undefined, 10, undefined, {
|
|
75
|
+
note_types: ["task"],
|
|
76
|
+
status: "active",
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
if (results.length > 0) {
|
|
80
|
+
const matches: string[] = []
|
|
81
|
+
for (const r of results) {
|
|
82
|
+
const score = r.score ? ` (${r.score.toFixed(2)})` : ""
|
|
83
|
+
const preview =
|
|
84
|
+
(r.content ?? "").length > 200
|
|
85
|
+
? `${(r.content ?? "").slice(0, 200)}…`
|
|
86
|
+
: (r.content ?? "")
|
|
87
|
+
matches.push(
|
|
88
|
+
`- **${r.title}**${score} — ${r.file_path}\n > ${preview.replace(/\n/g, "\n > ")}`,
|
|
89
|
+
)
|
|
103
90
|
}
|
|
91
|
+
return matches.join("\n\n")
|
|
104
92
|
}
|
|
105
93
|
} catch (err) {
|
|
106
|
-
log.debug(
|
|
107
|
-
"BM metadata task search failed, falling back to filesystem scan",
|
|
108
|
-
err,
|
|
109
|
-
)
|
|
94
|
+
log.debug("BM task search failed, falling back to filesystem scan", err)
|
|
110
95
|
}
|
|
111
96
|
|
|
112
97
|
// Fallback: filesystem scan for tasks not yet indexed
|