@stevederico/dotbot 0.16.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/CHANGELOG.md +136 -0
- package/README.md +380 -0
- package/bin/dotbot.js +461 -0
- package/core/agent.js +779 -0
- package/core/compaction.js +261 -0
- package/core/cron_handler.js +262 -0
- package/core/events.js +229 -0
- package/core/failover.js +193 -0
- package/core/gptoss_tool_parser.js +173 -0
- package/core/init.js +154 -0
- package/core/normalize.js +324 -0
- package/core/trigger_handler.js +148 -0
- package/docs/core.md +103 -0
- package/docs/protected-files.md +59 -0
- package/examples/sqlite-session-example.js +69 -0
- package/index.js +341 -0
- package/observer/index.js +164 -0
- package/package.json +42 -0
- package/storage/CronStore.js +145 -0
- package/storage/EventStore.js +71 -0
- package/storage/MemoryStore.js +175 -0
- package/storage/MongoAdapter.js +291 -0
- package/storage/MongoCronAdapter.js +347 -0
- package/storage/MongoTaskAdapter.js +242 -0
- package/storage/MongoTriggerAdapter.js +158 -0
- package/storage/SQLiteAdapter.js +382 -0
- package/storage/SQLiteCronAdapter.js +562 -0
- package/storage/SQLiteEventStore.js +300 -0
- package/storage/SQLiteMemoryAdapter.js +240 -0
- package/storage/SQLiteTaskAdapter.js +419 -0
- package/storage/SQLiteTriggerAdapter.js +262 -0
- package/storage/SessionStore.js +149 -0
- package/storage/TaskStore.js +100 -0
- package/storage/TriggerStore.js +90 -0
- package/storage/cron_constants.js +48 -0
- package/storage/index.js +21 -0
- package/tools/appgen.js +311 -0
- package/tools/browser.js +634 -0
- package/tools/code.js +101 -0
- package/tools/events.js +145 -0
- package/tools/files.js +201 -0
- package/tools/images.js +253 -0
- package/tools/index.js +97 -0
- package/tools/jobs.js +159 -0
- package/tools/memory.js +332 -0
- package/tools/messages.js +135 -0
- package/tools/notify.js +42 -0
- package/tools/tasks.js +404 -0
- package/tools/triggers.js +159 -0
- package/tools/weather.js +82 -0
- package/tools/web.js +283 -0
- package/utils/providers.js +136 -0
package/tools/index.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry
|
|
3
|
+
*
|
|
4
|
+
* Central registry for agent tools with dynamic registration API.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { memoryTools } from './memory.js';
|
|
8
|
+
import { webTools } from './web.js';
|
|
9
|
+
import { codeTools } from './code.js';
|
|
10
|
+
import { fileTools } from './files.js';
|
|
11
|
+
import { messageTools } from './messages.js';
|
|
12
|
+
import { imageTools } from './images.js';
|
|
13
|
+
import { weatherTools } from './weather.js';
|
|
14
|
+
import { notifyTools } from './notify.js';
|
|
15
|
+
import { browserTools, createBrowserTools } from './browser.js';
|
|
16
|
+
import { taskTools, goalTools } from './tasks.js';
|
|
17
|
+
import { triggerTools } from './triggers.js';
|
|
18
|
+
import { jobTools, cronTools } from './jobs.js';
|
|
19
|
+
import { eventTools } from './events.js';
|
|
20
|
+
import { appgenTools } from './appgen.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Core tools included in the library by default
|
|
24
|
+
*/
|
|
25
|
+
export const coreTools = [
|
|
26
|
+
...memoryTools,
|
|
27
|
+
...webTools,
|
|
28
|
+
...codeTools,
|
|
29
|
+
...fileTools,
|
|
30
|
+
...messageTools,
|
|
31
|
+
...imageTools,
|
|
32
|
+
...weatherTools,
|
|
33
|
+
...notifyTools,
|
|
34
|
+
...browserTools,
|
|
35
|
+
...taskTools,
|
|
36
|
+
...triggerTools,
|
|
37
|
+
...jobTools,
|
|
38
|
+
...eventTools,
|
|
39
|
+
...appgenTools,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a tool registry with dynamic registration
|
|
44
|
+
*
|
|
45
|
+
* @returns {Object} Tool registry with register/getAll methods
|
|
46
|
+
*/
|
|
47
|
+
export function createToolRegistry() {
|
|
48
|
+
const tools = [];
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
/**
|
|
52
|
+
* Register one or more tools
|
|
53
|
+
*
|
|
54
|
+
* @param {...Object} newTools - Tool definitions to register
|
|
55
|
+
*/
|
|
56
|
+
register(...newTools) {
|
|
57
|
+
tools.push(...newTools);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get all registered tools
|
|
62
|
+
*
|
|
63
|
+
* @returns {Array} All registered tools
|
|
64
|
+
*/
|
|
65
|
+
getAll() {
|
|
66
|
+
return tools;
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Clear all registered tools
|
|
71
|
+
*/
|
|
72
|
+
clear() {
|
|
73
|
+
tools.length = 0;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Re-export individual tool arrays for direct access
|
|
79
|
+
export {
|
|
80
|
+
memoryTools,
|
|
81
|
+
webTools,
|
|
82
|
+
codeTools,
|
|
83
|
+
fileTools,
|
|
84
|
+
messageTools,
|
|
85
|
+
imageTools,
|
|
86
|
+
weatherTools,
|
|
87
|
+
notifyTools,
|
|
88
|
+
browserTools,
|
|
89
|
+
createBrowserTools,
|
|
90
|
+
taskTools,
|
|
91
|
+
goalTools, // backwards compatibility alias
|
|
92
|
+
triggerTools,
|
|
93
|
+
jobTools,
|
|
94
|
+
cronTools, // backwards compatibility alias
|
|
95
|
+
eventTools,
|
|
96
|
+
appgenTools,
|
|
97
|
+
};
|
package/tools/jobs.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job Tools
|
|
3
|
+
*
|
|
4
|
+
* Agent tools for scheduling and managing jobs (time-triggered scheduled prompts).
|
|
5
|
+
* Each tool receives (input, signal, context) where context.cronStore
|
|
6
|
+
* provides the storage backend.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { parseInterval } from '../storage/cron_constants.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Agent tools for scheduling and managing jobs
|
|
13
|
+
* @type {Array<{name: string, description: string, parameters: Object, execute: Function}>}
|
|
14
|
+
*/
|
|
15
|
+
export const jobTools = [
|
|
16
|
+
{
|
|
17
|
+
name: "schedule_job",
|
|
18
|
+
description:
|
|
19
|
+
"Schedule a job to run later or on a recurring basis. The job will send a message to you (the agent) at the scheduled time, and you will process it like a normal user message. Use this for reminders, periodic checks, daily summaries, etc.",
|
|
20
|
+
parameters: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
name: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Short name for the job. e.g. 'daily-news-summary', 'remind-standup'",
|
|
26
|
+
},
|
|
27
|
+
prompt: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description:
|
|
30
|
+
"The message that will be sent to you when the job fires. Write it as if the user is asking you to do something. e.g. 'Give me a summary of today\\'s top tech news' or 'Remind me about the standup meeting in 10 minutes'",
|
|
31
|
+
},
|
|
32
|
+
run_at: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description:
|
|
35
|
+
"When to run. ISO 8601 datetime string. e.g. '2025-01-15T09:00:00' for a specific time.",
|
|
36
|
+
},
|
|
37
|
+
interval: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description:
|
|
40
|
+
"For recurring jobs: interval between runs. e.g. '30m' (30 minutes), '2h' (2 hours), '1d' (daily), '1w' (weekly). Omit for one-shot jobs.",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["name", "prompt", "run_at"],
|
|
44
|
+
},
|
|
45
|
+
execute: async (input, signal, context) => {
|
|
46
|
+
if (!context?.cronStore) return "Error: cron store not available";
|
|
47
|
+
try {
|
|
48
|
+
const { sessionId, userID } = context || {};
|
|
49
|
+
const intervalMs = input.interval ? parseInterval(input.interval) : null;
|
|
50
|
+
const recurring = !!intervalMs;
|
|
51
|
+
|
|
52
|
+
const task = await context.cronStore.createTask({
|
|
53
|
+
name: input.name,
|
|
54
|
+
prompt: input.prompt,
|
|
55
|
+
sessionId: sessionId || 'default',
|
|
56
|
+
userId: userID,
|
|
57
|
+
runAt: input.run_at,
|
|
58
|
+
intervalMs,
|
|
59
|
+
recurring,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const desc = recurring
|
|
63
|
+
? `Recurring job "${input.name}" scheduled starting ${input.run_at}, repeating every ${input.interval}`
|
|
64
|
+
: `One-time job "${input.name}" scheduled for ${input.run_at}`;
|
|
65
|
+
|
|
66
|
+
return desc;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
return `Error scheduling job: ${err.message}`;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
name: "list_jobs",
|
|
75
|
+
description: "List all scheduled jobs (active and completed).",
|
|
76
|
+
parameters: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {},
|
|
79
|
+
},
|
|
80
|
+
execute: async (input, signal, context) => {
|
|
81
|
+
if (!context?.cronStore) return "Error: cron store not available";
|
|
82
|
+
try {
|
|
83
|
+
const tasks = await context.cronStore.listTasks();
|
|
84
|
+
|
|
85
|
+
if (tasks.length === 0) {
|
|
86
|
+
return "No scheduled jobs.";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return tasks
|
|
90
|
+
.map(
|
|
91
|
+
(t) =>
|
|
92
|
+
`- ${t.name} [${t.enabled ? "active" : "done"}]` +
|
|
93
|
+
`\n prompt: "${t.prompt}"` +
|
|
94
|
+
`\n next: ${t.nextRunAt?.toISOString?.() || t.nextRunAt || "n/a"}` +
|
|
95
|
+
(t.recurring ? `\n repeats every ${t.intervalMs / 60000}m` : "") +
|
|
96
|
+
(t.lastRunAt ? `\n last ran: ${t.lastRunAt.toISOString?.() || t.lastRunAt}` : "")
|
|
97
|
+
)
|
|
98
|
+
.join("\n\n");
|
|
99
|
+
} catch (err) {
|
|
100
|
+
return `Error listing jobs: ${err.message}`;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
{
|
|
106
|
+
name: "toggle_job",
|
|
107
|
+
description: "Enable or disable a scheduled job without deleting it.",
|
|
108
|
+
parameters: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {
|
|
111
|
+
job_id: { type: "string", description: "The job ID to toggle" },
|
|
112
|
+
enabled: { type: "boolean", description: "true to enable, false to disable" },
|
|
113
|
+
},
|
|
114
|
+
required: ["job_id", "enabled"],
|
|
115
|
+
},
|
|
116
|
+
execute: async (input, signal, context) => {
|
|
117
|
+
if (!context?.cronStore) return "Error: cron store not available";
|
|
118
|
+
try {
|
|
119
|
+
const task = await context.cronStore.getTask(input.job_id);
|
|
120
|
+
if (!task) return `Error: job ${input.job_id} not found.`;
|
|
121
|
+
if (task.name === 'heartbeat') return "Error: cannot modify system jobs.";
|
|
122
|
+
await context.cronStore.toggleTask(input.job_id, input.enabled);
|
|
123
|
+
return `Job ${input.job_id} ${input.enabled ? 'enabled' : 'disabled'}.`;
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return `Error toggling job: ${err.message}`;
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
name: "cancel_job",
|
|
132
|
+
description: "Cancel/delete a scheduled job by its ID.",
|
|
133
|
+
parameters: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {
|
|
136
|
+
job_id: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "The job ID to cancel",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
required: ["job_id"],
|
|
142
|
+
},
|
|
143
|
+
execute: async (input, signal, context) => {
|
|
144
|
+
if (!context?.cronStore) return "Error: cron store not available";
|
|
145
|
+
try {
|
|
146
|
+
const task = await context.cronStore.getTask(input.job_id);
|
|
147
|
+
if (!task) return `Error: job ${input.job_id} not found.`;
|
|
148
|
+
if (task.name === 'heartbeat') return "Error: cannot delete system jobs.";
|
|
149
|
+
await context.cronStore.deleteTask(input.job_id);
|
|
150
|
+
return `Job ${input.job_id} cancelled.`;
|
|
151
|
+
} catch (err) {
|
|
152
|
+
return `Error cancelling job: ${err.message}`;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
// Backwards compatibility alias
|
|
159
|
+
export const cronTools = jobTools;
|
package/tools/memory.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// agent/memory.js
|
|
2
|
+
// Agent memory tools that write to the shared Memory collection via memoryStore.
|
|
3
|
+
// Uses SQLiteMemoryStore or any compatible store implementation.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate a slug-style key from content text.
|
|
7
|
+
* Takes the first ~50 characters, strips non-alphanumeric chars, and joins with underscores.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} content - Raw content string
|
|
10
|
+
* @returns {string} Cleaned key like "users_favorite_color_is_blue"
|
|
11
|
+
*/
|
|
12
|
+
function generateMemoryKey(content) {
|
|
13
|
+
return content
|
|
14
|
+
.slice(0, 50)
|
|
15
|
+
.replace(/[^a-zA-Z0-9 ]/g, "")
|
|
16
|
+
.trim()
|
|
17
|
+
.replace(/\s+/g, "_")
|
|
18
|
+
.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ── Tool definitions for the agent ──
|
|
22
|
+
|
|
23
|
+
export const memoryTools = [
|
|
24
|
+
{
|
|
25
|
+
name: "memory_save",
|
|
26
|
+
description:
|
|
27
|
+
"Save an important fact, preference, or piece of information to long-term memory. Use this when the user tells you something worth remembering for future conversations — their name, preferences, projects, goals, important dates, etc. Be selective: only save things that would be useful to recall later.",
|
|
28
|
+
parameters: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
content: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description:
|
|
34
|
+
"The information to remember. Write it as a clear, standalone fact. e.g. 'User's name is Steve. He is a full-stack developer based in SF.'",
|
|
35
|
+
},
|
|
36
|
+
tags: {
|
|
37
|
+
type: "array",
|
|
38
|
+
items: { type: "string" },
|
|
39
|
+
description:
|
|
40
|
+
"Short tags for categorization. e.g. ['personal', 'name'] or ['project', 'dottie']",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["content"],
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Save a memory via memoryStore.writeMemory.
|
|
47
|
+
*
|
|
48
|
+
* @param {Object} input - Tool input with content and optional tags
|
|
49
|
+
* @param {AbortSignal} signal - Abort signal
|
|
50
|
+
* @param {Object} context - { userID, memoryStore }
|
|
51
|
+
* @returns {Promise<string>} Confirmation message
|
|
52
|
+
*/
|
|
53
|
+
execute: async (input, signal, context) => {
|
|
54
|
+
try {
|
|
55
|
+
const { userID, memoryStore } = context;
|
|
56
|
+
if (!memoryStore) {
|
|
57
|
+
return "Error: memoryStore not configured. Memory features are disabled.";
|
|
58
|
+
}
|
|
59
|
+
const key = generateMemoryKey(input.content);
|
|
60
|
+
const value = {
|
|
61
|
+
content: input.content,
|
|
62
|
+
tags: input.tags || [],
|
|
63
|
+
source: "agent",
|
|
64
|
+
};
|
|
65
|
+
await memoryStore.writeMemory(userID, key, value, "agent");
|
|
66
|
+
return `Saved to memory: "${input.content}"`;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
return `Error saving memory: ${err.message}`;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
name: "memory_search",
|
|
75
|
+
description:
|
|
76
|
+
"Search long-term memory for previously saved information. Use this when the user references something from a past conversation, asks 'do you remember...', or when context from past interactions would help you give a better answer.",
|
|
77
|
+
parameters: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
query: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description:
|
|
83
|
+
"What to search for in memory. Use keywords related to the topic.",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
required: ["query"],
|
|
87
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* Search memories via memoryStore.readMemoryPattern and filter by query.
|
|
90
|
+
*
|
|
91
|
+
* @param {Object} input - Tool input with query string
|
|
92
|
+
* @param {AbortSignal} signal - Abort signal
|
|
93
|
+
* @param {Object} context - { userID, memoryStore }
|
|
94
|
+
* @returns {Promise<string>} Formatted search results or "no matches" message
|
|
95
|
+
*/
|
|
96
|
+
execute: async (input, signal, context) => {
|
|
97
|
+
try {
|
|
98
|
+
const { userID, memoryStore } = context;
|
|
99
|
+
if (!memoryStore) {
|
|
100
|
+
return "Error: memoryStore not configured. Memory features are disabled.";
|
|
101
|
+
}
|
|
102
|
+
const all = await memoryStore.readMemoryPattern(userID, ".*");
|
|
103
|
+
|
|
104
|
+
const query = input.query.toLowerCase();
|
|
105
|
+
const matches = all
|
|
106
|
+
.filter((m) => {
|
|
107
|
+
const valStr =
|
|
108
|
+
typeof m.value === "object"
|
|
109
|
+
? JSON.stringify(m.value)
|
|
110
|
+
: String(m.value);
|
|
111
|
+
return (
|
|
112
|
+
m.key.toLowerCase().includes(query) ||
|
|
113
|
+
valStr.toLowerCase().includes(query)
|
|
114
|
+
);
|
|
115
|
+
})
|
|
116
|
+
.slice(0, 5);
|
|
117
|
+
|
|
118
|
+
if (matches.length === 0) {
|
|
119
|
+
return "No matching memories found.";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return matches
|
|
123
|
+
.map((m, i) => {
|
|
124
|
+
const content =
|
|
125
|
+
typeof m.value === "object" && m.value.content
|
|
126
|
+
? m.value.content
|
|
127
|
+
: JSON.stringify(m.value);
|
|
128
|
+
const tags =
|
|
129
|
+
typeof m.value === "object" && m.value.tags?.length
|
|
130
|
+
? `\n tags: ${m.value.tags.join(", ")}`
|
|
131
|
+
: "";
|
|
132
|
+
const saved = m.updated_at
|
|
133
|
+
? `\n saved: ${new Date(m.updated_at).toISOString()}`
|
|
134
|
+
: "";
|
|
135
|
+
return `${i + 1}. ${content}${tags}${saved}`;
|
|
136
|
+
})
|
|
137
|
+
.join("\n\n");
|
|
138
|
+
} catch (err) {
|
|
139
|
+
return `Error searching memory: ${err.message}`;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
name: "memory_delete",
|
|
146
|
+
description:
|
|
147
|
+
"Delete a specific memory by its key. Use this when the user asks to forget something or remove outdated information.",
|
|
148
|
+
parameters: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
key: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "The memory key to delete. Use memory_search first to find the key.",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
required: ["key"],
|
|
157
|
+
},
|
|
158
|
+
/**
|
|
159
|
+
* Delete a memory via memoryStore.deleteMemory.
|
|
160
|
+
*
|
|
161
|
+
* @param {Object} input - Tool input with key
|
|
162
|
+
* @param {AbortSignal} signal - Abort signal
|
|
163
|
+
* @param {Object} context - { userID, memoryStore }
|
|
164
|
+
* @returns {Promise<string>} Confirmation message
|
|
165
|
+
*/
|
|
166
|
+
execute: async (input, signal, context) => {
|
|
167
|
+
try {
|
|
168
|
+
const { userID, memoryStore } = context;
|
|
169
|
+
if (!memoryStore) {
|
|
170
|
+
return "Error: memoryStore not configured. Memory features are disabled.";
|
|
171
|
+
}
|
|
172
|
+
const result = await memoryStore.deleteMemory(userID, input.key);
|
|
173
|
+
return result.deletedCount > 0 ? `Memory "${input.key}" deleted.` : "Memory not found.";
|
|
174
|
+
} catch (err) {
|
|
175
|
+
return `Error deleting memory: ${err.message}`;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
name: "memory_list",
|
|
182
|
+
description:
|
|
183
|
+
"List all memories saved in the knowledge graph. Returns all memory keys and their content. Use this when you need to see everything that's stored, or when the user asks 'what do you remember about me' or 'show me all my memories'.",
|
|
184
|
+
parameters: {
|
|
185
|
+
type: "object",
|
|
186
|
+
properties: {},
|
|
187
|
+
},
|
|
188
|
+
/**
|
|
189
|
+
* List all memories via memoryStore.readMemoryPattern.
|
|
190
|
+
*
|
|
191
|
+
* @param {Object} input - Tool input (empty)
|
|
192
|
+
* @param {AbortSignal} signal - Abort signal
|
|
193
|
+
* @param {Object} context - { userID, memoryStore }
|
|
194
|
+
* @returns {Promise<string>} Formatted list of all memories
|
|
195
|
+
*/
|
|
196
|
+
execute: async (input, signal, context) => {
|
|
197
|
+
try {
|
|
198
|
+
const { userID, memoryStore } = context;
|
|
199
|
+
if (!memoryStore) {
|
|
200
|
+
return "Error: memoryStore not configured. Memory features are disabled.";
|
|
201
|
+
}
|
|
202
|
+
const all = await memoryStore.readMemoryPattern(userID, ".*");
|
|
203
|
+
|
|
204
|
+
if (all.length === 0) {
|
|
205
|
+
return "No memories found.";
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return `Found ${all.length} ${all.length === 1 ? 'memory' : 'memories'}:\n\n` +
|
|
209
|
+
all.map((m, i) => {
|
|
210
|
+
const content =
|
|
211
|
+
typeof m.value === "object" && m.value.content
|
|
212
|
+
? m.value.content
|
|
213
|
+
: JSON.stringify(m.value);
|
|
214
|
+
const tags =
|
|
215
|
+
typeof m.value === "object" && m.value.tags?.length
|
|
216
|
+
? `\n tags: ${m.value.tags.join(", ")}`
|
|
217
|
+
: "";
|
|
218
|
+
const saved = m.updated_at
|
|
219
|
+
? `\n saved: ${new Date(m.updated_at).toISOString()}`
|
|
220
|
+
: "";
|
|
221
|
+
return `${i + 1}. [${m.key}]\n ${content}${tags}${saved}`;
|
|
222
|
+
}).join("\n\n");
|
|
223
|
+
} catch (err) {
|
|
224
|
+
return `Error listing memories: ${err.message}`;
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
{
|
|
230
|
+
name: "memory_read",
|
|
231
|
+
description:
|
|
232
|
+
"Read a specific memory by its exact key. Use this when you know the key and want to retrieve its full content.",
|
|
233
|
+
parameters: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {
|
|
236
|
+
key: {
|
|
237
|
+
type: "string",
|
|
238
|
+
description: "The exact memory key to read. Use memory_list or memory_search to find keys.",
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
required: ["key"],
|
|
242
|
+
},
|
|
243
|
+
/**
|
|
244
|
+
* Read a specific memory via memoryStore.readMemory.
|
|
245
|
+
*
|
|
246
|
+
* @param {Object} input - Tool input with key
|
|
247
|
+
* @param {AbortSignal} signal - Abort signal
|
|
248
|
+
* @param {Object} context - { userID, memoryStore }
|
|
249
|
+
* @returns {Promise<string>} Memory content or not found message
|
|
250
|
+
*/
|
|
251
|
+
execute: async (input, signal, context) => {
|
|
252
|
+
try {
|
|
253
|
+
const { userID, memoryStore } = context;
|
|
254
|
+
if (!memoryStore) {
|
|
255
|
+
return "Error: memoryStore not configured. Memory features are disabled.";
|
|
256
|
+
}
|
|
257
|
+
const memory = await memoryStore.readMemory(userID, input.key);
|
|
258
|
+
|
|
259
|
+
if (!memory || !memory.value) {
|
|
260
|
+
return `Memory "${input.key}" not found.`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const content =
|
|
264
|
+
typeof memory.value === "object" && memory.value.content
|
|
265
|
+
? memory.value.content
|
|
266
|
+
: JSON.stringify(memory.value, null, 2);
|
|
267
|
+
const tags =
|
|
268
|
+
typeof memory.value === "object" && memory.value.tags?.length
|
|
269
|
+
? `\ntags: ${memory.value.tags.join(", ")}`
|
|
270
|
+
: "";
|
|
271
|
+
const saved = memory.updated_at
|
|
272
|
+
? `\nsaved: ${new Date(memory.updated_at).toISOString()}`
|
|
273
|
+
: "";
|
|
274
|
+
const app = memory.app_id ? `\nwritten by: ${memory.app_id}` : "";
|
|
275
|
+
|
|
276
|
+
return `[${input.key}]\n${content}${tags}${saved}${app}`;
|
|
277
|
+
} catch (err) {
|
|
278
|
+
return `Error reading memory: ${err.message}`;
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
{
|
|
284
|
+
name: "memory_update",
|
|
285
|
+
description:
|
|
286
|
+
"Update an existing memory or create a new one with a specific key. Use this when you need to modify existing information or when you want full control over the memory key (unlike memory_save which auto-generates keys).",
|
|
287
|
+
parameters: {
|
|
288
|
+
type: "object",
|
|
289
|
+
properties: {
|
|
290
|
+
key: {
|
|
291
|
+
type: "string",
|
|
292
|
+
description: "The memory key to update or create. Use snake_case like 'user_preferences' or 'project_notes'.",
|
|
293
|
+
},
|
|
294
|
+
content: {
|
|
295
|
+
type: "string",
|
|
296
|
+
description: "The new content to store. Can be plain text, markdown, or any string.",
|
|
297
|
+
},
|
|
298
|
+
tags: {
|
|
299
|
+
type: "array",
|
|
300
|
+
items: { type: "string" },
|
|
301
|
+
description: "Optional tags for categorization. e.g. ['personal', 'project']",
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
required: ["key", "content"],
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* Update or create a memory via memoryStore.writeMemory.
|
|
308
|
+
*
|
|
309
|
+
* @param {Object} input - Tool input with key, content, and optional tags
|
|
310
|
+
* @param {AbortSignal} signal - Abort signal
|
|
311
|
+
* @param {Object} context - { userID, memoryStore }
|
|
312
|
+
* @returns {Promise<string>} Confirmation message
|
|
313
|
+
*/
|
|
314
|
+
execute: async (input, signal, context) => {
|
|
315
|
+
try {
|
|
316
|
+
const { userID, memoryStore } = context;
|
|
317
|
+
if (!memoryStore) {
|
|
318
|
+
return "Error: memoryStore not configured. Memory features are disabled.";
|
|
319
|
+
}
|
|
320
|
+
const value = {
|
|
321
|
+
content: input.content,
|
|
322
|
+
tags: input.tags || [],
|
|
323
|
+
source: "agent",
|
|
324
|
+
};
|
|
325
|
+
await memoryStore.writeMemory(userID, input.key, value, "agent");
|
|
326
|
+
return `Memory "${input.key}" saved.`;
|
|
327
|
+
} catch (err) {
|
|
328
|
+
return `Error updating memory: ${err.message}`;
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
];
|