Skip to content

Commit 93fe36c

Browse files
authored
feat: sm_source attribution and kebab tool names (#53)
## Summary Plugin unification: source attribution and kebab tool names for analytics and consistent naming. ## Changes | Area | Change | |------|--------| | **Attribution** | `sm_source: openclaw`, `x-sm-source: openclaw` on all API calls | | **Tools** | Kebab primary names: `supermemory-search`, `supermemory-save`, `supermemory-forget`, `supermemory-profile` (+ legacy snake registrations) | | **Capture** | `sm_capture_mode: tool` on explicit store | | **Version** | **2.1.13** | Also includes prior packaging/status capture fix on this branch. ## Test plan - [ ] `openclaw plugins update` picks up changes - [ ] Kebab tool names work; snake aliases still resolve - [ ] API requests include `x-sm-source: openclaw`
2 parents 7e33a5f + 11d3e0e commit 93fe36c

8 files changed

Lines changed: 65 additions & 18 deletions

File tree

client.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ export class SupermemoryClient {
4747
log.warn(`container tag warning: ${tagCheck.reason}`)
4848
}
4949

50-
this.client = new Supermemory({ apiKey })
50+
// `x-sm-source` is read by mono's API to attribute searches and
51+
// writes to the OpenClaw plugin in PostHog / `document.source`.
52+
this.client = new Supermemory({
53+
apiKey,
54+
defaultHeaders: { "x-sm-source": "openclaw" },
55+
})
5156
this.containerTag = containerTag
5257
log.info(`initialized (container: ${containerTag})`)
5358
}
@@ -58,14 +63,23 @@ export class SupermemoryClient {
5863
customId?: string,
5964
containerTag?: string,
6065
entityContext?: string,
61-
): Promise<{ id: string }> {
66+
): Promise<{ id: string; status: string }> {
6267
const cleaned = sanitizeContent(content)
6368
const tag = containerTag ?? this.containerTag
6469

70+
// Always stamp `sm_source` so mono's `document.source` column attributes
71+
// these writes to the OpenClaw plugin. Existing callers can still pass
72+
// extra metadata (e.g. `source: "openclaw_tool"`) and it is preserved
73+
// underneath the canonical `sm_source` key.
74+
const mergedMetadata: Record<string, string | number | boolean> = {
75+
sm_source: "openclaw",
76+
...(metadata ?? {}),
77+
}
78+
6579
log.debugRequest("add", {
6680
contentLength: cleaned.length,
6781
customId,
68-
metadata,
82+
metadata: mergedMetadata,
6983
containerTag: tag,
7084
})
7185

@@ -76,13 +90,22 @@ export class SupermemoryClient {
7690
const result = await this.client.add({
7791
content: cleaned,
7892
containerTag: tag,
79-
...(metadata && { metadata }),
93+
metadata: mergedMetadata,
8094
...(customId && { customId }),
8195
...(clampedCtx && { entityContext: clampedCtx }),
8296
})
8397

84-
log.debugResponse("add", { id: result.id })
85-
return { id: result.id }
98+
log.debugResponse("add", { id: result.id, status: result.status })
99+
100+
if (result.status === "failed") {
101+
log.warn(
102+
`add returned status="failed" for id=${result.id}` +
103+
(customId ? ` customId=${customId}` : "") +
104+
` (contentLength=${cleaned.length}, containerTag=${tag})`,
105+
)
106+
}
107+
108+
return { id: result.id, status: result.status }
86109
}
87110

88111
async search(

commands/slash.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export function registerCommands(
8686
try {
8787
const category = detectCategory(text)
8888
const sk = getSessionKey()
89-
await client.addMemory(
89+
const { status } = await client.addMemory(
9090
text,
9191
{ type: category, source: "openclaw_command" },
9292
sk ? buildDocumentId(sk) : undefined,
@@ -95,6 +95,11 @@ export function registerCommands(
9595
)
9696

9797
const preview = text.length > 60 ? `${text.slice(0, 60)}…` : text
98+
if (status === "failed") {
99+
return {
100+
text: `Memory store failed (server returned status="failed") for: "${preview}"`,
101+
}
102+
}
98103
return { text: `Remembered: "${preview}"` }
99104
} catch (err) {
100105
log.error("/remember failed", err)

index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ export default {
7777
registerStoreTool(api, client, cfg, getSessionKey)
7878
registerForgetTool(api, client, cfg)
7979
registerProfileTool(api, client, cfg)
80+
registerSearchTool(api, client, cfg, "supermemory-search")
81+
registerStoreTool(api, client, cfg, getSessionKey, "supermemory-save")
82+
registerForgetTool(api, client, cfg, "supermemory-forget")
83+
registerProfileTool(api, client, cfg, "supermemory-profile")
8084

8185
if (cfg.autoRecall) {
8286
api.on("before_prompt_build", buildRecallHandler(client, cfg))

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@supermemory/openclaw-supermemory",
3-
"version": "2.1.12",
3+
"version": "2.1.13",
44
"type": "module",
55
"description": "OpenClaw Supermemory memory plugin",
66
"license": "MIT",

tools/forget.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ export function registerForgetTool(
88
api: OpenClawPluginApi,
99
client: SupermemoryClient,
1010
_cfg: SupermemoryConfig,
11+
toolName = "supermemory_forget",
1112
): void {
1213
api.registerTool(
1314
{
14-
name: "supermemory_forget",
15+
name: toolName,
1516
label: "Memory Forget",
1617
description:
1718
"Forget/delete a specific memory. Searches for the closest match and removes it.",
@@ -66,6 +67,6 @@ export function registerForgetTool(
6667
}
6768
},
6869
},
69-
{ name: "supermemory_forget" },
70+
{ name: toolName },
7071
)
7172
}

tools/profile.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ export function registerProfileTool(
88
api: OpenClawPluginApi,
99
client: SupermemoryClient,
1010
_cfg: SupermemoryConfig,
11+
toolName = "supermemory_profile",
1112
): void {
1213
api.registerTool(
1314
{
14-
name: "supermemory_profile",
15+
name: toolName,
1516
label: "User Profile",
1617
description:
1718
"Get a summary of what is known about the user — stable preferences and recent context.",
@@ -77,6 +78,6 @@ export function registerProfileTool(
7778
}
7879
},
7980
},
80-
{ name: "supermemory_profile" },
81+
{ name: toolName },
8182
)
8283
}

tools/search.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ export function registerSearchTool(
88
api: OpenClawPluginApi,
99
client: SupermemoryClient,
1010
_cfg: SupermemoryConfig,
11+
toolName = "supermemory_search",
1112
): void {
1213
api.registerTool(
1314
{
14-
name: "supermemory_search",
15+
name: toolName,
1516
label: "Memory Search",
1617
description:
1718
"Search through long-term memories for relevant information.",
@@ -77,6 +78,6 @@ export function registerSearchTool(
7778
}
7879
},
7980
},
80-
{ name: "supermemory_search" },
81+
{ name: toolName },
8182
)
8283
}

tools/store.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ export function registerStoreTool(
1414
client: SupermemoryClient,
1515
cfg: SupermemoryConfig,
1616
getSessionKey: () => string | undefined,
17+
toolName = "supermemory_store",
1718
): void {
1819
api.registerTool(
1920
{
20-
name: "supermemory_store",
21+
name: toolName,
2122
label: "Memory Store",
2223
description: "Save important information to long-term memory.",
2324
parameters: Type.Object({
@@ -44,9 +45,9 @@ export function registerStoreTool(
4445
`store tool: category="${category}" customId="${customId}" containerTag="${params.containerTag ?? "default"}"`,
4546
)
4647

47-
await client.addMemory(
48+
const { status } = await client.addMemory(
4849
params.text,
49-
{ type: category, source: "openclaw_tool" },
50+
{ type: category, sm_capture_mode: "tool" },
5051
customId,
5152
params.containerTag,
5253
cfg.entityContext,
@@ -55,11 +56,22 @@ export function registerStoreTool(
5556
const preview =
5657
params.text.length > 80 ? `${params.text.slice(0, 80)}…` : params.text
5758

59+
if (status === "failed") {
60+
return {
61+
content: [
62+
{
63+
type: "text" as const,
64+
text: `Memory store failed (server returned status="failed") for: "${preview}"`,
65+
},
66+
],
67+
}
68+
}
69+
5870
return {
5971
content: [{ type: "text" as const, text: `Stored: "${preview}"` }],
6072
}
6173
},
6274
},
63-
{ name: "supermemory_store" },
75+
{ name: toolName },
6476
)
6577
}

0 commit comments

Comments
 (0)