@memorystack/clawdbot-memorystack 1.0.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/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # MemoryStack Plugin for Clawdbot
2
+
3
+ Long-term memory for Clawdbot. Automatically remembers conversations, recalls relevant context, and builds a persistent memory layer — all powered by [MemoryStack](https://memorystack.app) cloud.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ cd extensions/memorystack
9
+ npm install
10
+ ```
11
+
12
+ Or install from npm (once published):
13
+
14
+ ```bash
15
+ clawdbot plugins install @memorystack/clawdbot-memorystack
16
+ ```
17
+
18
+ Restart Clawdbot after installing.
19
+
20
+ ## Configuration
21
+
22
+ The only required value is your MemoryStack API key. Get one at [memorystack.app/dashboard/api-keys](https://memorystack.app/dashboard/api-keys).
23
+
24
+ Set it as an environment variable:
25
+
26
+ ```bash
27
+ export MEMORYSTACK_API_KEY="ms_proj_..."
28
+ ```
29
+
30
+ Or configure it directly in `clawdbot.json`:
31
+
32
+ ```json
33
+ {
34
+ "plugins": {
35
+ "slots": {
36
+ "memory": "clawdbot-memorystack"
37
+ },
38
+ "entries": {
39
+ "clawdbot-memorystack": {
40
+ "enabled": true,
41
+ "config": {
42
+ "apiKey": "${MEMORYSTACK_API_KEY}",
43
+ "autoRecall": true,
44
+ "autoCapture": true,
45
+ "maxRecallResults": 5
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### Advanced options
54
+
55
+ | Key | Type | Default | Description |
56
+ |-----|------|---------|-------------|
57
+ | `apiKey` | `string` | - | Your MemoryStack API key (required) |
58
+ | `baseUrl` | `string` | `https://memorystack.app` | API endpoint URL |
59
+ | `autoRecall` | `boolean` | `true` | Inject relevant memories before every AI turn |
60
+ | `autoCapture` | `boolean` | `true` | Automatically store conversation content after every turn |
61
+ | `maxRecallResults` | `number` | `5` | Max memories injected into context per turn (1-20) |
62
+ | `debug` | `boolean` | `false` | Verbose debug logs for API calls and responses |
63
+
64
+ ## How it works
65
+
66
+ Once installed, the plugin works automatically with zero interaction:
67
+
68
+ - **Auto-Recall** — Before every AI turn, the plugin queries MemoryStack for relevant memories and injects them as context using semantic search.
69
+ - **Auto-Capture** — After every AI turn, the last user/assistant exchange is sent to MemoryStack for extraction and long-term storage.
70
+
71
+ Everything runs in the cloud. MemoryStack handles importance scoring, contradiction detection, consolidation, and reflection on its end.
72
+
73
+ ## AI Tools
74
+
75
+ The AI can use these tools autonomously during conversations:
76
+
77
+ | Tool | Description |
78
+ |------|-------------|
79
+ | `memorystack_search` | Search memories by query with semantic search |
80
+ | `memorystack_add` | Save information to long-term memory |
81
+ | `memorystack_stats` | View memory usage statistics |
82
+
83
+ ## Features
84
+
85
+ - **Hybrid Search** - Vector similarity + text search with RRF ranking
86
+ - **Importance Scoring** - Automatic ranking of what matters
87
+ - **Contradiction Detection** - Resolves conflicts and updates beliefs
88
+ - **Memory Consolidation** - Deduplication and merging (runs server-side)
89
+ - **Temporal Decay** - Simulates natural forgetting
90
+ - **Reflection** - Discovers patterns and generates insights
91
+
92
+ ## Links
93
+
94
+ - 🌐 **Website**: https://memorystack.app
95
+ - 📚 **Documentation**: https://memorystack.app/docs
96
+ - 🚀 **Quick Start**: https://memorystack.app/docs/quickstart
97
+ - 🔑 **Get API Key**: https://memorystack.app/dashboard/api-keys
98
+ - 💰 **Pricing**: https://memorystack.app/pricing
99
+
100
+ ## License
101
+
102
+ MIT
@@ -0,0 +1,66 @@
1
+ {
2
+ "id": "clawdbot-memorystack",
3
+ "kind": "memory",
4
+ "uiHints": {
5
+ "apiKey": {
6
+ "label": "MemoryStack API Key",
7
+ "sensitive": true,
8
+ "placeholder": "ms_proj_...",
9
+ "help": "Your API key from memorystack.app/dashboard/api-keys (or use ${MEMORYSTACK_API_KEY})"
10
+ },
11
+ "baseUrl": {
12
+ "label": "API Base URL",
13
+ "placeholder": "https://memorystack.app",
14
+ "help": "MemoryStack API endpoint (default: https://memorystack.app)",
15
+ "advanced": true
16
+ },
17
+ "autoRecall": {
18
+ "label": "Auto-Recall",
19
+ "help": "Inject relevant memories before every AI turn"
20
+ },
21
+ "autoCapture": {
22
+ "label": "Auto-Capture",
23
+ "help": "Automatically store important information from conversations"
24
+ },
25
+ "maxRecallResults": {
26
+ "label": "Max Recall Results",
27
+ "placeholder": "5",
28
+ "help": "Maximum memories injected into context per turn",
29
+ "advanced": true
30
+ },
31
+ "debug": {
32
+ "label": "Debug Logging",
33
+ "help": "Enable verbose debug logs for API calls and responses",
34
+ "advanced": true
35
+ }
36
+ },
37
+ "configSchema": {
38
+ "type": "object",
39
+ "additionalProperties": false,
40
+ "properties": {
41
+ "apiKey": {
42
+ "type": "string"
43
+ },
44
+ "baseUrl": {
45
+ "type": "string"
46
+ },
47
+ "autoRecall": {
48
+ "type": "boolean"
49
+ },
50
+ "autoCapture": {
51
+ "type": "boolean"
52
+ },
53
+ "maxRecallResults": {
54
+ "type": "number",
55
+ "minimum": 1,
56
+ "maximum": 20
57
+ },
58
+ "debug": {
59
+ "type": "boolean"
60
+ }
61
+ },
62
+ "required": [
63
+ "apiKey"
64
+ ]
65
+ }
66
+ }
package/config.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { Type } from "@sinclair/typebox";
2
+
3
+ export const memorystackConfigSchema = Type.Object({
4
+ apiKey: Type.String(),
5
+ baseUrl: Type.Optional(Type.String()),
6
+ autoRecall: Type.Optional(Type.Boolean()),
7
+ autoCapture: Type.Optional(Type.Boolean()),
8
+ maxRecallResults: Type.Optional(Type.Number({ minimum: 1, maximum: 20 })),
9
+ debug: Type.Optional(Type.Boolean()),
10
+ });
11
+
12
+ export type MemorystackConfig = {
13
+ apiKey: string;
14
+ baseUrl: string;
15
+ autoRecall: boolean;
16
+ autoCapture: boolean;
17
+ maxRecallResults: number;
18
+ debug: boolean;
19
+ };
20
+
21
+ export function parseConfig(rawConfig: unknown): MemorystackConfig {
22
+ const cfg = rawConfig as Partial<MemorystackConfig>;
23
+
24
+ return {
25
+ apiKey: cfg.apiKey || process.env.MEMORYSTACK_API_KEY || "",
26
+ baseUrl: cfg.baseUrl || "https://memorystack.app",
27
+ autoRecall: cfg.autoRecall ?? true,
28
+ autoCapture: cfg.autoCapture ?? true,
29
+ maxRecallResults: cfg.maxRecallResults ?? 5,
30
+ debug: cfg.debug ?? false,
31
+ };
32
+ }
@@ -0,0 +1,96 @@
1
+ import { MemoryStackClient } from "@memorystack/sdk";
2
+ import type { MemorystackConfig } from "../config.ts";
3
+ import { log } from "../logger.ts";
4
+
5
+ function getLastTurn(messages: unknown[]): unknown[] {
6
+ let lastUserIdx = -1;
7
+ for (let i = messages.length - 1; i >= 0; i--) {
8
+ const msg = messages[i];
9
+ if (
10
+ msg &&
11
+ typeof msg === "object" &&
12
+ (msg as Record<string, unknown>).role === "user"
13
+ ) {
14
+ lastUserIdx = i;
15
+ break;
16
+ }
17
+ }
18
+ return lastUserIdx >= 0 ? messages.slice(lastUserIdx) : messages;
19
+ }
20
+
21
+ export function buildCaptureHandler(cfg: MemorystackConfig) {
22
+ return async (event: Record<string, unknown>) => {
23
+ if (
24
+ !event.success ||
25
+ !Array.isArray(event.messages) ||
26
+ event.messages.length === 0
27
+ )
28
+ return;
29
+
30
+ const lastTurn = getLastTurn(event.messages);
31
+
32
+ const texts: string[] = [];
33
+ for (const msg of lastTurn) {
34
+ if (!msg || typeof msg !== "object") continue;
35
+ const msgObj = msg as Record<string, unknown>;
36
+ const role = msgObj.role;
37
+ if (role !== "user" && role !== "assistant") continue;
38
+
39
+ const content = msgObj.content;
40
+
41
+ const parts: string[] = [];
42
+
43
+ if (typeof content === "string") {
44
+ parts.push(content);
45
+ } else if (Array.isArray(content)) {
46
+ for (const block of content) {
47
+ if (!block || typeof block !== "object") continue;
48
+ const b = block as Record<string, unknown>;
49
+ if (b.type === "text" && typeof b.text === "string") {
50
+ parts.push(b.text);
51
+ }
52
+ }
53
+ }
54
+
55
+ if (parts.length > 0) {
56
+ texts.push(`[role: ${role}]\n${parts.join("\n")}\n[${role}:end]`);
57
+ }
58
+ }
59
+
60
+ // Filter out injected context and short messages
61
+ const captured = texts
62
+ .map((t) =>
63
+ t
64
+ .replace(
65
+ /<memorystack-context>[\s\S]*?<\/memorystack-context>\s*/g,
66
+ "",
67
+ )
68
+ .trim(),
69
+ )
70
+ .filter((t) => t.length >= 10 && t.length <= 500);
71
+
72
+ if (captured.length === 0) return;
73
+
74
+ const content = captured.join("\n\n");
75
+
76
+ log.debug(
77
+ `capturing ${captured.length} texts (${content.length} chars)`,
78
+ );
79
+
80
+ try {
81
+ const client = new MemoryStackClient({
82
+ apiKey: cfg.apiKey,
83
+ baseUrl: cfg.baseUrl,
84
+ enableLogging: cfg.debug,
85
+ });
86
+
87
+ await client.add(content, {
88
+ metadata: { source: "clawdbot_auto_capture", timestamp: new Date().toISOString() },
89
+ });
90
+
91
+ log.debug(`captured ${captured.length} messages successfully`);
92
+ } catch (err) {
93
+ log.error("capture failed", err);
94
+ }
95
+ };
96
+ }
@@ -0,0 +1,104 @@
1
+ import { MemoryStackClient } from "@memorystack/sdk";
2
+ import type { MemorystackConfig } from "../config.ts";
3
+ import { log } from "../logger.ts";
4
+
5
+ const MAX_CONTEXT_CHARS = 2000;
6
+
7
+ interface MemoryResult {
8
+ content: string;
9
+ memory_type?: string;
10
+ confidence?: number;
11
+ created_at?: string;
12
+ }
13
+
14
+ function formatGroupedMemories(memories: MemoryResult[]): string {
15
+ // Group by memory type
16
+ const groups: Record<string, MemoryResult[]> = {};
17
+ const ungrouped: MemoryResult[] = [];
18
+
19
+ for (const m of memories) {
20
+ const type = m.memory_type || "other";
21
+ if (type === "other") {
22
+ ungrouped.push(m);
23
+ } else {
24
+ groups[type] = groups[type] || [];
25
+ groups[type].push(m);
26
+ }
27
+ }
28
+
29
+ const sections: string[] = [];
30
+ const typeLabels: Record<string, string> = {
31
+ fact: "📋 Facts",
32
+ preference: "💜 Preferences",
33
+ episode: "📅 Recent Context",
34
+ procedure: "🔧 Procedures",
35
+ belief: "💭 Beliefs",
36
+ };
37
+
38
+ // Format grouped memories
39
+ for (const [type, items] of Object.entries(groups)) {
40
+ const label = typeLabels[type] || type.charAt(0).toUpperCase() + type.slice(1);
41
+ const lines = items.map((m) => {
42
+ const conf = m.confidence && m.confidence >= 0.8 ? ` (${Math.round(m.confidence * 100)}%)` : "";
43
+ return `- ${m.content}${conf}`;
44
+ });
45
+ sections.push(`## ${label}\n${lines.join("\n")}`);
46
+ }
47
+
48
+ // Add ungrouped at end
49
+ if (ungrouped.length > 0) {
50
+ const lines = ungrouped.map((m) => `- ${m.content}`);
51
+ sections.push(`## Other\n${lines.join("\n")}`);
52
+ }
53
+
54
+ return sections.join("\n\n");
55
+ }
56
+
57
+ export function buildRecallHandler(cfg: MemorystackConfig) {
58
+ return async (event: Record<string, unknown>) => {
59
+ const prompt = event.prompt as string | undefined;
60
+ if (!prompt || prompt.length < 10) return;
61
+
62
+ log.debug(`recalling for prompt (${prompt.length} chars)`);
63
+
64
+ try {
65
+ const client = new MemoryStackClient({
66
+ apiKey: cfg.apiKey,
67
+ baseUrl: cfg.baseUrl,
68
+ enableLogging: cfg.debug,
69
+ });
70
+
71
+ const results = await client.search(prompt, {
72
+ limit: cfg.maxRecallResults,
73
+ });
74
+
75
+ if (results.count === 0) {
76
+ log.debug("no memories to inject");
77
+ return;
78
+ }
79
+
80
+ const formattedMemories = formatGroupedMemories(results.results);
81
+
82
+ // Truncate if too long
83
+ const truncated = formattedMemories.length > MAX_CONTEXT_CHARS
84
+ ? formattedMemories.slice(0, MAX_CONTEXT_CHARS) + "\n...(truncated)"
85
+ : formattedMemories;
86
+
87
+ const context = `<memorystack-context>
88
+ The following is recalled context about the user. Reference it only when relevant.
89
+
90
+ ${truncated}
91
+
92
+ Use these memories naturally when relevant — including indirect connections — but don't force them into every response or make assumptions beyond what's stated.
93
+ </memorystack-context>`;
94
+
95
+ log.debug(`injecting ${results.count} memories (${context.length} chars)`);
96
+
97
+ return { prependContext: context };
98
+ } catch (err) {
99
+ log.error("recall failed", err);
100
+ return;
101
+ }
102
+ };
103
+ }
104
+
package/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
2
+ import { parseConfig, memorystackConfigSchema } from "./config.ts";
3
+ import { initLogger } from "./logger.ts";
4
+ import { registerSearchTool } from "./tools/search.ts";
5
+ import { registerAddTool } from "./tools/add.ts";
6
+ import { registerStatsTool } from "./tools/stats.ts";
7
+ import { buildRecallHandler } from "./hooks/recall.ts";
8
+ import { buildCaptureHandler } from "./hooks/capture.ts";
9
+
10
+ export default {
11
+ id: "clawdbot-memorystack",
12
+ name: "MemoryStack",
13
+ description: "Clawdbot powered by MemoryStack plugin",
14
+ kind: "memory" as const,
15
+ configSchema: memorystackConfigSchema,
16
+
17
+ register(api: ClawdbotPluginApi) {
18
+ const cfg = parseConfig(api.pluginConfig);
19
+
20
+ if (!cfg.apiKey) {
21
+ api.logger.error("memorystack: API key is required. Set MEMORYSTACK_API_KEY or configure in plugin config.");
22
+ return;
23
+ }
24
+
25
+ initLogger(api.logger, cfg.debug);
26
+
27
+ // Register tools
28
+ registerSearchTool(api, cfg);
29
+ registerAddTool(api, cfg);
30
+ registerStatsTool(api, cfg);
31
+
32
+ // Auto-recall hook
33
+ if (cfg.autoRecall) {
34
+ const recallHandler = buildRecallHandler(cfg);
35
+ api.on("before_agent_start", recallHandler);
36
+ }
37
+
38
+ // Auto-capture hook
39
+ if (cfg.autoCapture) {
40
+ api.on("agent_end", buildCaptureHandler(cfg));
41
+ }
42
+
43
+ // Register service
44
+ api.registerService({
45
+ id: "clawdbot-memorystack",
46
+ start: () => {
47
+ api.logger.info("memorystack: connected");
48
+ },
49
+ stop: () => {
50
+ api.logger.info("memorystack: stopped");
51
+ },
52
+ });
53
+ },
54
+ };
package/logger.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { Logger } from "clawdbot/plugin-sdk";
2
+
3
+ let logger: Logger;
4
+ let debugEnabled = false;
5
+
6
+ export function initLogger(l: Logger, debug: boolean): void {
7
+ logger = l;
8
+ debugEnabled = debug;
9
+ }
10
+
11
+ export const log = {
12
+ info: (msg: string, ...args: unknown[]) => {
13
+ logger?.info(`memorystack: ${msg}`, ...args);
14
+ },
15
+ warn: (msg: string, ...args: unknown[]) => {
16
+ logger?.warn(`memorystack: ${msg}`, ...args);
17
+ },
18
+ error: (msg: string, err?: unknown) => {
19
+ logger?.error(`memorystack: ${msg}`, err);
20
+ },
21
+ debug: (msg: string, data?: unknown) => {
22
+ if (debugEnabled) {
23
+ logger?.debug?.(`memorystack: ${msg}`, data);
24
+ }
25
+ },
26
+ debugRequest: (method: string, params: unknown) => {
27
+ if (debugEnabled) {
28
+ logger?.debug?.(`memorystack → ${method}`, params);
29
+ }
30
+ },
31
+ debugResponse: (method: string, result: unknown) => {
32
+ if (debugEnabled) {
33
+ logger?.debug?.(`memorystack ← ${method}`, result);
34
+ }
35
+ },
36
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@memorystack/clawdbot-memorystack",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Clawdbot MemoryStack memory plugin - long-term memory for your AI assistant",
6
+ "license": "MIT",
7
+ "author": "MemoryStack <support@memorystack.app>",
8
+ "homepage": "https://memorystack.app",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/memorystack-labs/memorystack"
12
+ },
13
+ "keywords": [
14
+ "clawdbot",
15
+ "moltbot",
16
+ "memory",
17
+ "memorystack",
18
+ "ai",
19
+ "long-term-memory"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "dependencies": {
25
+ "@memorystack/sdk": "^1.0.3",
26
+ "@sinclair/typebox": "0.34.47"
27
+ },
28
+ "peerDependencies": {
29
+ "clawdbot": ">=2026.1.24"
30
+ },
31
+ "clawdbot": {
32
+ "extensions": [
33
+ "./index.ts"
34
+ ]
35
+ },
36
+ "devDependencies": {
37
+ "typescript": "^5.9.3"
38
+ }
39
+ }
package/tools/add.ts ADDED
@@ -0,0 +1,58 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
3
+ import { MemoryStackClient } from "@memorystack/sdk";
4
+ import type { MemorystackConfig } from "../config.ts";
5
+ import { log } from "../logger.ts";
6
+
7
+ export function registerAddTool(
8
+ api: ClawdbotPluginApi,
9
+ cfg: MemorystackConfig,
10
+ ): void {
11
+ api.registerTool(
12
+ {
13
+ name: "memorystack_add",
14
+ label: "Memory Add",
15
+ description: "Save important information to long-term memory with automatic importance scoring.",
16
+ parameters: Type.Object({
17
+ text: Type.String({ description: "Information to remember" }),
18
+ userId: Type.Optional(Type.String({ description: "User ID (for B2B apps)" })),
19
+ }),
20
+ async execute(
21
+ _toolCallId: string,
22
+ params: { text: string; userId?: string },
23
+ ) {
24
+ log.debugRequest("add", { textLength: params.text.length, userId: params.userId });
25
+
26
+ const client = new MemoryStackClient({
27
+ apiKey: cfg.apiKey,
28
+ baseUrl: cfg.baseUrl,
29
+ enableLogging: cfg.debug,
30
+ });
31
+
32
+ const result = await client.add(params.text, {
33
+ userId: params.userId,
34
+ metadata: { source: "clawdbot_tool" },
35
+ });
36
+
37
+ log.debugResponse("add", {
38
+ memories_created: result.memories_created,
39
+ memory_ids: result.memory_ids,
40
+ });
41
+
42
+ const preview =
43
+ params.text.length > 80 ? `${params.text.slice(0, 80)}…` : params.text;
44
+
45
+ return {
46
+ content: [
47
+ {
48
+ type: "text" as const,
49
+ text: `Stored: "${preview}"\nCreated ${result.memories_created} memory (IDs: ${result.memory_ids.join(", ")})`,
50
+ },
51
+ ],
52
+ details: result,
53
+ };
54
+ },
55
+ },
56
+ { name: "memorystack_add" },
57
+ );
58
+ }
@@ -0,0 +1,107 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
3
+ import { MemoryStackClient } from "@memorystack/sdk";
4
+ import type { MemorystackConfig } from "../config.ts";
5
+ import { log } from "../logger.ts";
6
+
7
+ export function registerSearchTool(
8
+ api: ClawdbotPluginApi,
9
+ cfg: MemorystackConfig,
10
+ ): void {
11
+ api.registerTool(
12
+ {
13
+ name: "memorystack_search",
14
+ label: "Memory Search",
15
+ description:
16
+ "Search through long-term memories for relevant information using semantic search. Supports filtering by memory type, confidence, and recency.",
17
+ parameters: Type.Object({
18
+ query: Type.String({ description: "Search query" }),
19
+ limit: Type.Optional(
20
+ Type.Number({ description: "Max results (default: 5)" }),
21
+ ),
22
+ memory_type: Type.Optional(
23
+ Type.String({
24
+ description:
25
+ "Filter by type: fact, preference, episode, procedure, belief",
26
+ }),
27
+ ),
28
+ min_confidence: Type.Optional(
29
+ Type.Number({
30
+ description: "Minimum confidence score 0-1 (default: 0)",
31
+ }),
32
+ ),
33
+ days_ago: Type.Optional(
34
+ Type.Number({
35
+ description: "Only memories from last N days",
36
+ }),
37
+ ),
38
+ }),
39
+ async execute(
40
+ _toolCallId: string,
41
+ params: {
42
+ query: string;
43
+ limit?: number;
44
+ memory_type?: string;
45
+ min_confidence?: number;
46
+ days_ago?: number;
47
+ },
48
+ ) {
49
+ const limit = params.limit ?? 5;
50
+ log.debugRequest("search", { query: params.query, limit, ...params });
51
+
52
+ const client = new MemoryStackClient({
53
+ apiKey: cfg.apiKey,
54
+ baseUrl: cfg.baseUrl,
55
+ enableLogging: cfg.debug,
56
+ });
57
+
58
+ const searchOpts: Record<string, unknown> = { limit };
59
+ if (params.memory_type) {
60
+ searchOpts.memory_type = params.memory_type;
61
+ }
62
+
63
+ const results = await client.search(params.query, searchOpts);
64
+
65
+ if (results.count === 0) {
66
+ return {
67
+ content: [
68
+ { type: "text" as const, text: "No relevant memories found." },
69
+ ],
70
+ };
71
+ }
72
+
73
+ const text = results.results
74
+ .map((r, i) => {
75
+ const type = r.memory_type ? ` [${r.memory_type}]` : "";
76
+ const conf = r.confidence
77
+ ? ` (${(r.confidence * 100).toFixed(0)}%)`
78
+ : "";
79
+ return `${i + 1}. ${r.content}${type}${conf}`;
80
+ })
81
+ .join("\n");
82
+
83
+ log.debugResponse("search", { count: results.count });
84
+
85
+ return {
86
+ content: [
87
+ {
88
+ type: "text" as const,
89
+ text: `Found ${results.count} memories:\n\n${text}`,
90
+ },
91
+ ],
92
+ details: {
93
+ count: results.count,
94
+ mode: results.mode,
95
+ memories: results.results.map((r) => ({
96
+ id: r.id,
97
+ content: r.content,
98
+ memory_type: r.memory_type,
99
+ confidence: r.confidence,
100
+ })),
101
+ },
102
+ };
103
+ },
104
+ },
105
+ { name: "memorystack_search" },
106
+ );
107
+ }
package/tools/stats.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
3
+ import { MemoryStackClient } from "@memorystack/sdk";
4
+ import type { MemorystackConfig } from "../config.ts";
5
+ import { log } from "../logger.ts";
6
+
7
+ export function registerStatsTool(
8
+ api: ClawdbotPluginApi,
9
+ cfg: MemorystackConfig,
10
+ ): void {
11
+ api.registerTool(
12
+ {
13
+ name: "memorystack_stats",
14
+ label: "Memory Stats",
15
+ description: "Get statistics about memory usage and API calls.",
16
+ parameters: Type.Object({}),
17
+ async execute() {
18
+ log.debugRequest("stats", {});
19
+
20
+ const client = new MemoryStackClient({
21
+ apiKey: cfg.apiKey,
22
+ baseUrl: cfg.baseUrl,
23
+ enableLogging: cfg.debug,
24
+ });
25
+
26
+ const stats = await client.getStats();
27
+
28
+ log.debugResponse("stats", {
29
+ total_memories: stats.totals.total_memories,
30
+ api_calls: stats.usage.current_month_api_calls,
31
+ });
32
+
33
+ const text = [
34
+ `**Total Memories:** ${stats.totals.total_memories}`,
35
+ `**API Calls:** ${stats.usage.current_month_api_calls} / ${stats.usage.monthly_api_limit}`,
36
+ `**Plan:** ${stats.plan_tier || "Free"}`,
37
+ `**Storage:** ${(stats.storage.total_storage_bytes / 1024 / 1024).toFixed(2)} MB`,
38
+ ].join("\n");
39
+
40
+ return {
41
+ content: [
42
+ {
43
+ type: "text" as const,
44
+ text: `# MemoryStack Stats\n\n${text}`,
45
+ },
46
+ ],
47
+ details: stats,
48
+ };
49
+ },
50
+ },
51
+ { name: "memorystack_stats" },
52
+ );
53
+ }