feat: integrate Memento MCP for shared memory across agents

- Add MEMENTO_MCP_SSE_URL/ACCESS_KEY/REMOTE_PATH to readEnvFile
- Merge .env vars into cleanEnv for runner process inheritance
- Claude Code runner: add memento-mcp via mcp-remote in mcpServers
- Codex runner: inject memento-mcp section into config.toml
- Various improvements: CI watch, task scheduler, DB, IPC auth
This commit is contained in:
Eyejoker
2026-03-20 00:12:43 +09:00
parent e7200a1ed8
commit 41afcb91cf
17 changed files with 572 additions and 59 deletions

117
src/db.ts
View File

@@ -48,6 +48,9 @@ function createSchema(database: Database.Database): void {
id TEXT PRIMARY KEY,
group_folder TEXT NOT NULL,
chat_jid TEXT NOT NULL,
agent_type TEXT,
status_message_id TEXT,
status_started_at TEXT,
prompt TEXT NOT NULL,
schedule_type TEXT NOT NULL,
schedule_value TEXT NOT NULL,
@@ -107,6 +110,47 @@ function createSchema(database: Database.Database): void {
/* column already exists */
}
try {
database.exec(`ALTER TABLE scheduled_tasks ADD COLUMN agent_type TEXT`);
} catch {
/* column already exists */
}
try {
database.exec(`ALTER TABLE scheduled_tasks ADD COLUMN status_message_id TEXT`);
} catch {
/* column already exists */
}
try {
database.exec(`ALTER TABLE scheduled_tasks ADD COLUMN status_started_at TEXT`);
} catch {
/* column already exists */
}
database.exec(`
UPDATE scheduled_tasks
SET agent_type = COALESCE(
(
SELECT CASE WHEN COUNT(*) = 1 THEN MIN(agent_type) ELSE NULL END
FROM registered_groups
WHERE jid = scheduled_tasks.chat_jid
AND folder = scheduled_tasks.group_folder
),
(
SELECT CASE WHEN COUNT(*) = 1 THEN MIN(agent_type) ELSE NULL END
FROM registered_groups
WHERE jid = scheduled_tasks.chat_jid
),
(
SELECT CASE WHEN COUNT(*) = 1 THEN MIN(agent_type) ELSE NULL END
FROM registered_groups
WHERE folder = scheduled_tasks.group_folder
)
)
WHERE agent_type IS NULL;
`);
// Add is_bot_message column if it doesn't exist (migration for existing DBs)
try {
database.exec(
@@ -420,17 +464,31 @@ export function getLastHumanMessageTimestamp(chatJid: string): string | null {
}
export function createTask(
task: Omit<ScheduledTask, 'last_run' | 'last_result'>,
task: Omit<
ScheduledTask,
| 'last_run'
| 'last_result'
| 'agent_type'
| 'status_message_id'
| 'status_started_at'
> & {
agent_type?: AgentType | null;
status_message_id?: string | null;
status_started_at?: string | null;
},
): void {
db.prepare(
`
INSERT INTO scheduled_tasks (id, group_folder, chat_jid, prompt, schedule_type, schedule_value, context_mode, next_run, status, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO scheduled_tasks (id, group_folder, chat_jid, agent_type, status_message_id, status_started_at, prompt, schedule_type, schedule_value, context_mode, next_run, status, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`,
).run(
task.id,
task.group_folder,
task.chat_jid,
task.agent_type || SERVICE_AGENT_TYPE,
task.status_message_id || null,
task.status_started_at || null,
task.prompt,
task.schedule_type,
task.schedule_value,
@@ -447,7 +505,18 @@ export function getTaskById(id: string): ScheduledTask | undefined {
| undefined;
}
export function getTasksForGroup(groupFolder: string): ScheduledTask[] {
export function getTasksForGroup(
groupFolder: string,
agentType?: AgentType,
): ScheduledTask[] {
if (agentType) {
return db
.prepare(
'SELECT * FROM scheduled_tasks WHERE group_folder = ? AND agent_type = ? ORDER BY created_at DESC',
)
.all(groupFolder, agentType) as ScheduledTask[];
}
return db
.prepare(
'SELECT * FROM scheduled_tasks WHERE group_folder = ? ORDER BY created_at DESC',
@@ -455,7 +524,15 @@ export function getTasksForGroup(groupFolder: string): ScheduledTask[] {
.all(groupFolder) as ScheduledTask[];
}
export function getAllTasks(): ScheduledTask[] {
export function getAllTasks(agentType?: AgentType): ScheduledTask[] {
if (agentType) {
return db
.prepare(
'SELECT * FROM scheduled_tasks WHERE agent_type = ? ORDER BY created_at DESC',
)
.all(agentType) as ScheduledTask[];
}
return db
.prepare('SELECT * FROM scheduled_tasks ORDER BY created_at DESC')
.all() as ScheduledTask[];
@@ -502,23 +579,47 @@ export function updateTask(
).run(...values);
}
export function updateTaskStatusTracking(
id: string,
updates: Partial<Pick<ScheduledTask, 'status_message_id' | 'status_started_at'>>,
): void {
const fields: string[] = [];
const values: unknown[] = [];
if (updates.status_message_id !== undefined) {
fields.push('status_message_id = ?');
values.push(updates.status_message_id);
}
if (updates.status_started_at !== undefined) {
fields.push('status_started_at = ?');
values.push(updates.status_started_at);
}
if (fields.length === 0) return;
values.push(id);
db.prepare(
`UPDATE scheduled_tasks SET ${fields.join(', ')} WHERE id = ?`,
).run(...values);
}
export function deleteTask(id: string): void {
// Delete child records first (FK constraint)
db.prepare('DELETE FROM task_run_logs WHERE task_id = ?').run(id);
db.prepare('DELETE FROM scheduled_tasks WHERE id = ?').run(id);
}
export function getDueTasks(): ScheduledTask[] {
export function getDueTasks(agentType: AgentType = SERVICE_AGENT_TYPE): ScheduledTask[] {
const now = new Date().toISOString();
return db
.prepare(
`
SELECT * FROM scheduled_tasks
WHERE status = 'active' AND next_run IS NOT NULL AND next_run <= ?
WHERE status = 'active' AND agent_type = ? AND next_run IS NOT NULL AND next_run <= ?
ORDER BY next_run
`,
)
.all(now) as ScheduledTask[];
.all(agentType, now) as ScheduledTask[];
}
export function updateTaskAfterRun(