@grec0/memory-bank-mcp 0.1.30 → 0.1.32
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.
|
@@ -115,6 +115,8 @@ export class AgentBoardSqlite {
|
|
|
115
115
|
SET status = ?, focus = ?, last_heartbeat = ?
|
|
116
116
|
WHERE id = ? AND project_id = ?
|
|
117
117
|
`).run(status, focus, now, resolvedId, this.projectId);
|
|
118
|
+
// Flush WAL so external readers can see changes
|
|
119
|
+
databaseManager.flushForExternalReaders();
|
|
118
120
|
}
|
|
119
121
|
/**
|
|
120
122
|
* Update heartbeat for an agent
|
|
@@ -127,6 +129,8 @@ export class AgentBoardSqlite {
|
|
|
127
129
|
SET last_heartbeat = ?
|
|
128
130
|
WHERE id = ? AND project_id = ?
|
|
129
131
|
`).run(now, agentId, this.projectId);
|
|
132
|
+
// Flush WAL so external readers can see changes
|
|
133
|
+
databaseManager.flushForExternalReaders();
|
|
130
134
|
}
|
|
131
135
|
/**
|
|
132
136
|
* Get all agents for this project (for session history view)
|
|
@@ -283,6 +287,7 @@ export class AgentBoardSqlite {
|
|
|
283
287
|
WHERE resource = ? AND project_id = ? AND agent_id = ?
|
|
284
288
|
`).run(resource, this.projectId, agentId);
|
|
285
289
|
this.logMessage(agentId, `Released lock on ${resource}`);
|
|
290
|
+
// Note: logMessage already flushes
|
|
286
291
|
}
|
|
287
292
|
/**
|
|
288
293
|
* Get all locks for this project
|
|
@@ -310,6 +315,8 @@ export class AgentBoardSqlite {
|
|
|
310
315
|
DELETE FROM locks
|
|
311
316
|
WHERE agent_id = ? AND project_id = ?
|
|
312
317
|
`).run(agentId, this.projectId);
|
|
318
|
+
// Flush WAL so external readers can see changes
|
|
319
|
+
databaseManager.flushForExternalReaders();
|
|
313
320
|
return result.changes;
|
|
314
321
|
}
|
|
315
322
|
/**
|
|
@@ -323,6 +330,8 @@ export class AgentBoardSqlite {
|
|
|
323
330
|
SELECT id FROM agents WHERE project_id = ? AND status = 'ACTIVE'
|
|
324
331
|
)
|
|
325
332
|
`).run(this.projectId, this.projectId);
|
|
333
|
+
// Flush WAL so external readers can see changes
|
|
334
|
+
databaseManager.flushForExternalReaders();
|
|
326
335
|
return result.changes;
|
|
327
336
|
}
|
|
328
337
|
// ========================================================================
|
|
@@ -330,6 +339,7 @@ export class AgentBoardSqlite {
|
|
|
330
339
|
// ========================================================================
|
|
331
340
|
/**
|
|
332
341
|
* Log a session event
|
|
342
|
+
* Auto-flushes WAL for external readers since this tracks all agent actions
|
|
333
343
|
*/
|
|
334
344
|
logSessionEvent(sessionId, eventType, eventData, agentId) {
|
|
335
345
|
const db = databaseManager.getConnection();
|
|
@@ -338,6 +348,8 @@ export class AgentBoardSqlite {
|
|
|
338
348
|
INSERT INTO session_events (project_id, session_id, agent_id, event_type, event_data, timestamp)
|
|
339
349
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
340
350
|
`).run(this.projectId, sessionId, agentId || null, eventType, JSON.stringify(eventData), now);
|
|
351
|
+
// Flush WAL so external readers (sql.js) can see changes
|
|
352
|
+
databaseManager.flushForExternalReaders();
|
|
341
353
|
}
|
|
342
354
|
/**
|
|
343
355
|
* Get session history for a specific session
|
|
@@ -390,6 +402,7 @@ export class AgentBoardSqlite {
|
|
|
390
402
|
// ========================================================================
|
|
391
403
|
/**
|
|
392
404
|
* Log a message
|
|
405
|
+
* Auto-flushes WAL for external readers since this is called at end of most operations
|
|
393
406
|
*/
|
|
394
407
|
logMessage(agentId, message) {
|
|
395
408
|
const db = databaseManager.getConnection();
|
|
@@ -398,6 +411,8 @@ export class AgentBoardSqlite {
|
|
|
398
411
|
INSERT INTO messages (project_id, agent_id, message, timestamp)
|
|
399
412
|
VALUES (?, ?, ?, ?)
|
|
400
413
|
`).run(this.projectId, agentId, message, now);
|
|
414
|
+
// Flush WAL so external readers (sql.js) can see changes
|
|
415
|
+
databaseManager.flushForExternalReaders();
|
|
401
416
|
}
|
|
402
417
|
/**
|
|
403
418
|
* Get recent messages
|
|
@@ -469,6 +484,8 @@ export function cleanupStaleAgents(staleMinutes = 30) {
|
|
|
469
484
|
WHERE status = 'ACTIVE'
|
|
470
485
|
AND datetime(last_heartbeat) < datetime('now', '-' || ? || ' minutes')
|
|
471
486
|
`).run(staleMinutes);
|
|
487
|
+
// Flush WAL so external readers can see changes
|
|
488
|
+
databaseManager.flushForExternalReaders();
|
|
472
489
|
return result.changes;
|
|
473
490
|
}
|
|
474
491
|
/**
|
|
@@ -482,5 +499,7 @@ export function cleanupAllOrphanedLocks() {
|
|
|
482
499
|
SELECT id FROM agents WHERE status = 'ACTIVE'
|
|
483
500
|
)
|
|
484
501
|
`).run();
|
|
502
|
+
// Flush WAL so external readers can see changes
|
|
503
|
+
databaseManager.flushForExternalReaders();
|
|
485
504
|
return result.changes;
|
|
486
505
|
}
|
package/dist/common/database.js
CHANGED
|
@@ -188,11 +188,69 @@ class DatabaseManager {
|
|
|
188
188
|
return fs.existsSync(this.dbPath);
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
|
-
* Run a transaction with automatic rollback on error
|
|
191
|
+
* Run a transaction with automatic rollback on error.
|
|
192
|
+
* Automatically flushes WAL after successful transaction for external readers.
|
|
192
193
|
*/
|
|
193
194
|
transaction(fn) {
|
|
194
195
|
const db = this.getConnection();
|
|
195
|
-
|
|
196
|
+
const result = db.transaction(fn)();
|
|
197
|
+
// Auto-flush after any transaction so sql.js can read the changes
|
|
198
|
+
this.flushForExternalReaders();
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Execute a write operation (INSERT, UPDATE, DELETE) and auto-flush WAL.
|
|
203
|
+
* Use this for single statements that modify data.
|
|
204
|
+
* For multiple related writes, use transaction() instead.
|
|
205
|
+
*
|
|
206
|
+
* @param sql - SQL statement to execute
|
|
207
|
+
* @param params - Parameters to bind to the statement
|
|
208
|
+
* @returns RunResult with changes count and lastInsertRowid
|
|
209
|
+
*/
|
|
210
|
+
runWrite(sql, ...params) {
|
|
211
|
+
const db = this.getConnection();
|
|
212
|
+
const result = db.prepare(sql).run(...params);
|
|
213
|
+
// Auto-flush so sql.js can read the changes immediately
|
|
214
|
+
this.flushForExternalReaders();
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Force WAL checkpoint to flush pending writes to the main database file.
|
|
219
|
+
* This is necessary for external readers (like sql.js in VS Code extension)
|
|
220
|
+
* that cannot read WAL files directly.
|
|
221
|
+
*
|
|
222
|
+
* @param mode - Checkpoint mode:
|
|
223
|
+
* - 'PASSIVE': Checkpoint as much as possible without blocking (default)
|
|
224
|
+
* - 'FULL': Wait for all readers, then checkpoint everything
|
|
225
|
+
* - 'RESTART': Like FULL, but also restart the WAL file
|
|
226
|
+
* - 'TRUNCATE': Like RESTART, but also truncate WAL file to zero bytes
|
|
227
|
+
* @returns Object with busy (pages that couldn't be checkpointed) and log (total WAL pages)
|
|
228
|
+
*/
|
|
229
|
+
checkpoint(mode = 'PASSIVE') {
|
|
230
|
+
if (!this.db) {
|
|
231
|
+
return { busy: 0, log: 0 };
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
const result = this.db.pragma(`wal_checkpoint(${mode})`);
|
|
235
|
+
const checkpointResult = result[0] || { busy: 0, log: 0 };
|
|
236
|
+
// Only log if there were actual pages to flush (reduce noise)
|
|
237
|
+
if (checkpointResult.log > 0) {
|
|
238
|
+
console.error(`[Database] WAL checkpoint: ${checkpointResult.log - checkpointResult.busy}/${checkpointResult.log} pages flushed`);
|
|
239
|
+
}
|
|
240
|
+
return checkpointResult;
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
console.error(`[Database] Checkpoint error: ${error}`);
|
|
244
|
+
return { busy: 0, log: 0 };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Perform a full checkpoint and ensure all data is written to main DB file.
|
|
249
|
+
* Called automatically after write operations.
|
|
250
|
+
* Can also be called manually if needed.
|
|
251
|
+
*/
|
|
252
|
+
flushForExternalReaders() {
|
|
253
|
+
this.checkpoint('TRUNCATE');
|
|
196
254
|
}
|
|
197
255
|
}
|
|
198
256
|
// Export singleton instance
|
package/dist/common/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Version of the MCP Kanban server
|
|
2
|
-
export const VERSION = "0.1.
|
|
2
|
+
export const VERSION = "0.1.31";
|
|
@@ -4,7 +4,7 @@ import { sessionState } from '../common/sessionState.js';
|
|
|
4
4
|
import { sessionLogger } from '../common/sessionLogger.js';
|
|
5
5
|
import os from 'os';
|
|
6
6
|
export async function manageAgentsTool(params) {
|
|
7
|
-
const { projectId, action, agentId, sessionId, status, focus, resource, workspacePath } = params;
|
|
7
|
+
const { projectId, action, agentId, sessionId, status, focus, resource, taskId, workspacePath } = params;
|
|
8
8
|
// For register action, workspacePath is REQUIRED to correctly register the project
|
|
9
9
|
if (action === 'register' && !workspacePath) {
|
|
10
10
|
throw new Error('workspacePath is REQUIRED for register action. Please provide the absolute path to the project workspace.');
|
|
@@ -72,6 +72,42 @@ export async function manageAgentsTool(params) {
|
|
|
72
72
|
case 'get_board':
|
|
73
73
|
const content = await board.getBoardContent();
|
|
74
74
|
return { success: true, content };
|
|
75
|
+
case 'complete_task':
|
|
76
|
+
if (!taskId)
|
|
77
|
+
throw new Error('taskId is required for complete_task');
|
|
78
|
+
const completeAgentId = agentId
|
|
79
|
+
? await board.resolveActiveAgentId(agentId)
|
|
80
|
+
: sessionState.getCurrentAgentId() || 'SYSTEM';
|
|
81
|
+
await board.completeTask(taskId, completeAgentId);
|
|
82
|
+
return {
|
|
83
|
+
success: true,
|
|
84
|
+
message: `Task ${taskId} marked as COMPLETED by ${completeAgentId}`,
|
|
85
|
+
taskId,
|
|
86
|
+
completedBy: completeAgentId
|
|
87
|
+
};
|
|
88
|
+
case 'claim_task':
|
|
89
|
+
if (!taskId)
|
|
90
|
+
throw new Error('taskId is required for claim_task');
|
|
91
|
+
const claimAgentId = agentId
|
|
92
|
+
? await board.resolveActiveAgentId(agentId)
|
|
93
|
+
: sessionState.getCurrentAgentId();
|
|
94
|
+
if (!claimAgentId)
|
|
95
|
+
throw new Error('agentId is required for claim_task (or register first)');
|
|
96
|
+
const taskClaimed = board.claimTask(taskId, claimAgentId);
|
|
97
|
+
if (taskClaimed) {
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
message: `Task ${taskId} claimed by ${claimAgentId}`,
|
|
101
|
+
taskId,
|
|
102
|
+
claimedBy: claimAgentId
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
message: `Task ${taskId} could not be claimed (already claimed or doesn't exist)`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
75
111
|
default:
|
|
76
112
|
throw new Error(`Unknown action: ${action}`);
|
|
77
113
|
}
|
|
@@ -96,7 +132,7 @@ export const manageAgentsToolDefinition = {
|
|
|
96
132
|
action: {
|
|
97
133
|
type: "string",
|
|
98
134
|
description: "Acción a realizar",
|
|
99
|
-
enum: ["register", "update_status", "claim_resource", "release_resource", "get_board"],
|
|
135
|
+
enum: ["register", "update_status", "claim_resource", "release_resource", "get_board", "complete_task", "claim_task"],
|
|
100
136
|
},
|
|
101
137
|
agentId: {
|
|
102
138
|
type: "string",
|
|
@@ -118,9 +154,13 @@ export const manageAgentsToolDefinition = {
|
|
|
118
154
|
type: "string",
|
|
119
155
|
description: "Identificador del recurso a bloquear (ej: 'src/auth/').",
|
|
120
156
|
},
|
|
157
|
+
taskId: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "ID de la tarea para complete_task o claim_task (ej: 'EXT-123456', 'TASK-789012').",
|
|
160
|
+
},
|
|
121
161
|
workspacePath: {
|
|
122
162
|
type: "string",
|
|
123
|
-
description: "RUTA ABSOLUTA al directorio raíz del workspace. IMPORTANTE
|
|
163
|
+
description: "RUTA ABSOLUTA al directorio raíz del workspace. IMPORTANTE para registro correcto del proyecto.",
|
|
124
164
|
},
|
|
125
165
|
},
|
|
126
166
|
required: ["projectId", "action"],
|