@geekbeer/minion 2.70.2 → 2.73.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/core/db.js +297 -0
- package/core/lib/thread-watcher.js +3 -6
- package/core/routes/daily-logs.js +17 -11
- package/core/routes/memory.js +16 -10
- package/core/stores/chat-store.js +138 -76
- package/core/stores/daily-log-store.js +84 -105
- package/core/stores/execution-store.js +59 -84
- package/core/stores/memory-store.js +84 -177
- package/core/stores/routine-store.js +55 -72
- package/core/stores/workflow-store.js +52 -69
- package/docs/api-reference.md +6 -2
- package/linux/routes/chat.js +54 -7
- package/linux/server.js +3 -1
- package/package.json +2 -1
- package/roles/engineer.md +12 -0
- package/roles/pm.md +16 -0
- package/rules/core.md +66 -0
- package/win/routes/chat.js +54 -7
- package/win/server.js +2 -0
package/core/db.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Database Module
|
|
3
|
+
* Provides a singleton SQLite database connection for all minion stores.
|
|
4
|
+
* Uses better-sqlite3 (synchronous API) with WAL mode for performance.
|
|
5
|
+
*
|
|
6
|
+
* Database file: $DATA_DIR/minion.db
|
|
7
|
+
*
|
|
8
|
+
* FTS5 uses the trigram tokenizer for Japanese/CJK language support.
|
|
9
|
+
* Trigram splits text into 3-character sequences, enabling substring search
|
|
10
|
+
* in any language without a language-specific tokenizer.
|
|
11
|
+
* Note: trigram requires search queries of 3+ characters.
|
|
12
|
+
* Shorter queries fall back to LIKE-based search in the store layer.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require('path')
|
|
16
|
+
const fs = require('fs')
|
|
17
|
+
|
|
18
|
+
const { DATA_DIR } = require('./lib/platform')
|
|
19
|
+
const { config } = require('./config')
|
|
20
|
+
|
|
21
|
+
let _db = null
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the database file path.
|
|
25
|
+
* Uses DATA_DIR if accessible, otherwise falls back to HOME_DIR.
|
|
26
|
+
*/
|
|
27
|
+
function resolveDbPath() {
|
|
28
|
+
try {
|
|
29
|
+
fs.accessSync(DATA_DIR, fs.constants.W_OK)
|
|
30
|
+
return path.join(DATA_DIR, 'minion.db')
|
|
31
|
+
} catch {
|
|
32
|
+
return path.join(config.HOME_DIR, 'minion.db')
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the singleton database connection.
|
|
38
|
+
* Initializes the database and schema on first call.
|
|
39
|
+
* @returns {import('better-sqlite3').Database}
|
|
40
|
+
*/
|
|
41
|
+
function getDb() {
|
|
42
|
+
if (_db) return _db
|
|
43
|
+
|
|
44
|
+
const dbPath = resolveDbPath()
|
|
45
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true })
|
|
46
|
+
|
|
47
|
+
const Database = require('better-sqlite3')
|
|
48
|
+
_db = new Database(dbPath)
|
|
49
|
+
|
|
50
|
+
// Performance & integrity settings
|
|
51
|
+
_db.pragma('journal_mode = WAL')
|
|
52
|
+
_db.pragma('foreign_keys = ON')
|
|
53
|
+
|
|
54
|
+
initSchema(_db)
|
|
55
|
+
migrateSchema(_db)
|
|
56
|
+
|
|
57
|
+
console.log(`[DB] SQLite database initialized: ${dbPath}`)
|
|
58
|
+
return _db
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Initialize all tables, indices, FTS virtual tables, and triggers.
|
|
63
|
+
* All statements use IF NOT EXISTS for idempotency.
|
|
64
|
+
*/
|
|
65
|
+
function initSchema(db) {
|
|
66
|
+
db.exec(`
|
|
67
|
+
-- ==================== memories ====================
|
|
68
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
69
|
+
id TEXT PRIMARY KEY,
|
|
70
|
+
title TEXT NOT NULL DEFAULT 'Untitled',
|
|
71
|
+
category TEXT NOT NULL DEFAULT 'reference'
|
|
72
|
+
CHECK (category IN ('user', 'feedback', 'project', 'reference')),
|
|
73
|
+
content TEXT NOT NULL DEFAULT '',
|
|
74
|
+
created_at TEXT NOT NULL,
|
|
75
|
+
updated_at TEXT NOT NULL
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(category);
|
|
79
|
+
CREATE INDEX IF NOT EXISTS idx_memories_updated_at ON memories(updated_at DESC);
|
|
80
|
+
|
|
81
|
+
-- FTS5 with trigram tokenizer for Japanese/CJK support
|
|
82
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
83
|
+
title,
|
|
84
|
+
content,
|
|
85
|
+
content=memories,
|
|
86
|
+
content_rowid=rowid,
|
|
87
|
+
tokenize='trigram'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
-- Triggers to keep FTS index in sync with memories table
|
|
91
|
+
CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
|
|
92
|
+
INSERT INTO memories_fts(rowid, title, content)
|
|
93
|
+
VALUES (new.rowid, new.title, new.content);
|
|
94
|
+
END;
|
|
95
|
+
|
|
96
|
+
CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
|
|
97
|
+
INSERT INTO memories_fts(memories_fts, rowid, title, content)
|
|
98
|
+
VALUES ('delete', old.rowid, old.title, old.content);
|
|
99
|
+
END;
|
|
100
|
+
|
|
101
|
+
CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
|
|
102
|
+
INSERT INTO memories_fts(memories_fts, rowid, title, content)
|
|
103
|
+
VALUES ('delete', old.rowid, old.title, old.content);
|
|
104
|
+
INSERT INTO memories_fts(rowid, title, content)
|
|
105
|
+
VALUES (new.rowid, new.title, new.content);
|
|
106
|
+
END;
|
|
107
|
+
|
|
108
|
+
-- ==================== daily_logs ====================
|
|
109
|
+
CREATE TABLE IF NOT EXISTS daily_logs (
|
|
110
|
+
date TEXT PRIMARY KEY,
|
|
111
|
+
content TEXT NOT NULL DEFAULT '',
|
|
112
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
113
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
-- FTS5 with trigram tokenizer for Japanese/CJK support
|
|
117
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS daily_logs_fts USING fts5(
|
|
118
|
+
content,
|
|
119
|
+
content=daily_logs,
|
|
120
|
+
content_rowid=rowid,
|
|
121
|
+
tokenize='trigram'
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
CREATE TRIGGER IF NOT EXISTS daily_logs_ai AFTER INSERT ON daily_logs BEGIN
|
|
125
|
+
INSERT INTO daily_logs_fts(rowid, content)
|
|
126
|
+
VALUES (new.rowid, new.content);
|
|
127
|
+
END;
|
|
128
|
+
|
|
129
|
+
CREATE TRIGGER IF NOT EXISTS daily_logs_ad AFTER DELETE ON daily_logs BEGIN
|
|
130
|
+
INSERT INTO daily_logs_fts(daily_logs_fts, rowid, content)
|
|
131
|
+
VALUES ('delete', old.rowid, old.content);
|
|
132
|
+
END;
|
|
133
|
+
|
|
134
|
+
CREATE TRIGGER IF NOT EXISTS daily_logs_au AFTER UPDATE ON daily_logs BEGIN
|
|
135
|
+
INSERT INTO daily_logs_fts(daily_logs_fts, rowid, content)
|
|
136
|
+
VALUES ('delete', old.rowid, old.content);
|
|
137
|
+
INSERT INTO daily_logs_fts(rowid, content)
|
|
138
|
+
VALUES (new.rowid, new.content);
|
|
139
|
+
END;
|
|
140
|
+
|
|
141
|
+
-- ==================== chat_sessions ====================
|
|
142
|
+
CREATE TABLE IF NOT EXISTS chat_sessions (
|
|
143
|
+
session_id TEXT PRIMARY KEY,
|
|
144
|
+
turn_count INTEGER NOT NULL DEFAULT 0,
|
|
145
|
+
created_at INTEGER NOT NULL,
|
|
146
|
+
updated_at INTEGER NOT NULL
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
150
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
151
|
+
session_id TEXT NOT NULL REFERENCES chat_sessions(session_id) ON DELETE CASCADE,
|
|
152
|
+
role TEXT NOT NULL,
|
|
153
|
+
content TEXT NOT NULL,
|
|
154
|
+
timestamp INTEGER NOT NULL
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
CREATE INDEX IF NOT EXISTS idx_chat_messages_session ON chat_messages(session_id, id);
|
|
158
|
+
|
|
159
|
+
-- ==================== executions ====================
|
|
160
|
+
CREATE TABLE IF NOT EXISTS executions (
|
|
161
|
+
id TEXT PRIMARY KEY,
|
|
162
|
+
skill_name TEXT,
|
|
163
|
+
workflow_id TEXT,
|
|
164
|
+
status TEXT,
|
|
165
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
166
|
+
data TEXT NOT NULL
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_executions_workflow ON executions(workflow_id);
|
|
170
|
+
CREATE INDEX IF NOT EXISTS idx_executions_created ON executions(created_at DESC);
|
|
171
|
+
|
|
172
|
+
-- ==================== routines ====================
|
|
173
|
+
CREATE TABLE IF NOT EXISTS routines (
|
|
174
|
+
id TEXT PRIMARY KEY,
|
|
175
|
+
name TEXT NOT NULL,
|
|
176
|
+
data TEXT NOT NULL
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
CREATE INDEX IF NOT EXISTS idx_routines_name ON routines(name);
|
|
180
|
+
|
|
181
|
+
-- ==================== workflows ====================
|
|
182
|
+
CREATE TABLE IF NOT EXISTS workflows (
|
|
183
|
+
id TEXT PRIMARY KEY,
|
|
184
|
+
name TEXT NOT NULL,
|
|
185
|
+
data TEXT NOT NULL
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
CREATE INDEX IF NOT EXISTS idx_workflows_name ON workflows(name);
|
|
189
|
+
|
|
190
|
+
-- ==================== schema_version ====================
|
|
191
|
+
CREATE TABLE IF NOT EXISTS schema_version (
|
|
192
|
+
version INTEGER PRIMARY KEY,
|
|
193
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
194
|
+
);
|
|
195
|
+
`)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Run schema migrations for existing databases.
|
|
200
|
+
* Each migration checks schema_version to avoid re-running.
|
|
201
|
+
*/
|
|
202
|
+
function migrateSchema(db) {
|
|
203
|
+
const currentVersion = db.prepare(
|
|
204
|
+
'SELECT COALESCE(MAX(version), 0) as v FROM schema_version'
|
|
205
|
+
).get().v
|
|
206
|
+
|
|
207
|
+
if (currentVersion < 1) {
|
|
208
|
+
// Migration 1: Recreate FTS tables with trigram tokenizer for Japanese support.
|
|
209
|
+
// The default unicode61 tokenizer doesn't tokenize CJK text correctly.
|
|
210
|
+
console.log('[DB] Migration 1: Switching FTS5 to trigram tokenizer...')
|
|
211
|
+
|
|
212
|
+
db.exec(`
|
|
213
|
+
-- Drop old FTS tables and triggers (may use unicode61 tokenizer)
|
|
214
|
+
DROP TRIGGER IF EXISTS memories_ai;
|
|
215
|
+
DROP TRIGGER IF EXISTS memories_ad;
|
|
216
|
+
DROP TRIGGER IF EXISTS memories_au;
|
|
217
|
+
DROP TABLE IF EXISTS memories_fts;
|
|
218
|
+
|
|
219
|
+
DROP TRIGGER IF EXISTS daily_logs_ai;
|
|
220
|
+
DROP TRIGGER IF EXISTS daily_logs_ad;
|
|
221
|
+
DROP TRIGGER IF EXISTS daily_logs_au;
|
|
222
|
+
DROP TABLE IF EXISTS daily_logs_fts;
|
|
223
|
+
|
|
224
|
+
-- Recreate with trigram tokenizer
|
|
225
|
+
CREATE VIRTUAL TABLE memories_fts USING fts5(
|
|
226
|
+
title,
|
|
227
|
+
content,
|
|
228
|
+
content=memories,
|
|
229
|
+
content_rowid=rowid,
|
|
230
|
+
tokenize='trigram'
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
CREATE TRIGGER memories_ai AFTER INSERT ON memories BEGIN
|
|
234
|
+
INSERT INTO memories_fts(rowid, title, content)
|
|
235
|
+
VALUES (new.rowid, new.title, new.content);
|
|
236
|
+
END;
|
|
237
|
+
|
|
238
|
+
CREATE TRIGGER memories_ad AFTER DELETE ON memories BEGIN
|
|
239
|
+
INSERT INTO memories_fts(memories_fts, rowid, title, content)
|
|
240
|
+
VALUES ('delete', old.rowid, old.title, old.content);
|
|
241
|
+
END;
|
|
242
|
+
|
|
243
|
+
CREATE TRIGGER memories_au AFTER UPDATE ON memories BEGIN
|
|
244
|
+
INSERT INTO memories_fts(memories_fts, rowid, title, content)
|
|
245
|
+
VALUES ('delete', old.rowid, old.title, old.content);
|
|
246
|
+
INSERT INTO memories_fts(rowid, title, content)
|
|
247
|
+
VALUES (new.rowid, new.title, new.content);
|
|
248
|
+
END;
|
|
249
|
+
|
|
250
|
+
CREATE VIRTUAL TABLE daily_logs_fts USING fts5(
|
|
251
|
+
content,
|
|
252
|
+
content=daily_logs,
|
|
253
|
+
content_rowid=rowid,
|
|
254
|
+
tokenize='trigram'
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
CREATE TRIGGER daily_logs_ai AFTER INSERT ON daily_logs BEGIN
|
|
258
|
+
INSERT INTO daily_logs_fts(rowid, content)
|
|
259
|
+
VALUES (new.rowid, new.content);
|
|
260
|
+
END;
|
|
261
|
+
|
|
262
|
+
CREATE TRIGGER daily_logs_ad AFTER DELETE ON daily_logs BEGIN
|
|
263
|
+
INSERT INTO daily_logs_fts(daily_logs_fts, rowid, content)
|
|
264
|
+
VALUES ('delete', old.rowid, old.content);
|
|
265
|
+
END;
|
|
266
|
+
|
|
267
|
+
CREATE TRIGGER daily_logs_au AFTER UPDATE ON daily_logs BEGIN
|
|
268
|
+
INSERT INTO daily_logs_fts(daily_logs_fts, rowid, content)
|
|
269
|
+
VALUES ('delete', old.rowid, old.content);
|
|
270
|
+
INSERT INTO daily_logs_fts(rowid, content)
|
|
271
|
+
VALUES (new.rowid, new.content);
|
|
272
|
+
END;
|
|
273
|
+
|
|
274
|
+
-- Rebuild FTS index from existing data
|
|
275
|
+
INSERT INTO memories_fts(memories_fts) VALUES ('rebuild');
|
|
276
|
+
INSERT INTO daily_logs_fts(daily_logs_fts) VALUES ('rebuild');
|
|
277
|
+
|
|
278
|
+
INSERT INTO schema_version (version) VALUES (1);
|
|
279
|
+
`)
|
|
280
|
+
|
|
281
|
+
console.log('[DB] Migration 1 complete: FTS5 trigram tokenizer applied')
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Close the database connection.
|
|
287
|
+
* Call this during graceful shutdown to flush WAL.
|
|
288
|
+
*/
|
|
289
|
+
function closeDb() {
|
|
290
|
+
if (_db) {
|
|
291
|
+
_db.close()
|
|
292
|
+
_db = null
|
|
293
|
+
console.log('[DB] SQLite database closed')
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
module.exports = { getDb, closeDb }
|
|
@@ -194,8 +194,8 @@ async function evaluateWithLlm(threadSummary, threadDetail, allMessages, newMess
|
|
|
194
194
|
const messageHistory = recentMessages
|
|
195
195
|
.map(m => {
|
|
196
196
|
const sender = m.sender_minion_id
|
|
197
|
-
? (m.sender_minion_id === config.MINION_ID ? 'You' : `Minion(${m.sender_minion_id.slice(0, 8)})`)
|
|
198
|
-
: 'User'
|
|
197
|
+
? (m.sender_minion_id === config.MINION_ID ? 'You' : (m.sender_name || `Minion(${m.sender_minion_id.slice(0, 8)})`))
|
|
198
|
+
: (m.sender_name || 'User')
|
|
199
199
|
return `[${sender}] ${m.content}`
|
|
200
200
|
})
|
|
201
201
|
.join('\n')
|
|
@@ -238,10 +238,7 @@ ${messageHistory || '(メッセージなし)'}
|
|
|
238
238
|
- 自分のロール(${myProject.role})に関連する話題か
|
|
239
239
|
- 自分が貢献できる知見や意見があるか
|
|
240
240
|
- 既に十分な回答がある場合は重複を避ける
|
|
241
|
-
- 人間に聞くべき場合は @user
|
|
242
|
-
|
|
243
|
-
フォーマットルール:
|
|
244
|
-
- 他のミニオンに言及する場合は必ず Minion(IDの先頭8文字) の形式を使うこと(例: メッセージ履歴中の Minion(abc12345) をそのまま使用)`
|
|
241
|
+
- 人間に聞くべき場合は @user メンションを含めて返信する`
|
|
245
242
|
|
|
246
243
|
try {
|
|
247
244
|
const result = await llmCallFn(prompt)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Daily log endpoints
|
|
3
3
|
*
|
|
4
|
-
* Provides CRUD
|
|
5
|
-
* Logs are stored
|
|
4
|
+
* Provides CRUD + search for daily conversation summaries.
|
|
5
|
+
* Logs are stored in SQLite with FTS5 full-text search.
|
|
6
6
|
*
|
|
7
7
|
* Endpoints:
|
|
8
|
-
* GET /api/daily-logs - List all logs (
|
|
8
|
+
* GET /api/daily-logs - List all logs (or search with ?search=keyword)
|
|
9
9
|
* POST /api/daily-logs - Create a new daily log
|
|
10
10
|
* GET /api/daily-logs/:date - Get a specific day's log
|
|
11
11
|
* PUT /api/daily-logs/:date - Update a daily log
|
|
@@ -21,14 +21,20 @@ const dailyLogStore = require('../stores/daily-log-store')
|
|
|
21
21
|
*/
|
|
22
22
|
async function dailyLogRoutes(fastify) {
|
|
23
23
|
|
|
24
|
-
// GET /api/daily-logs - List
|
|
24
|
+
// GET /api/daily-logs - List or search logs
|
|
25
25
|
fastify.get('/api/daily-logs', async (request, reply) => {
|
|
26
26
|
if (!verifyToken(request)) {
|
|
27
27
|
reply.code(401)
|
|
28
28
|
return { success: false, error: 'Unauthorized' }
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const
|
|
31
|
+
const { search } = request.query || {}
|
|
32
|
+
if (search) {
|
|
33
|
+
const logs = dailyLogStore.searchLogs(search)
|
|
34
|
+
return { success: true, logs }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const logs = dailyLogStore.listLogs()
|
|
32
38
|
return { success: true, logs }
|
|
33
39
|
})
|
|
34
40
|
|
|
@@ -46,13 +52,13 @@ async function dailyLogRoutes(fastify) {
|
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
// Check if log already exists
|
|
49
|
-
const existing =
|
|
55
|
+
const existing = dailyLogStore.loadLog(date)
|
|
50
56
|
if (existing !== null) {
|
|
51
57
|
reply.code(409)
|
|
52
58
|
return { success: false, error: 'Log already exists for this date. Use PUT to update.' }
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
dailyLogStore.saveLog(date, content)
|
|
56
62
|
console.log(`[DailyLogs] Created log: ${date}`)
|
|
57
63
|
return { success: true, log: { date, content } }
|
|
58
64
|
})
|
|
@@ -65,7 +71,7 @@ async function dailyLogRoutes(fastify) {
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
const { date } = request.params
|
|
68
|
-
const content =
|
|
74
|
+
const content = dailyLogStore.loadLog(date)
|
|
69
75
|
if (content === null) {
|
|
70
76
|
reply.code(404)
|
|
71
77
|
return { success: false, error: 'Log not found' }
|
|
@@ -89,13 +95,13 @@ async function dailyLogRoutes(fastify) {
|
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
// Check if log exists
|
|
92
|
-
const existing =
|
|
98
|
+
const existing = dailyLogStore.loadLog(date)
|
|
93
99
|
if (existing === null) {
|
|
94
100
|
reply.code(404)
|
|
95
101
|
return { success: false, error: 'Log not found' }
|
|
96
102
|
}
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
dailyLogStore.saveLog(date, content)
|
|
99
105
|
console.log(`[DailyLogs] Updated log: ${date}`)
|
|
100
106
|
return { success: true, log: { date, content } }
|
|
101
107
|
})
|
|
@@ -108,7 +114,7 @@ async function dailyLogRoutes(fastify) {
|
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
const { date } = request.params
|
|
111
|
-
const deleted =
|
|
117
|
+
const deleted = dailyLogStore.deleteLog(date)
|
|
112
118
|
if (!deleted) {
|
|
113
119
|
reply.code(404)
|
|
114
120
|
return { success: false, error: 'Log not found' }
|
package/core/routes/memory.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory management endpoints
|
|
3
3
|
*
|
|
4
|
-
* Provides CRUD for the minion's long-term memory entries.
|
|
5
|
-
* Memory is stored
|
|
4
|
+
* Provides CRUD + search for the minion's long-term memory entries.
|
|
5
|
+
* Memory is stored in SQLite with FTS5 full-text search.
|
|
6
6
|
*
|
|
7
7
|
* Endpoints:
|
|
8
|
-
* GET /api/memory - List all
|
|
8
|
+
* GET /api/memory - List all entries (or search with ?search=keyword)
|
|
9
9
|
* GET /api/memory/:id - Get a single entry
|
|
10
10
|
* POST /api/memory - Create a new entry
|
|
11
11
|
* PUT /api/memory/:id - Update an entry
|
|
@@ -21,14 +21,20 @@ const memoryStore = require('../stores/memory-store')
|
|
|
21
21
|
*/
|
|
22
22
|
async function memoryRoutes(fastify) {
|
|
23
23
|
|
|
24
|
-
// GET /api/memory - List
|
|
24
|
+
// GET /api/memory - List or search entries
|
|
25
25
|
fastify.get('/api/memory', async (request, reply) => {
|
|
26
26
|
if (!verifyToken(request)) {
|
|
27
27
|
reply.code(401)
|
|
28
28
|
return { success: false, error: 'Unauthorized' }
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const
|
|
31
|
+
const { search } = request.query || {}
|
|
32
|
+
if (search) {
|
|
33
|
+
const entries = memoryStore.searchEntries(search)
|
|
34
|
+
return { success: true, entries }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const entries = memoryStore.listEntries()
|
|
32
38
|
return { success: true, entries }
|
|
33
39
|
})
|
|
34
40
|
|
|
@@ -39,7 +45,7 @@ async function memoryRoutes(fastify) {
|
|
|
39
45
|
return { success: false, error: 'Unauthorized' }
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
const entry =
|
|
48
|
+
const entry = memoryStore.loadEntry(request.params.id)
|
|
43
49
|
if (!entry) {
|
|
44
50
|
reply.code(404)
|
|
45
51
|
return { success: false, error: 'Entry not found' }
|
|
@@ -61,7 +67,7 @@ async function memoryRoutes(fastify) {
|
|
|
61
67
|
return { success: false, error: 'title and content are required' }
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
const entry =
|
|
70
|
+
const entry = memoryStore.saveEntry({ title, category, content })
|
|
65
71
|
console.log(`[Memory] Created entry: ${entry.id} (${entry.title})`)
|
|
66
72
|
return { success: true, entry }
|
|
67
73
|
})
|
|
@@ -73,14 +79,14 @@ async function memoryRoutes(fastify) {
|
|
|
73
79
|
return { success: false, error: 'Unauthorized' }
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
const existing =
|
|
82
|
+
const existing = memoryStore.loadEntry(request.params.id)
|
|
77
83
|
if (!existing) {
|
|
78
84
|
reply.code(404)
|
|
79
85
|
return { success: false, error: 'Entry not found' }
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
const { title, category, content } = request.body || {}
|
|
83
|
-
const entry =
|
|
89
|
+
const entry = memoryStore.saveEntry({
|
|
84
90
|
id: request.params.id,
|
|
85
91
|
title: title || existing.title,
|
|
86
92
|
category: category || existing.category,
|
|
@@ -98,7 +104,7 @@ async function memoryRoutes(fastify) {
|
|
|
98
104
|
return { success: false, error: 'Unauthorized' }
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
const deleted =
|
|
107
|
+
const deleted = memoryStore.deleteEntry(request.params.id)
|
|
102
108
|
if (!deleted) {
|
|
103
109
|
reply.code(404)
|
|
104
110
|
return { success: false, error: 'Entry not found' }
|