@elizaos/plugin-scratchpad 2.0.0-alpha
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 +161 -0
- package/dist/__tests__/scratchpad-plugin.test.d.ts +3 -0
- package/dist/__tests__/scratchpad-plugin.test.d.ts.map +1 -0
- package/dist/__tests__/scratchpad-service.test.d.ts +3 -0
- package/dist/__tests__/scratchpad-service.test.d.ts.map +1 -0
- package/dist/actions/append.d.ts +4 -0
- package/dist/actions/append.d.ts.map +1 -0
- package/dist/actions/delete.d.ts +4 -0
- package/dist/actions/delete.d.ts.map +1 -0
- package/dist/actions/list.d.ts +4 -0
- package/dist/actions/list.d.ts.map +1 -0
- package/dist/actions/read.d.ts +4 -0
- package/dist/actions/read.d.ts.map +1 -0
- package/dist/actions/search.d.ts +4 -0
- package/dist/actions/search.d.ts.map +1 -0
- package/dist/actions/write.d.ts +4 -0
- package/dist/actions/write.d.ts.map +1 -0
- package/dist/browser/index.browser.js +3 -0
- package/dist/browser/index.browser.js.map +10 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/build.d.ts +4 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.node.cjs +1121 -0
- package/dist/cjs/index.node.js.map +19 -0
- package/dist/generated/specs/specs.d.ts +15 -0
- package/dist/generated/specs/specs.d.ts.map +1 -0
- package/dist/index.browser.d.ts +11 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.node.d.ts +6 -0
- package/dist/index.node.d.ts.map +1 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.node.js +1100 -0
- package/dist/node/index.node.js.map +19 -0
- package/dist/providers/scratchpad.d.ts +8 -0
- package/dist/providers/scratchpad.d.ts.map +1 -0
- package/dist/services/scratchpadService.d.ts +63 -0
- package/dist/services/scratchpadService.d.ts.map +1 -0
- package/dist/tests.d.ts +6 -0
- package/dist/tests.d.ts.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,1121 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
19
|
+
var __toCommonJS = (from) => {
|
|
20
|
+
var entry = __moduleCache.get(from), desc;
|
|
21
|
+
if (entry)
|
|
22
|
+
return entry;
|
|
23
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
24
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
25
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
26
|
+
get: () => from[key],
|
|
27
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
28
|
+
}));
|
|
29
|
+
__moduleCache.set(from, entry);
|
|
30
|
+
return entry;
|
|
31
|
+
};
|
|
32
|
+
var __export = (target, all) => {
|
|
33
|
+
for (var name in all)
|
|
34
|
+
__defProp(target, name, {
|
|
35
|
+
get: all[name],
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
set: (newValue) => all[name] = () => newValue
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// index.node.ts
|
|
43
|
+
var exports_index_node = {};
|
|
44
|
+
__export(exports_index_node, {
|
|
45
|
+
scratchpadWriteAction: () => scratchpadWriteAction,
|
|
46
|
+
scratchpadSearchAction: () => scratchpadSearchAction,
|
|
47
|
+
scratchpadReadAction: () => scratchpadReadAction,
|
|
48
|
+
scratchpadProvider: () => scratchpadProvider,
|
|
49
|
+
scratchpadPlugin: () => scratchpadPlugin,
|
|
50
|
+
scratchpadListAction: () => scratchpadListAction,
|
|
51
|
+
scratchpadDeleteAction: () => scratchpadDeleteAction,
|
|
52
|
+
scratchpadAppendAction: () => scratchpadAppendAction,
|
|
53
|
+
default: () => typescript_default,
|
|
54
|
+
createScratchpadService: () => createScratchpadService,
|
|
55
|
+
ScratchpadService: () => ScratchpadService
|
|
56
|
+
});
|
|
57
|
+
module.exports = __toCommonJS(exports_index_node);
|
|
58
|
+
|
|
59
|
+
// index.ts
|
|
60
|
+
var import_core9 = require("@elizaos/core");
|
|
61
|
+
|
|
62
|
+
// actions/append.ts
|
|
63
|
+
var import_core2 = require("@elizaos/core");
|
|
64
|
+
|
|
65
|
+
// generated/specs/specs.ts
|
|
66
|
+
var actionSpecs = {
|
|
67
|
+
SCRATCHPAD_WRITE: {
|
|
68
|
+
name: "SCRATCHPAD_WRITE",
|
|
69
|
+
description: "Write a new note or memory to the scratchpad. Use this to save information for later retrieval.",
|
|
70
|
+
similes: [
|
|
71
|
+
"SAVE_NOTE",
|
|
72
|
+
"CREATE_NOTE",
|
|
73
|
+
"WRITE_NOTE",
|
|
74
|
+
"REMEMBER_THIS",
|
|
75
|
+
"SAVE_MEMORY",
|
|
76
|
+
"JOT_DOWN",
|
|
77
|
+
"NOTE_THIS"
|
|
78
|
+
],
|
|
79
|
+
examples: [
|
|
80
|
+
[
|
|
81
|
+
{
|
|
82
|
+
role: "user",
|
|
83
|
+
content: "Please save a note about the meeting tomorrow at 3pm with John about the marketing strategy."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
role: "assistant",
|
|
87
|
+
content: "I've saved a note about your meeting. Title: 'Meeting with John - Marketing Strategy'. You can retrieve it later."
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
{
|
|
92
|
+
role: "user",
|
|
93
|
+
content: "Remember that the API key for the service is stored in the .env file."
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
role: "assistant",
|
|
97
|
+
content: "I've noted that the API key is stored in the .env file. I'll remember this for future reference."
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
SCRATCHPAD_READ: {
|
|
103
|
+
name: "SCRATCHPAD_READ",
|
|
104
|
+
description: "Read the content of a specific scratchpad entry. Use after searching to retrieve full details.",
|
|
105
|
+
similes: ["GET_NOTE", "READ_NOTE", "RETRIEVE_NOTE", "GET_MEMORY", "FETCH_NOTE", "OPEN_NOTE"],
|
|
106
|
+
examples: [
|
|
107
|
+
[
|
|
108
|
+
{
|
|
109
|
+
role: "user",
|
|
110
|
+
content: "Can you show me the note about the marketing meeting?"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
role: "assistant",
|
|
114
|
+
content: "Here's the note 'Meeting with John - Marketing Strategy': Meeting scheduled for tomorrow at 3pm..."
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
SCRATCHPAD_SEARCH: {
|
|
120
|
+
name: "SCRATCHPAD_SEARCH",
|
|
121
|
+
description: "Search through scratchpad entries for relevant information. Returns matching snippets with relevance scores.",
|
|
122
|
+
similes: [
|
|
123
|
+
"FIND_NOTE",
|
|
124
|
+
"SEARCH_NOTES",
|
|
125
|
+
"LOOKUP_MEMORY",
|
|
126
|
+
"FIND_MEMORY",
|
|
127
|
+
"SEARCH_MEMORY",
|
|
128
|
+
"RECALL"
|
|
129
|
+
],
|
|
130
|
+
examples: [
|
|
131
|
+
[
|
|
132
|
+
{
|
|
133
|
+
role: "user",
|
|
134
|
+
content: "What notes do I have about marketing?"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
role: "assistant",
|
|
138
|
+
content: "I found 2 scratchpad entries mentioning marketing: 1) 'Meeting with John - Marketing Strategy' (score: 0.85)..."
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
SCRATCHPAD_LIST: {
|
|
144
|
+
name: "SCRATCHPAD_LIST",
|
|
145
|
+
description: "List all scratchpad entries with their titles and modification dates.",
|
|
146
|
+
similes: ["SHOW_NOTES", "LIST_NOTES", "ALL_NOTES", "MY_NOTES", "SHOW_MEMORIES"],
|
|
147
|
+
examples: [
|
|
148
|
+
[
|
|
149
|
+
{
|
|
150
|
+
role: "user",
|
|
151
|
+
content: "Show me all my saved notes."
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
role: "assistant",
|
|
155
|
+
content: "You have 5 scratchpad entries: 1) Meeting notes (modified today), 2) API documentation (modified yesterday)..."
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
SCRATCHPAD_DELETE: {
|
|
161
|
+
name: "SCRATCHPAD_DELETE",
|
|
162
|
+
description: "Delete a scratchpad entry by its ID.",
|
|
163
|
+
similes: ["REMOVE_NOTE", "DELETE_NOTE", "FORGET_NOTE", "ERASE_NOTE", "REMOVE_MEMORY"],
|
|
164
|
+
examples: [
|
|
165
|
+
[
|
|
166
|
+
{
|
|
167
|
+
role: "user",
|
|
168
|
+
content: "Delete the note about the old meeting."
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
role: "assistant",
|
|
172
|
+
content: "I've deleted the scratchpad entry 'old-meeting'. The note has been removed."
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
SCRATCHPAD_APPEND: {
|
|
178
|
+
name: "SCRATCHPAD_APPEND",
|
|
179
|
+
description: "Append additional content to an existing scratchpad entry.",
|
|
180
|
+
similes: ["ADD_TO_NOTE", "UPDATE_NOTE", "APPEND_NOTE", "EXTEND_NOTE", "ADD_MORE"],
|
|
181
|
+
examples: [
|
|
182
|
+
[
|
|
183
|
+
{
|
|
184
|
+
role: "user",
|
|
185
|
+
content: "Add to the meeting notes that we decided on a $50k budget."
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
role: "assistant",
|
|
189
|
+
content: "I've appended the budget decision to the meeting notes. The note now includes the $50k budget information."
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
function requireActionSpec(name) {
|
|
196
|
+
const spec = actionSpecs[name];
|
|
197
|
+
if (!spec) {
|
|
198
|
+
throw new Error(`Action spec not found: ${name}`);
|
|
199
|
+
}
|
|
200
|
+
return spec;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// services/scratchpadService.ts
|
|
204
|
+
var fs = __toESM(require("node:fs/promises"));
|
|
205
|
+
var os = __toESM(require("node:os"));
|
|
206
|
+
var path = __toESM(require("node:path"));
|
|
207
|
+
var import_core = require("@elizaos/core");
|
|
208
|
+
var DEFAULT_CONFIG = {
|
|
209
|
+
basePath: path.join(os.homedir(), ".eliza", "scratchpad"),
|
|
210
|
+
maxFileSize: 1024 * 1024,
|
|
211
|
+
allowedExtensions: [".md", ".txt"]
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
class ScratchpadService {
|
|
215
|
+
config;
|
|
216
|
+
constructor(_runtime, config) {
|
|
217
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
218
|
+
}
|
|
219
|
+
async ensureDirectory() {
|
|
220
|
+
try {
|
|
221
|
+
await fs.mkdir(this.config.basePath, { recursive: true });
|
|
222
|
+
} catch (error) {
|
|
223
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
224
|
+
import_core.logger.error("[ScratchpadService] Failed to create directory:", errorMsg);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
sanitizeFilename(title) {
|
|
229
|
+
return title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").substring(0, 100);
|
|
230
|
+
}
|
|
231
|
+
getFilePath(id) {
|
|
232
|
+
const filename = id.endsWith(".md") ? id : `${id}.md`;
|
|
233
|
+
return path.join(this.config.basePath, filename);
|
|
234
|
+
}
|
|
235
|
+
getEntryId(filename) {
|
|
236
|
+
return path.basename(filename, path.extname(filename));
|
|
237
|
+
}
|
|
238
|
+
async write(title, content, options = {}) {
|
|
239
|
+
await this.ensureDirectory();
|
|
240
|
+
const id = this.sanitizeFilename(title);
|
|
241
|
+
const filePath = this.getFilePath(id);
|
|
242
|
+
const now = new Date;
|
|
243
|
+
let finalContent;
|
|
244
|
+
let createdAt = now;
|
|
245
|
+
const exists = await this.exists(id);
|
|
246
|
+
if (exists && options.append) {
|
|
247
|
+
const existing = await this.read(id);
|
|
248
|
+
finalContent = `${existing.content}
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
${content}`;
|
|
253
|
+
createdAt = existing.createdAt;
|
|
254
|
+
} else {
|
|
255
|
+
const tagsLine = options.tags?.length ? `tags: [${options.tags.join(", ")}]` : "";
|
|
256
|
+
const frontmatter = [
|
|
257
|
+
"---",
|
|
258
|
+
`title: "${title}"`,
|
|
259
|
+
`created: ${now.toISOString()}`,
|
|
260
|
+
`modified: ${now.toISOString()}`,
|
|
261
|
+
tagsLine,
|
|
262
|
+
"---",
|
|
263
|
+
""
|
|
264
|
+
].filter(Boolean).join(`
|
|
265
|
+
`);
|
|
266
|
+
finalContent = `${frontmatter}
|
|
267
|
+
${content}`;
|
|
268
|
+
}
|
|
269
|
+
if (Buffer.byteLength(finalContent, "utf8") > (this.config.maxFileSize ?? 1024 * 1024)) {
|
|
270
|
+
throw new Error(`Content exceeds maximum file size of ${this.config.maxFileSize} bytes`);
|
|
271
|
+
}
|
|
272
|
+
await fs.writeFile(filePath, finalContent, "utf8");
|
|
273
|
+
import_core.logger.info(`[ScratchpadService] Wrote entry: ${id}`);
|
|
274
|
+
return {
|
|
275
|
+
id,
|
|
276
|
+
path: filePath,
|
|
277
|
+
title,
|
|
278
|
+
content: finalContent,
|
|
279
|
+
createdAt,
|
|
280
|
+
modifiedAt: now,
|
|
281
|
+
tags: options.tags
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
async read(id, options = {}) {
|
|
285
|
+
const filePath = this.getFilePath(id);
|
|
286
|
+
try {
|
|
287
|
+
const stat2 = await fs.stat(filePath);
|
|
288
|
+
let content = await fs.readFile(filePath, "utf8");
|
|
289
|
+
if (options.from !== undefined || options.lines !== undefined) {
|
|
290
|
+
const lines = content.split(`
|
|
291
|
+
`);
|
|
292
|
+
const fromLine = Math.max(1, options.from ?? 1) - 1;
|
|
293
|
+
const numLines = options.lines ?? lines.length - fromLine;
|
|
294
|
+
content = lines.slice(fromLine, fromLine + numLines).join(`
|
|
295
|
+
`);
|
|
296
|
+
}
|
|
297
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
298
|
+
let title = id;
|
|
299
|
+
let tags = [];
|
|
300
|
+
let createdAt = stat2.birthtime;
|
|
301
|
+
if (frontmatterMatch) {
|
|
302
|
+
const frontmatter = frontmatterMatch[1];
|
|
303
|
+
const titleMatch = frontmatter.match(/title:\s*"?([^"\n]+)"?/);
|
|
304
|
+
const tagsMatch = frontmatter.match(/tags:\s*\[([^\]]+)\]/);
|
|
305
|
+
const createdMatch = frontmatter.match(/created:\s*(.+)/);
|
|
306
|
+
if (titleMatch)
|
|
307
|
+
title = titleMatch[1];
|
|
308
|
+
if (tagsMatch)
|
|
309
|
+
tags = tagsMatch[1].split(",").map((t) => t.trim());
|
|
310
|
+
if (createdMatch)
|
|
311
|
+
createdAt = new Date(createdMatch[1]);
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
id,
|
|
315
|
+
path: filePath,
|
|
316
|
+
title,
|
|
317
|
+
content,
|
|
318
|
+
createdAt,
|
|
319
|
+
modifiedAt: stat2.mtime,
|
|
320
|
+
tags
|
|
321
|
+
};
|
|
322
|
+
} catch (error) {
|
|
323
|
+
if (error.code === "ENOENT") {
|
|
324
|
+
throw new Error(`Scratchpad entry not found: ${id}`);
|
|
325
|
+
}
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async exists(id) {
|
|
330
|
+
const filePath = this.getFilePath(id);
|
|
331
|
+
try {
|
|
332
|
+
await fs.access(filePath);
|
|
333
|
+
return true;
|
|
334
|
+
} catch {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async list() {
|
|
339
|
+
await this.ensureDirectory();
|
|
340
|
+
try {
|
|
341
|
+
const files = await fs.readdir(this.config.basePath);
|
|
342
|
+
const entries = [];
|
|
343
|
+
for (const file of files) {
|
|
344
|
+
const ext = path.extname(file);
|
|
345
|
+
if (!this.config.allowedExtensions?.includes(ext))
|
|
346
|
+
continue;
|
|
347
|
+
try {
|
|
348
|
+
const id = this.getEntryId(file);
|
|
349
|
+
const entry = await this.read(id);
|
|
350
|
+
entries.push(entry);
|
|
351
|
+
} catch (error) {
|
|
352
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
353
|
+
import_core.logger.warn(`[ScratchpadService] Failed to read entry ${file}:`, errorMsg);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return entries.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
357
|
+
} catch (error) {
|
|
358
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
359
|
+
import_core.logger.error("[ScratchpadService] Failed to list entries:", errorMsg);
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async search(query, options = {}) {
|
|
364
|
+
const entries = await this.list();
|
|
365
|
+
const results = [];
|
|
366
|
+
const maxResults = options.maxResults ?? 10;
|
|
367
|
+
const minScore = options.minScore ?? 0.1;
|
|
368
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
369
|
+
for (const entry of entries) {
|
|
370
|
+
const lines = entry.content.split(`
|
|
371
|
+
`);
|
|
372
|
+
const contentLower = entry.content.toLowerCase();
|
|
373
|
+
let matchCount = 0;
|
|
374
|
+
for (const term of queryTerms) {
|
|
375
|
+
const regex = new RegExp(term, "gi");
|
|
376
|
+
const matches = contentLower.match(regex);
|
|
377
|
+
if (matches)
|
|
378
|
+
matchCount += matches.length;
|
|
379
|
+
}
|
|
380
|
+
if (matchCount === 0)
|
|
381
|
+
continue;
|
|
382
|
+
const score = Math.min(1, matchCount / (queryTerms.length * 3));
|
|
383
|
+
if (score < minScore)
|
|
384
|
+
continue;
|
|
385
|
+
let bestSnippetStart = 0;
|
|
386
|
+
let bestSnippetEnd = Math.min(lines.length, 5);
|
|
387
|
+
for (let i = 0;i < lines.length; i++) {
|
|
388
|
+
const lineLower = lines[i].toLowerCase();
|
|
389
|
+
for (const term of queryTerms) {
|
|
390
|
+
if (lineLower.includes(term)) {
|
|
391
|
+
bestSnippetStart = Math.max(0, i - 2);
|
|
392
|
+
bestSnippetEnd = Math.min(lines.length, i + 3);
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const snippet = lines.slice(bestSnippetStart, bestSnippetEnd).join(`
|
|
398
|
+
`);
|
|
399
|
+
results.push({
|
|
400
|
+
path: entry.path,
|
|
401
|
+
startLine: bestSnippetStart + 1,
|
|
402
|
+
endLine: bestSnippetEnd,
|
|
403
|
+
score,
|
|
404
|
+
snippet,
|
|
405
|
+
entryId: entry.id
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
return results.sort((a, b) => b.score - a.score).slice(0, maxResults);
|
|
409
|
+
}
|
|
410
|
+
async delete(id) {
|
|
411
|
+
const filePath = this.getFilePath(id);
|
|
412
|
+
try {
|
|
413
|
+
await fs.unlink(filePath);
|
|
414
|
+
import_core.logger.info(`[ScratchpadService] Deleted entry: ${id}`);
|
|
415
|
+
return true;
|
|
416
|
+
} catch (error) {
|
|
417
|
+
if (error.code === "ENOENT") {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async getSummary() {
|
|
424
|
+
const entries = await this.list();
|
|
425
|
+
if (entries.length === 0) {
|
|
426
|
+
return "No scratchpad entries found.";
|
|
427
|
+
}
|
|
428
|
+
const summaryParts = [`**Scratchpad Summary** (${entries.length} entries)`, ""];
|
|
429
|
+
for (const entry of entries.slice(0, 10)) {
|
|
430
|
+
const preview = entry.content.replace(/^---[\s\S]*?---\n*/m, "").substring(0, 100).replace(/\n/g, " ").trim();
|
|
431
|
+
summaryParts.push(`- **${entry.title}** (${entry.id})`);
|
|
432
|
+
summaryParts.push(` ${preview}${preview.length >= 100 ? "..." : ""}`);
|
|
433
|
+
summaryParts.push(` _Modified: ${entry.modifiedAt.toLocaleDateString()}_`);
|
|
434
|
+
}
|
|
435
|
+
if (entries.length > 10) {
|
|
436
|
+
summaryParts.push(`
|
|
437
|
+
_...and ${entries.length - 10} more entries_`);
|
|
438
|
+
}
|
|
439
|
+
return summaryParts.join(`
|
|
440
|
+
`);
|
|
441
|
+
}
|
|
442
|
+
getBasePath() {
|
|
443
|
+
return this.config.basePath;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function createScratchpadService(runtime, config) {
|
|
447
|
+
return new ScratchpadService(runtime, config);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// actions/append.ts
|
|
451
|
+
function isValidAppendInput(obj) {
|
|
452
|
+
return typeof obj.id === "string" && obj.id.length > 0 && typeof obj.content === "string" && obj.content.length > 0;
|
|
453
|
+
}
|
|
454
|
+
var EXTRACT_TEMPLATE = `Extract the scratchpad entry ID and content to append from the user's message.
|
|
455
|
+
|
|
456
|
+
User message: {{text}}
|
|
457
|
+
|
|
458
|
+
Available scratchpad entries:
|
|
459
|
+
{{entries}}
|
|
460
|
+
|
|
461
|
+
Respond with XML containing:
|
|
462
|
+
- id: The ID of the scratchpad entry to append to (required)
|
|
463
|
+
- content: The new content to append (required)
|
|
464
|
+
|
|
465
|
+
<response>
|
|
466
|
+
<id>entry-id</id>
|
|
467
|
+
<content>Content to append</content>
|
|
468
|
+
</response>`;
|
|
469
|
+
async function extractAppendInfo(runtime, message, availableEntries) {
|
|
470
|
+
const prompt = EXTRACT_TEMPLATE.replace("{{text}}", message.content.text ?? "").replace("{{entries}}", availableEntries);
|
|
471
|
+
const result = await runtime.useModel(import_core2.ModelType.TEXT_SMALL, {
|
|
472
|
+
prompt,
|
|
473
|
+
stopSequences: []
|
|
474
|
+
});
|
|
475
|
+
import_core2.logger.debug("[ScratchpadAppend] Extract result:", result);
|
|
476
|
+
const parsed = import_core2.parseKeyValueXml(String(result));
|
|
477
|
+
if (!parsed || !isValidAppendInput(parsed)) {
|
|
478
|
+
import_core2.logger.error("[ScratchpadAppend] Failed to extract valid append info");
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
id: String(parsed.id),
|
|
483
|
+
content: String(parsed.content)
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
var spec = requireActionSpec("SCRATCHPAD_APPEND");
|
|
487
|
+
var scratchpadAppendAction = {
|
|
488
|
+
name: spec.name,
|
|
489
|
+
similes: spec.similes ? [...spec.similes] : [],
|
|
490
|
+
description: spec.description,
|
|
491
|
+
validate: async (_runtime, _message) => {
|
|
492
|
+
return true;
|
|
493
|
+
},
|
|
494
|
+
handler: async (runtime, message, _stateFromTrigger, _options, callback, _responses) => {
|
|
495
|
+
const service = createScratchpadService(runtime);
|
|
496
|
+
const entries = await service.list();
|
|
497
|
+
const entriesContext = entries.map((e) => `- ${e.id}: "${e.title}"`).join(`
|
|
498
|
+
`);
|
|
499
|
+
if (entries.length === 0) {
|
|
500
|
+
if (callback) {
|
|
501
|
+
await callback({
|
|
502
|
+
text: "There are no scratchpad entries to append to. Create one first with SCRATCHPAD_WRITE.",
|
|
503
|
+
actions: ["SCRATCHPAD_APPEND_EMPTY"],
|
|
504
|
+
source: message.content.source
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return { success: false, text: "No entries available" };
|
|
508
|
+
}
|
|
509
|
+
const appendInfo = await extractAppendInfo(runtime, message, entriesContext);
|
|
510
|
+
if (!appendInfo) {
|
|
511
|
+
if (callback) {
|
|
512
|
+
await callback({
|
|
513
|
+
text: `I couldn't determine which note to update or what to add. Available entries:
|
|
514
|
+
${entriesContext}`,
|
|
515
|
+
actions: ["SCRATCHPAD_APPEND_FAILED"],
|
|
516
|
+
source: message.content.source
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
return { success: false, text: "Failed to extract append info" };
|
|
520
|
+
}
|
|
521
|
+
try {
|
|
522
|
+
const exists = await service.exists(appendInfo.id);
|
|
523
|
+
if (!exists) {
|
|
524
|
+
if (callback) {
|
|
525
|
+
await callback({
|
|
526
|
+
text: `Scratchpad entry "${appendInfo.id}" not found. Available entries:
|
|
527
|
+
${entriesContext}`,
|
|
528
|
+
actions: ["SCRATCHPAD_APPEND_NOT_FOUND"],
|
|
529
|
+
source: message.content.source
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
return { success: false, text: "Entry not found" };
|
|
533
|
+
}
|
|
534
|
+
const existingEntry = await service.read(appendInfo.id);
|
|
535
|
+
const entry = await service.write(existingEntry.title, appendInfo.content, {
|
|
536
|
+
append: true,
|
|
537
|
+
tags: existingEntry.tags
|
|
538
|
+
});
|
|
539
|
+
const successMessage = `Successfully appended content to "${entry.title}" (${entry.id}).`;
|
|
540
|
+
if (callback) {
|
|
541
|
+
await callback({
|
|
542
|
+
text: successMessage,
|
|
543
|
+
actions: ["SCRATCHPAD_APPEND_SUCCESS"],
|
|
544
|
+
source: message.content.source
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return { success: true, text: successMessage, entry };
|
|
548
|
+
} catch (error) {
|
|
549
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
550
|
+
import_core2.logger.error("[ScratchpadAppend] Error:", errorMsg);
|
|
551
|
+
if (callback) {
|
|
552
|
+
await callback({
|
|
553
|
+
text: `Failed to append to the note: ${errorMsg}`,
|
|
554
|
+
actions: ["SCRATCHPAD_APPEND_FAILED"],
|
|
555
|
+
source: message.content.source
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
return { success: false, text: "Failed to append to scratchpad entry" };
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
examples: []
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// actions/delete.ts
|
|
565
|
+
var import_core3 = require("@elizaos/core");
|
|
566
|
+
function isValidDeleteInput(obj) {
|
|
567
|
+
return typeof obj.id === "string" && obj.id.length > 0;
|
|
568
|
+
}
|
|
569
|
+
var EXTRACT_TEMPLATE2 = `Extract the scratchpad entry ID to delete from the user's message.
|
|
570
|
+
|
|
571
|
+
User message: {{text}}
|
|
572
|
+
|
|
573
|
+
Available scratchpad entries:
|
|
574
|
+
{{entries}}
|
|
575
|
+
|
|
576
|
+
Respond with XML containing:
|
|
577
|
+
- id: The ID of the scratchpad entry to delete (required)
|
|
578
|
+
|
|
579
|
+
<response>
|
|
580
|
+
<id>entry-id</id>
|
|
581
|
+
</response>`;
|
|
582
|
+
async function extractDeleteInfo(runtime, message, availableEntries) {
|
|
583
|
+
const prompt = EXTRACT_TEMPLATE2.replace("{{text}}", message.content.text ?? "").replace("{{entries}}", availableEntries);
|
|
584
|
+
const result = await runtime.useModel(import_core3.ModelType.TEXT_SMALL, {
|
|
585
|
+
prompt,
|
|
586
|
+
stopSequences: []
|
|
587
|
+
});
|
|
588
|
+
import_core3.logger.debug("[ScratchpadDelete] Extract result:", result);
|
|
589
|
+
const parsed = import_core3.parseKeyValueXml(String(result));
|
|
590
|
+
if (!parsed || !isValidDeleteInput(parsed)) {
|
|
591
|
+
import_core3.logger.error("[ScratchpadDelete] Failed to extract valid delete info");
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
return {
|
|
595
|
+
id: String(parsed.id)
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
var spec2 = requireActionSpec("SCRATCHPAD_DELETE");
|
|
599
|
+
var scratchpadDeleteAction = {
|
|
600
|
+
name: spec2.name,
|
|
601
|
+
similes: spec2.similes ? [...spec2.similes] : [],
|
|
602
|
+
description: spec2.description,
|
|
603
|
+
validate: async (_runtime, _message) => {
|
|
604
|
+
return true;
|
|
605
|
+
},
|
|
606
|
+
handler: async (runtime, message, _stateFromTrigger, _options, callback, _responses) => {
|
|
607
|
+
const service = createScratchpadService(runtime);
|
|
608
|
+
const entries = await service.list();
|
|
609
|
+
const entriesContext = entries.map((e) => `- ${e.id}: "${e.title}"`).join(`
|
|
610
|
+
`);
|
|
611
|
+
if (entries.length === 0) {
|
|
612
|
+
if (callback) {
|
|
613
|
+
await callback({
|
|
614
|
+
text: "There are no scratchpad entries to delete.",
|
|
615
|
+
actions: ["SCRATCHPAD_DELETE_EMPTY"],
|
|
616
|
+
source: message.content.source
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
return { success: false, text: "No entries available" };
|
|
620
|
+
}
|
|
621
|
+
const deleteInfo = await extractDeleteInfo(runtime, message, entriesContext);
|
|
622
|
+
if (!deleteInfo) {
|
|
623
|
+
if (callback) {
|
|
624
|
+
await callback({
|
|
625
|
+
text: `I couldn't determine which note to delete. Available entries:
|
|
626
|
+
${entriesContext}`,
|
|
627
|
+
actions: ["SCRATCHPAD_DELETE_FAILED"],
|
|
628
|
+
source: message.content.source
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return { success: false, text: "Failed to extract delete info" };
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
const deleted = await service.delete(deleteInfo.id);
|
|
635
|
+
if (!deleted) {
|
|
636
|
+
if (callback) {
|
|
637
|
+
await callback({
|
|
638
|
+
text: `Scratchpad entry "${deleteInfo.id}" not found.`,
|
|
639
|
+
actions: ["SCRATCHPAD_DELETE_NOT_FOUND"],
|
|
640
|
+
source: message.content.source
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
return { success: false, text: "Entry not found" };
|
|
644
|
+
}
|
|
645
|
+
const successMessage = `Successfully deleted scratchpad entry "${deleteInfo.id}".`;
|
|
646
|
+
if (callback) {
|
|
647
|
+
await callback({
|
|
648
|
+
text: successMessage,
|
|
649
|
+
actions: ["SCRATCHPAD_DELETE_SUCCESS"],
|
|
650
|
+
source: message.content.source
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
return { success: true, text: successMessage };
|
|
654
|
+
} catch (error) {
|
|
655
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
656
|
+
import_core3.logger.error("[ScratchpadDelete] Error:", errorMsg);
|
|
657
|
+
if (callback) {
|
|
658
|
+
await callback({
|
|
659
|
+
text: `Failed to delete the note: ${errorMsg}`,
|
|
660
|
+
actions: ["SCRATCHPAD_DELETE_FAILED"],
|
|
661
|
+
source: message.content.source
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
return { success: false, text: "Failed to delete scratchpad entry" };
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
examples: []
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
// actions/list.ts
|
|
671
|
+
var import_core4 = require("@elizaos/core");
|
|
672
|
+
var spec3 = requireActionSpec("SCRATCHPAD_LIST");
|
|
673
|
+
var scratchpadListAction = {
|
|
674
|
+
name: spec3.name,
|
|
675
|
+
similes: spec3.similes ? [...spec3.similes] : [],
|
|
676
|
+
description: spec3.description,
|
|
677
|
+
validate: async (_runtime, _message) => {
|
|
678
|
+
return true;
|
|
679
|
+
},
|
|
680
|
+
handler: async (runtime, message, _stateFromTrigger, _options, callback, _responses) => {
|
|
681
|
+
try {
|
|
682
|
+
const service = createScratchpadService(runtime);
|
|
683
|
+
const entries = await service.list();
|
|
684
|
+
if (entries.length === 0) {
|
|
685
|
+
if (callback) {
|
|
686
|
+
await callback({
|
|
687
|
+
text: "You don't have any scratchpad entries yet. Use SCRATCHPAD_WRITE to create one.",
|
|
688
|
+
actions: ["SCRATCHPAD_LIST_EMPTY"],
|
|
689
|
+
source: message.content.source
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
return { success: true, text: "No entries", entries: [] };
|
|
693
|
+
}
|
|
694
|
+
const listText = entries.map((e, i) => {
|
|
695
|
+
const tagsStr = e.tags?.length ? ` [${e.tags.join(", ")}]` : "";
|
|
696
|
+
return `${i + 1}. **${e.title}** (${e.id})${tagsStr}
|
|
697
|
+
_Modified: ${e.modifiedAt.toLocaleDateString()}_`;
|
|
698
|
+
}).join(`
|
|
699
|
+
`);
|
|
700
|
+
const successMessage = `**Your Scratchpad Entries** (${entries.length} total):
|
|
701
|
+
|
|
702
|
+
${listText}`;
|
|
703
|
+
if (callback) {
|
|
704
|
+
await callback({
|
|
705
|
+
text: successMessage,
|
|
706
|
+
actions: ["SCRATCHPAD_LIST_SUCCESS"],
|
|
707
|
+
source: message.content.source
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
return { success: true, text: successMessage, entries };
|
|
711
|
+
} catch (error) {
|
|
712
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
713
|
+
import_core4.logger.error("[ScratchpadList] Error:", errorMsg);
|
|
714
|
+
if (callback) {
|
|
715
|
+
await callback({
|
|
716
|
+
text: `Failed to list scratchpad entries: ${errorMsg}`,
|
|
717
|
+
actions: ["SCRATCHPAD_LIST_FAILED"],
|
|
718
|
+
source: message.content.source
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
return { success: false, text: "Failed to list scratchpad entries" };
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
examples: []
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// actions/read.ts
|
|
728
|
+
var import_core5 = require("@elizaos/core");
|
|
729
|
+
function isValidReadInput(obj) {
|
|
730
|
+
return typeof obj.id === "string" && obj.id.length > 0;
|
|
731
|
+
}
|
|
732
|
+
var EXTRACT_TEMPLATE3 = `Extract the scratchpad entry ID and optional line range from the user's message.
|
|
733
|
+
|
|
734
|
+
User message: {{text}}
|
|
735
|
+
|
|
736
|
+
Available scratchpad entries:
|
|
737
|
+
{{entries}}
|
|
738
|
+
|
|
739
|
+
Respond with XML containing:
|
|
740
|
+
- id: The ID of the scratchpad entry to read (required)
|
|
741
|
+
- from: Starting line number (optional)
|
|
742
|
+
- lines: Number of lines to read (optional)
|
|
743
|
+
|
|
744
|
+
<response>
|
|
745
|
+
<id>entry-id</id>
|
|
746
|
+
<from>1</from>
|
|
747
|
+
<lines>10</lines>
|
|
748
|
+
</response>`;
|
|
749
|
+
async function extractReadInfo(runtime, message, availableEntries) {
|
|
750
|
+
const prompt = EXTRACT_TEMPLATE3.replace("{{text}}", message.content.text ?? "").replace("{{entries}}", availableEntries);
|
|
751
|
+
const result = await runtime.useModel(import_core5.ModelType.TEXT_SMALL, {
|
|
752
|
+
prompt,
|
|
753
|
+
stopSequences: []
|
|
754
|
+
});
|
|
755
|
+
import_core5.logger.debug("[ScratchpadRead] Extract result:", result);
|
|
756
|
+
const parsed = import_core5.parseKeyValueXml(String(result));
|
|
757
|
+
if (!parsed || !isValidReadInput(parsed)) {
|
|
758
|
+
import_core5.logger.error("[ScratchpadRead] Failed to extract valid read info");
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
id: String(parsed.id),
|
|
763
|
+
from: parsed.from ? Number(parsed.from) : undefined,
|
|
764
|
+
lines: parsed.lines ? Number(parsed.lines) : undefined
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
var spec4 = requireActionSpec("SCRATCHPAD_READ");
|
|
768
|
+
var scratchpadReadAction = {
|
|
769
|
+
name: spec4.name,
|
|
770
|
+
similes: spec4.similes ? [...spec4.similes] : [],
|
|
771
|
+
description: spec4.description,
|
|
772
|
+
validate: async (_runtime, _message) => {
|
|
773
|
+
return true;
|
|
774
|
+
},
|
|
775
|
+
handler: async (runtime, message, _stateFromTrigger, _options, callback, _responses) => {
|
|
776
|
+
const service = createScratchpadService(runtime);
|
|
777
|
+
const entries = await service.list();
|
|
778
|
+
const entriesContext = entries.map((e) => `- ${e.id}: "${e.title}"`).join(`
|
|
779
|
+
`);
|
|
780
|
+
if (entries.length === 0) {
|
|
781
|
+
if (callback) {
|
|
782
|
+
await callback({
|
|
783
|
+
text: "There are no scratchpad entries to read. You can create one first.",
|
|
784
|
+
actions: ["SCRATCHPAD_READ_EMPTY"],
|
|
785
|
+
source: message.content.source
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
return { success: false, text: "No entries available" };
|
|
789
|
+
}
|
|
790
|
+
const readInfo = await extractReadInfo(runtime, message, entriesContext);
|
|
791
|
+
if (!readInfo) {
|
|
792
|
+
if (callback) {
|
|
793
|
+
await callback({
|
|
794
|
+
text: `I couldn't determine which note to read. Available entries:
|
|
795
|
+
${entriesContext}`,
|
|
796
|
+
actions: ["SCRATCHPAD_READ_FAILED"],
|
|
797
|
+
source: message.content.source
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
return { success: false, text: "Failed to extract read info" };
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
const entry = await service.read(readInfo.id, {
|
|
804
|
+
from: readInfo.from,
|
|
805
|
+
lines: readInfo.lines
|
|
806
|
+
});
|
|
807
|
+
const lineInfo = readInfo.from !== undefined ? ` (lines ${readInfo.from}-${(readInfo.from ?? 1) + (readInfo.lines ?? 10)})` : "";
|
|
808
|
+
const successMessage = `**${entry.title}**${lineInfo}
|
|
809
|
+
|
|
810
|
+
${entry.content}`;
|
|
811
|
+
if (callback) {
|
|
812
|
+
await callback({
|
|
813
|
+
text: successMessage,
|
|
814
|
+
actions: ["SCRATCHPAD_READ_SUCCESS"],
|
|
815
|
+
source: message.content.source
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
return { success: true, text: successMessage, entry };
|
|
819
|
+
} catch (error) {
|
|
820
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
821
|
+
import_core5.logger.error("[ScratchpadRead] Error:", errorMsg);
|
|
822
|
+
if (callback) {
|
|
823
|
+
await callback({
|
|
824
|
+
text: `Failed to read the note: ${errorMsg}`,
|
|
825
|
+
actions: ["SCRATCHPAD_READ_FAILED"],
|
|
826
|
+
source: message.content.source
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
return { success: false, text: "Failed to read scratchpad entry" };
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
examples: []
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
// actions/search.ts
|
|
836
|
+
var import_core6 = require("@elizaos/core");
|
|
837
|
+
function isValidSearchInput(obj) {
|
|
838
|
+
return typeof obj.query === "string" && obj.query.length > 0;
|
|
839
|
+
}
|
|
840
|
+
var EXTRACT_TEMPLATE4 = `Extract the search query from the user's message.
|
|
841
|
+
|
|
842
|
+
User message: {{text}}
|
|
843
|
+
|
|
844
|
+
Respond with XML containing:
|
|
845
|
+
- query: The search terms to find in scratchpad entries (required)
|
|
846
|
+
- maxResults: Maximum number of results to return (optional, default 5)
|
|
847
|
+
|
|
848
|
+
<response>
|
|
849
|
+
<query>search terms</query>
|
|
850
|
+
<maxResults>5</maxResults>
|
|
851
|
+
</response>`;
|
|
852
|
+
async function extractSearchInfo(runtime, message) {
|
|
853
|
+
const prompt = EXTRACT_TEMPLATE4.replace("{{text}}", message.content.text ?? "");
|
|
854
|
+
const result = await runtime.useModel(import_core6.ModelType.TEXT_SMALL, {
|
|
855
|
+
prompt,
|
|
856
|
+
stopSequences: []
|
|
857
|
+
});
|
|
858
|
+
import_core6.logger.debug("[ScratchpadSearch] Extract result:", result);
|
|
859
|
+
const parsed = import_core6.parseKeyValueXml(String(result));
|
|
860
|
+
if (!parsed || !isValidSearchInput(parsed)) {
|
|
861
|
+
import_core6.logger.error("[ScratchpadSearch] Failed to extract valid search info");
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
return {
|
|
865
|
+
query: String(parsed.query),
|
|
866
|
+
maxResults: parsed.maxResults ? Number(parsed.maxResults) : 5
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
var spec5 = requireActionSpec("SCRATCHPAD_SEARCH");
|
|
870
|
+
var scratchpadSearchAction = {
|
|
871
|
+
name: spec5.name,
|
|
872
|
+
similes: spec5.similes ? [...spec5.similes] : [],
|
|
873
|
+
description: spec5.description,
|
|
874
|
+
validate: async (_runtime, _message) => {
|
|
875
|
+
return true;
|
|
876
|
+
},
|
|
877
|
+
handler: async (runtime, message, _stateFromTrigger, _options, callback, _responses) => {
|
|
878
|
+
const searchInfo = await extractSearchInfo(runtime, message);
|
|
879
|
+
if (!searchInfo) {
|
|
880
|
+
if (callback) {
|
|
881
|
+
await callback({
|
|
882
|
+
text: "I couldn't understand what you're searching for. Please provide search terms.",
|
|
883
|
+
actions: ["SCRATCHPAD_SEARCH_FAILED"],
|
|
884
|
+
source: message.content.source
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
return { success: false, text: "Failed to extract search info" };
|
|
888
|
+
}
|
|
889
|
+
try {
|
|
890
|
+
const service = createScratchpadService(runtime);
|
|
891
|
+
const results = await service.search(searchInfo.query, {
|
|
892
|
+
maxResults: searchInfo.maxResults
|
|
893
|
+
});
|
|
894
|
+
if (results.length === 0) {
|
|
895
|
+
if (callback) {
|
|
896
|
+
await callback({
|
|
897
|
+
text: `No scratchpad entries found matching "${searchInfo.query}".`,
|
|
898
|
+
actions: ["SCRATCHPAD_SEARCH_EMPTY"],
|
|
899
|
+
source: message.content.source
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
return { success: true, text: "No results found", results: [] };
|
|
903
|
+
}
|
|
904
|
+
const resultText = results.map((r, i) => {
|
|
905
|
+
const scorePercent = Math.round(r.score * 100);
|
|
906
|
+
return `**${i + 1}. ${r.entryId}** (${scorePercent}% match, lines ${r.startLine}-${r.endLine})
|
|
907
|
+
\`\`\`
|
|
908
|
+
${r.snippet.substring(0, 200)}${r.snippet.length > 200 ? "..." : ""}
|
|
909
|
+
\`\`\``;
|
|
910
|
+
}).join(`
|
|
911
|
+
|
|
912
|
+
`);
|
|
913
|
+
const successMessage = `Found ${results.length} matching scratchpad entries for "${searchInfo.query}":
|
|
914
|
+
|
|
915
|
+
${resultText}
|
|
916
|
+
|
|
917
|
+
Use SCRATCHPAD_READ with an entry ID to view the full content.`;
|
|
918
|
+
if (callback) {
|
|
919
|
+
await callback({
|
|
920
|
+
text: successMessage,
|
|
921
|
+
actions: ["SCRATCHPAD_SEARCH_SUCCESS"],
|
|
922
|
+
source: message.content.source
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
return { success: true, text: successMessage, results };
|
|
926
|
+
} catch (error) {
|
|
927
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
928
|
+
import_core6.logger.error("[ScratchpadSearch] Error:", errorMsg);
|
|
929
|
+
if (callback) {
|
|
930
|
+
await callback({
|
|
931
|
+
text: `Failed to search scratchpad: ${errorMsg}`,
|
|
932
|
+
actions: ["SCRATCHPAD_SEARCH_FAILED"],
|
|
933
|
+
source: message.content.source
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
return { success: false, text: "Failed to search scratchpad" };
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
examples: []
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
// actions/write.ts
|
|
943
|
+
var import_core7 = require("@elizaos/core");
|
|
944
|
+
function isValidWriteInput(obj) {
|
|
945
|
+
return typeof obj.title === "string" && obj.title.length > 0 && typeof obj.content === "string" && obj.content.length > 0;
|
|
946
|
+
}
|
|
947
|
+
var EXTRACT_TEMPLATE5 = `Extract the following information from the user's message to save to the scratchpad:
|
|
948
|
+
|
|
949
|
+
User message: {{text}}
|
|
950
|
+
|
|
951
|
+
Recent conversation:
|
|
952
|
+
{{messageHistory}}
|
|
953
|
+
|
|
954
|
+
Respond with XML containing:
|
|
955
|
+
- title: A short, descriptive title for the note (required)
|
|
956
|
+
- content: The main content to save (required)
|
|
957
|
+
- tags: Comma-separated tags for categorization (optional)
|
|
958
|
+
|
|
959
|
+
<response>
|
|
960
|
+
<title>The note title</title>
|
|
961
|
+
<content>The content to save</content>
|
|
962
|
+
<tags>tag1, tag2</tags>
|
|
963
|
+
</response>`;
|
|
964
|
+
async function extractWriteInfo(runtime, message, _state) {
|
|
965
|
+
const prompt = EXTRACT_TEMPLATE5.replace("{{text}}", message.content.text ?? "").replace("{{messageHistory}}", "");
|
|
966
|
+
const result = await runtime.useModel(import_core7.ModelType.TEXT_SMALL, {
|
|
967
|
+
prompt,
|
|
968
|
+
stopSequences: []
|
|
969
|
+
});
|
|
970
|
+
import_core7.logger.debug("[ScratchpadWrite] Extract result:", result);
|
|
971
|
+
const parsed = import_core7.parseKeyValueXml(String(result));
|
|
972
|
+
if (!parsed || !isValidWriteInput(parsed)) {
|
|
973
|
+
import_core7.logger.error("[ScratchpadWrite] Failed to extract valid write info");
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
const tags = parsed.tags ? String(parsed.tags).split(",").map((t) => t.trim()).filter(Boolean) : undefined;
|
|
977
|
+
return {
|
|
978
|
+
title: String(parsed.title),
|
|
979
|
+
content: String(parsed.content),
|
|
980
|
+
tags
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
var spec6 = requireActionSpec("SCRATCHPAD_WRITE");
|
|
984
|
+
var scratchpadWriteAction = {
|
|
985
|
+
name: spec6.name,
|
|
986
|
+
similes: spec6.similes ? [...spec6.similes] : [],
|
|
987
|
+
description: spec6.description,
|
|
988
|
+
validate: async (_runtime, _message) => {
|
|
989
|
+
return true;
|
|
990
|
+
},
|
|
991
|
+
handler: async (runtime, message, stateFromTrigger, _options, callback, _responses) => {
|
|
992
|
+
const state = stateFromTrigger ?? await runtime.composeState(message, []);
|
|
993
|
+
const writeInfo = await extractWriteInfo(runtime, message, state);
|
|
994
|
+
if (!writeInfo) {
|
|
995
|
+
if (callback) {
|
|
996
|
+
await callback({
|
|
997
|
+
text: "I couldn't understand what you want me to save. Please provide a clear title and content for the note.",
|
|
998
|
+
actions: ["SCRATCHPAD_WRITE_FAILED"],
|
|
999
|
+
source: message.content.source
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
return { success: false, text: "Failed to extract write info" };
|
|
1003
|
+
}
|
|
1004
|
+
try {
|
|
1005
|
+
const service = createScratchpadService(runtime);
|
|
1006
|
+
const entry = await service.write(writeInfo.title, writeInfo.content, {
|
|
1007
|
+
tags: writeInfo.tags
|
|
1008
|
+
});
|
|
1009
|
+
const successMessage = `I've saved a note titled "${entry.title}" (ID: ${entry.id}).${entry.tags?.length ? ` Tags: ${entry.tags.join(", ")}` : ""} You can retrieve it later using the ID or by searching for it.`;
|
|
1010
|
+
if (callback) {
|
|
1011
|
+
await callback({
|
|
1012
|
+
text: successMessage,
|
|
1013
|
+
actions: ["SCRATCHPAD_WRITE_SUCCESS"],
|
|
1014
|
+
source: message.content.source
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
return { success: true, text: successMessage, entryId: entry.id };
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1020
|
+
import_core7.logger.error("[ScratchpadWrite] Error:", errorMsg);
|
|
1021
|
+
if (callback) {
|
|
1022
|
+
await callback({
|
|
1023
|
+
text: `Failed to save the note: ${errorMsg}`,
|
|
1024
|
+
actions: ["SCRATCHPAD_WRITE_FAILED"],
|
|
1025
|
+
source: message.content.source
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
return { success: false, text: "Failed to write to scratchpad" };
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
examples: []
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
// providers/scratchpad.ts
|
|
1035
|
+
var import_core8 = require("@elizaos/core");
|
|
1036
|
+
var scratchpadProvider = {
|
|
1037
|
+
name: "scratchpad",
|
|
1038
|
+
description: "Provides information about the user's scratchpad entries - file-based notes and memories that persist across sessions.",
|
|
1039
|
+
get: async (runtime, _message, _state) => {
|
|
1040
|
+
try {
|
|
1041
|
+
const service = createScratchpadService(runtime);
|
|
1042
|
+
const entries = await service.list();
|
|
1043
|
+
if (entries.length === 0) {
|
|
1044
|
+
return {
|
|
1045
|
+
text: "No scratchpad entries available.",
|
|
1046
|
+
data: { entries: [], count: 0 },
|
|
1047
|
+
values: { scratchpadCount: 0 }
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
const summaryLines = [`**Scratchpad** (${entries.length} entries available):`, ""];
|
|
1051
|
+
const recentEntries = entries.slice(0, 5);
|
|
1052
|
+
for (const entry of recentEntries) {
|
|
1053
|
+
const contentWithoutFrontmatter = entry.content.replace(/^---[\s\S]*?---\n*/m, "").trim();
|
|
1054
|
+
const preview = contentWithoutFrontmatter.substring(0, 80).replace(/\n/g, " ");
|
|
1055
|
+
const tagsStr = entry.tags?.length ? ` [${entry.tags.join(", ")}]` : "";
|
|
1056
|
+
summaryLines.push(`- **${entry.title}** (${entry.id})${tagsStr}`);
|
|
1057
|
+
summaryLines.push(` ${preview}${contentWithoutFrontmatter.length > 80 ? "..." : ""}`);
|
|
1058
|
+
}
|
|
1059
|
+
if (entries.length > 5) {
|
|
1060
|
+
summaryLines.push(`
|
|
1061
|
+
_...and ${entries.length - 5} more entries_`);
|
|
1062
|
+
}
|
|
1063
|
+
summaryLines.push(`
|
|
1064
|
+
_Use SCRATCHPAD_SEARCH to find specific entries or SCRATCHPAD_READ to view full content._`);
|
|
1065
|
+
const entryData = entries.map((e) => ({
|
|
1066
|
+
id: e.id,
|
|
1067
|
+
title: e.title,
|
|
1068
|
+
modifiedAt: e.modifiedAt.toISOString(),
|
|
1069
|
+
tags: e.tags ?? []
|
|
1070
|
+
}));
|
|
1071
|
+
return {
|
|
1072
|
+
text: summaryLines.join(`
|
|
1073
|
+
`),
|
|
1074
|
+
data: {
|
|
1075
|
+
entries: entryData,
|
|
1076
|
+
count: entries.length,
|
|
1077
|
+
basePath: service.getBasePath()
|
|
1078
|
+
},
|
|
1079
|
+
values: {
|
|
1080
|
+
scratchpadCount: entries.length,
|
|
1081
|
+
scratchpadEntryIds: entries.map((e) => e.id).join(", ")
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1086
|
+
import_core8.logger.error("[ScratchpadProvider] Error:", errorMsg);
|
|
1087
|
+
return {
|
|
1088
|
+
text: "Scratchpad service unavailable.",
|
|
1089
|
+
data: { error: errorMsg },
|
|
1090
|
+
values: { scratchpadCount: 0 }
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
// index.ts
|
|
1097
|
+
var scratchpadPlugin = {
|
|
1098
|
+
name: "scratchpad",
|
|
1099
|
+
description: "File-based memory storage for persistent notes and memories that can be written, read, searched, and managed across sessions.",
|
|
1100
|
+
providers: [scratchpadProvider],
|
|
1101
|
+
actions: [
|
|
1102
|
+
scratchpadWriteAction,
|
|
1103
|
+
scratchpadReadAction,
|
|
1104
|
+
scratchpadSearchAction,
|
|
1105
|
+
scratchpadListAction,
|
|
1106
|
+
scratchpadDeleteAction,
|
|
1107
|
+
scratchpadAppendAction
|
|
1108
|
+
],
|
|
1109
|
+
async init(_config, _runtime) {
|
|
1110
|
+
try {
|
|
1111
|
+
import_core9.logger.info("[ScratchpadPlugin] Initializing...");
|
|
1112
|
+
import_core9.logger.info("[ScratchpadPlugin] Initialized successfully");
|
|
1113
|
+
} catch (error) {
|
|
1114
|
+
import_core9.logger.error("[ScratchpadPlugin] Error initializing:", error instanceof Error ? error.message : String(error));
|
|
1115
|
+
throw error;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
var typescript_default = scratchpadPlugin;
|
|
1120
|
+
|
|
1121
|
+
//# debugId=3C48A2EDC93D045364756E2164756E21
|