@basicmemory/openclaw-basic-memory 0.1.4 → 0.1.5

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 CHANGED
@@ -5,6 +5,15 @@ import { log } from "./logger.ts"
5
5
 
6
6
  const DEFAULT_RETRY_DELAYS_MS = [500, 1000, 2000]
7
7
 
8
+ export class NoteAlreadyExistsError extends Error {
9
+ readonly permalink: string
10
+ constructor(title: string, permalink: string) {
11
+ super(`Note already exists: "${title}" (${permalink})`)
12
+ this.name = "NoteAlreadyExistsError"
13
+ this.permalink = permalink
14
+ }
15
+ }
16
+
8
17
  const REQUIRED_TOOLS = [
9
18
  "search_notes",
10
19
  "read_note",
@@ -569,6 +578,7 @@ export class BmClient {
569
578
  content: string,
570
579
  folder: string,
571
580
  project?: string,
581
+ overwrite?: boolean,
572
582
  ): Promise<NoteResult> {
573
583
  const args: Record<string, unknown> = {
574
584
  title,
@@ -577,6 +587,7 @@ export class BmClient {
577
587
  output_format: "json",
578
588
  }
579
589
  if (project) args.project = project
590
+ if (overwrite !== undefined) args.overwrite = overwrite
580
591
 
581
592
  const payload = await this.callTool("write_note", args)
582
593
 
@@ -584,6 +595,13 @@ export class BmClient {
584
595
  throw new Error("invalid write_note response")
585
596
  }
586
597
 
598
+ if (payload.error === "NOTE_ALREADY_EXISTS") {
599
+ throw new NoteAlreadyExistsError(
600
+ asString(payload.title) ?? title,
601
+ asString(payload.permalink) ?? "",
602
+ )
603
+ }
604
+
587
605
  const resultTitle = asString(payload.title)
588
606
  const permalink = asString(payload.permalink)
589
607
  const filePath = asString(payload.file_path)
@@ -850,7 +868,7 @@ export class BmClient {
850
868
  ].join("\n")
851
869
 
852
870
  try {
853
- await this.writeNote(title, content, "conversations")
871
+ await this.writeNote(title, content, "conversations", undefined, true)
854
872
  log.debug(`created conversation note: ${title}`)
855
873
  } catch (err) {
856
874
  log.error("conversation index failed", err)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basicmemory/openclaw-basic-memory",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "Basic Memory plugin for OpenClaw — local-first knowledge graph for agent memory",
6
6
  "license": "MIT",
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox"
2
2
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
3
3
  import type { BmClient } from "../bm-client.ts"
4
+ import { NoteAlreadyExistsError } from "../bm-client.ts"
4
5
  import { log } from "../logger.ts"
5
6
 
6
7
  export function registerWriteTool(
@@ -12,9 +13,9 @@ export function registerWriteTool(
12
13
  name: "write_note",
13
14
  label: "Write Note",
14
15
  description:
15
- "Create or update a note in the Basic Memory knowledge graph. " +
16
- "Notes are stored as Markdown files with semantic structure " +
17
- "(observations, relations) that build a connected knowledge graph.",
16
+ "Create a note in the Basic Memory knowledge graph. " +
17
+ "If the note already exists, returns an error by default. " +
18
+ "Pass overwrite=true to replace, or use edit_note for incremental updates.",
18
19
  parameters: Type.Object({
19
20
  title: Type.String({ description: "Note title" }),
20
21
  content: Type.String({
@@ -26,6 +27,12 @@ export function registerWriteTool(
26
27
  description: "Target project name (defaults to current project)",
27
28
  }),
28
29
  ),
30
+ overwrite: Type.Optional(
31
+ Type.Boolean({
32
+ description:
33
+ "Set to true to replace an existing note. Defaults to false.",
34
+ }),
35
+ ),
29
36
  }),
30
37
  async execute(
31
38
  _toolCallId: string,
@@ -34,6 +41,7 @@ export function registerWriteTool(
34
41
  content: string
35
42
  folder: string
36
43
  project?: string
44
+ overwrite?: boolean
37
45
  },
38
46
  ) {
39
47
  log.debug(`write_note: title=${params.title} folder=${params.folder}`)
@@ -44,6 +52,7 @@ export function registerWriteTool(
44
52
  params.content,
45
53
  params.folder,
46
54
  params.project,
55
+ params.overwrite,
47
56
  )
48
57
 
49
58
  const msg = `Note saved: ${note.title} (${note.permalink})`
@@ -61,6 +70,22 @@ export function registerWriteTool(
61
70
  },
62
71
  }
63
72
  } catch (err) {
73
+ if (err instanceof NoteAlreadyExistsError) {
74
+ const hint = [
75
+ `Note already exists: "${params.title}" (${err.permalink})`,
76
+ "",
77
+ "To update this note, use one of:",
78
+ ` - edit_note("${err.permalink}", operation="append", content="...")`,
79
+ ` - edit_note("${err.permalink}", operation="replace_section", section="...", content="...")`,
80
+ ` - write_note("${params.title}", ..., overwrite=true) to fully replace`,
81
+ ` - read_note("${err.permalink}") to inspect current content first`,
82
+ ].join("\n")
83
+
84
+ return {
85
+ content: [{ type: "text" as const, text: hint }],
86
+ }
87
+ }
88
+
64
89
  log.error("write_note failed", err)
65
90
  return {
66
91
  content: [