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

View File

@@ -531,6 +531,18 @@ async function runQuery(
NANOCLAW_IS_MAIN: containerInput.isMain ? '1' : '0',
},
},
...(process.env.MEMENTO_MCP_SSE_URL
? {
'memento-mcp': {
command: process.env.MEMENTO_MCP_REMOTE_PATH || 'mcp-remote',
args: [
process.env.MEMENTO_MCP_SSE_URL,
'--header',
`Authorization:Bearer ${process.env.MEMENTO_ACCESS_KEY || ''}`,
],
},
}
: {}),
},
hooks: {
PreCompact: [{ hooks: [createPreCompactHook(containerInput.assistantName)] }],

View File

@@ -12,6 +12,7 @@ import path from 'path';
import { CronExpressionParser } from 'cron-parser';
import {
buildCiWatchPrompt,
DEFAULT_WATCH_CI_CONTEXT_MODE,
normalizeWatchCiIntervalSeconds,
} from './watch-ci.js';
@@ -174,8 +175,8 @@ server.tool(
.describe('How often to poll in seconds. Default 60, minimum 30.'),
context_mode: z
.enum(['group', 'isolated'])
.default('group')
.describe('group=runs with chat history and memory, isolated=fresh session (include all context in check_instructions)'),
.default(DEFAULT_WATCH_CI_CONTEXT_MODE)
.describe('group=runs with chat history and memory, isolated=fresh session (include all context in check_instructions). Default: isolated.'),
target_group_jid: z
.string()
.optional()
@@ -211,7 +212,7 @@ server.tool(
prompt,
schedule_type: 'interval' as const,
schedule_value: String(pollSeconds * 1000),
context_mode: args.context_mode || 'group',
context_mode: args.context_mode || DEFAULT_WATCH_CI_CONTEXT_MODE,
targetJid,
createdBy: groupFolder,
timestamp: new Date().toISOString(),

View File

@@ -1,6 +1,7 @@
export const DEFAULT_WATCH_CI_INTERVAL_SECONDS = 60;
export const MIN_WATCH_CI_INTERVAL_SECONDS = 30;
export const MAX_WATCH_CI_INTERVAL_SECONDS = 3600;
export const DEFAULT_WATCH_CI_CONTEXT_MODE = 'isolated';
export interface BuildCiWatchPromptArgs {
taskId: string;
@@ -49,12 +50,21 @@ Check instructions:
${checkInstructions}
Rules:
- Use the watch target and check instructions in this prompt as the source of truth for what to inspect.
- On each run, check whether the target is still queued, pending, running, in progress, or otherwise non-terminal.
- If it is still not finished, send no visible message and end this run quietly.
- If it reached a terminal state such as success, failure, cancelled, timed out, neutral, skipped, or action required:
1. Send exactly one concise completion message with \`send_message\`.
2. Include the final status and only the most important details.
3. Call \`cancel_task\` with task_id "${taskId}" so this watcher stops itself.
2. Format it as a short multiline summary when possible, not one long paragraph.
3. Preferred shape:
- First line: \`CI 완료: <target>\`
- Second line: \`판정: <one-line conclusion>\`
- Then 2-4 flat bullet points with only the most important metrics, errors, or comparisons.
- Optional final line: \`다음: <next action>\` if a concrete follow-up is needed.
4. Adapt the content to the specific CI. Do not invent fixed fields when they do not fit.
5. Avoid tables unless they are clearly the shortest readable format.
6. Keep the message compact and easy for other agents to parse.
7. Call \`cancel_task\` with task_id "${taskId}" so this watcher stops itself.
- If you hit a transient problem such as a rate limit, network issue, or temporary auth failure, send no visible message and leave the task active for the next retry.
- Prefer no normal final response. Use \`send_message\` for the completion message, and keep any non-user-facing notes inside \`<internal>\` tags if needed.
- Do not claim continued monitoring after you cancel the task.

View File

@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
import {
buildCiWatchPrompt,
DEFAULT_WATCH_CI_CONTEXT_MODE,
normalizeWatchCiIntervalSeconds,
} from '../src/watch-ci.js';
@@ -18,6 +19,15 @@ describe('watch-ci helpers', () => {
expect(prompt).toContain('cancel_task');
expect(prompt).toContain('send_message');
expect(prompt).toContain('gh pr checks 42');
expect(prompt).toContain('CI 완료: <target>');
expect(prompt).toContain('판정: <one-line conclusion>');
expect(prompt).toContain(
'Use the watch target and check instructions in this prompt as the source of truth',
);
});
it('defaults CI watchers to isolated context', () => {
expect(DEFAULT_WATCH_CI_CONTEXT_MODE).toBe('isolated');
});
it('normalizes valid poll intervals', () => {

View File

@@ -8,7 +8,7 @@
"name": "ejclaw-codex-runner",
"version": "1.0.0",
"dependencies": {
"@openai/codex-sdk": "^0.114.0"
"@openai/codex-sdk": "^0.115.0"
},
"devDependencies": {
"@types/node": "^22.10.7",
@@ -16,9 +16,9 @@
}
},
"node_modules/@openai/codex": {
"version": "0.114.0",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0.tgz",
"integrity": "sha512-HMo8LRR6CtfKkaa28xvFK6eOarmBFTDfsrS9GJtEoaspGGemFok494CpafDspiTZaHZZGHfSUe5SWTUxYq7OaA==",
"version": "0.115.0",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.115.0.tgz",
"integrity": "sha512-uu689DHUzvuPcb39hJ+7fqy++TAvY32w9VggDpcz3HS0Sx0WadWoAPPcMK547P2T6AqfMsAtA8kspkR3tqErOg==",
"license": "Apache-2.0",
"bin": {
"codex": "bin/codex.js"
@@ -27,19 +27,19 @@
"node": ">=16"
},
"optionalDependencies": {
"@openai/codex-darwin-arm64": "npm:@openai/codex@0.114.0-darwin-arm64",
"@openai/codex-darwin-x64": "npm:@openai/codex@0.114.0-darwin-x64",
"@openai/codex-linux-arm64": "npm:@openai/codex@0.114.0-linux-arm64",
"@openai/codex-linux-x64": "npm:@openai/codex@0.114.0-linux-x64",
"@openai/codex-win32-arm64": "npm:@openai/codex@0.114.0-win32-arm64",
"@openai/codex-win32-x64": "npm:@openai/codex@0.114.0-win32-x64"
"@openai/codex-darwin-arm64": "npm:@openai/codex@0.115.0-darwin-arm64",
"@openai/codex-darwin-x64": "npm:@openai/codex@0.115.0-darwin-x64",
"@openai/codex-linux-arm64": "npm:@openai/codex@0.115.0-linux-arm64",
"@openai/codex-linux-x64": "npm:@openai/codex@0.115.0-linux-x64",
"@openai/codex-win32-arm64": "npm:@openai/codex@0.115.0-win32-arm64",
"@openai/codex-win32-x64": "npm:@openai/codex@0.115.0-win32-x64"
}
},
"node_modules/@openai/codex-darwin-arm64": {
"name": "@openai/codex",
"version": "0.114.0-darwin-arm64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-darwin-arm64.tgz",
"integrity": "sha512-c9dlgo9O+66alr3s3G36fKE3KaO2+xrLZ/QcU3oujDruV86pqgVSEK2njHi+WIyyOa6m2AyWxdaHLEbKxPCNRg==",
"version": "0.115.0-darwin-arm64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.115.0-darwin-arm64.tgz",
"integrity": "sha512-wTWV+YlDTL0y0mL+FMmbzhilm+dPkbIZTe/lH3LwBzcEMhgSGLsPdZLfAiMND4wFE21HS7H0pjMogNQEMLQmqg==",
"cpu": [
"arm64"
],
@@ -54,9 +54,9 @@
},
"node_modules/@openai/codex-darwin-x64": {
"name": "@openai/codex",
"version": "0.114.0-darwin-x64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-darwin-x64.tgz",
"integrity": "sha512-oPAPeZSaJZ1AQneFPSJu44uqbZr63Bxut9FOTWZwuSqYx4YEees7i0HJmJcqQc0XnCbYtHJdqo/i07Uv374NLw==",
"version": "0.115.0-darwin-x64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.115.0-darwin-x64.tgz",
"integrity": "sha512-EPzgymU4CFp83qjv29wXFwhWib3zC8g6SLTJGh2OpcJiOYyLY0bO53FB+QchL1jC9gm1uLyD5j5F3ddI0AbjIg==",
"cpu": [
"x64"
],
@@ -71,9 +71,9 @@
},
"node_modules/@openai/codex-linux-arm64": {
"name": "@openai/codex",
"version": "0.114.0-linux-arm64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-linux-arm64.tgz",
"integrity": "sha512-oUFZGZgMiEqmo85C+dfhckpVApH25alLWfwBgfKr2IRgdx/tovy8vN101f6kj01E6nDDe4LfJdNSZGtZht4fRA==",
"version": "0.115.0-linux-arm64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.115.0-linux-arm64.tgz",
"integrity": "sha512-40+SCyI+LvVx/iX30qH7dTQzWt9MZxDaK2E6YRB4hoYej5UALrhkFNzweHa5uvqvhmqGZCuagZOYB8285eGCgw==",
"cpu": [
"arm64"
],
@@ -88,9 +88,9 @@
},
"node_modules/@openai/codex-linux-x64": {
"name": "@openai/codex",
"version": "0.114.0-linux-x64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-linux-x64.tgz",
"integrity": "sha512-MF6RhxfBoccccd5CYII2C7TL2TvYzombBQhanDZfQAajr6n7IuoazCmoHDlrFHS4AcSw9bYA4MRlrwEjbEjgsw==",
"version": "0.115.0-linux-x64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.115.0-linux-x64.tgz",
"integrity": "sha512-+6eRd2p4KMrhQvuF7XpjYIdCA2Ok2LbhOq+ywZdLpHbmwQ/Yynd0gJ/Q90xCeo2vloCwl9WsbsiVVSahG5FyWg==",
"cpu": [
"x64"
],
@@ -104,12 +104,12 @@
}
},
"node_modules/@openai/codex-sdk": {
"version": "0.114.0",
"resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.114.0.tgz",
"integrity": "sha512-28tV+pvoQhhoRADBCtlg24fR6LDTyeUA6C65NBTvYFnDoQVPJcHcAwRrbuWmUMcXdLrXwY2bmiMxyfXlUWQzgw==",
"version": "0.115.0",
"resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.115.0.tgz",
"integrity": "sha512-BPoPhim0uUm3rzugY7JFaFJ+rPG/wMRhvKNFii//Dp3kTb+gFy3jrip7ijXqhswsnqXM3nwTiv7kYHt1+TMUPg==",
"license": "Apache-2.0",
"dependencies": {
"@openai/codex": "0.114.0"
"@openai/codex": "0.115.0"
},
"engines": {
"node": ">=18"
@@ -117,9 +117,9 @@
},
"node_modules/@openai/codex-win32-arm64": {
"name": "@openai/codex",
"version": "0.114.0-win32-arm64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-win32-arm64.tgz",
"integrity": "sha512-CnRMHopj3en9aqQ2UaDW7EgpEGkxHdZVLLRq2cOy5D0HyuzF6Qb6595ADoFVJHEmPeN5Iz/KUbiGs5GLDjUwOA==",
"version": "0.115.0-win32-arm64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.115.0-win32-arm64.tgz",
"integrity": "sha512-yaYhQ0kPL9Kithmrr1vh5A4c+gqAMhMZeA/htTtkpWW8RQkmwgcYYpX/Ky2cEzu0w51pvxQQy63LgHftc+pg1Q==",
"cpu": [
"arm64"
],
@@ -134,9 +134,9 @@
},
"node_modules/@openai/codex-win32-x64": {
"name": "@openai/codex",
"version": "0.114.0-win32-x64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-win32-x64.tgz",
"integrity": "sha512-ztRsH5Z+gPVZ5ZInx6HzXsTFFkuEdjk3Ay0UGVOhRRCWvevXQi/SL4eU8hwysCYrs8K/WRq3mo4c95w9zQ2wLw==",
"version": "0.115.0-win32-x64",
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.115.0-win32-x64.tgz",
"integrity": "sha512-wtYf2HJCB+p+Uj7o1W08fRgZMzKCVGRQ4YdSfLRNfF4wwPqX5XsK9blsv7brukn5J9lclYxagiR6qGvbfHbD7w==",
"cpu": [
"x64"
],