fix: pass room role to Codex MCP tools (#185)

This commit is contained in:
Eyejoker
2026-05-29 06:14:08 +09:00
committed by GitHub
parent 0377935564
commit 5c8def0674
3 changed files with 98 additions and 1 deletions

View File

@@ -118,6 +118,21 @@ function writeSkill(dir: string, name: string): void {
);
}
function resetRoutingMocks(): void {
vi.mocked(config.isReviewService).mockReturnValue(false);
vi.mocked(serviceRouting.hasReviewerLease).mockReturnValue(false);
vi.mocked(serviceRouting.getEffectiveChannelLease).mockReturnValue({
chat_jid: 'dc:test',
owner_service_id: 'claude',
reviewer_service_id: 'codex-main',
arbiter_service_id: null,
owner_failover_active: false,
activated_at: null,
reason: null,
explicit: false,
});
}
describe('prepareGroupEnvironment codex auth handling', () => {
let tempRoot: string;
let previousCwd: string;
@@ -380,6 +395,79 @@ describe('prepareGroupEnvironment codex auth handling', () => {
});
});
describe('prepareGroupEnvironment Codex MCP room role env', () => {
let tempRoot: string;
let previousCwd: string;
beforeEach(() => {
tempRoot = fs.mkdtempSync(path.join('/tmp', 'ejclaw-agent-env-'));
previousCwd = process.cwd();
process.chdir(tempRoot);
process.env.EJ_TEST_ROOT = tempRoot;
process.env.EJ_TEST_HOME = path.join(tempRoot, 'home');
fs.mkdirSync(process.env.EJ_TEST_HOME, { recursive: true });
mockReadEnvFile.mockReset();
mockGetActiveCodexAuthPath.mockReset();
resetRoutingMocks();
});
afterEach(() => {
process.chdir(previousCwd);
delete process.env.EJ_TEST_ROOT;
delete process.env.EJ_TEST_HOME;
fs.rmSync(tempRoot, { recursive: true, force: true });
});
function writeMcpServer(): void {
const mcpServerPath = path.join(
tempRoot,
'runners',
'agent-runner',
'dist',
'ipc-mcp-stdio.js',
);
fs.mkdirSync(path.dirname(mcpServerPath), { recursive: true });
fs.writeFileSync(mcpServerPath, '// test mcp server\n');
}
function readCodexConfigToml(): string {
return fs.readFileSync(
path.join(
tempRoot,
'sessions',
group.folder,
'services',
'codex-main',
'.codex',
'config.toml',
),
'utf-8',
);
}
it('writes paired owner role into the Codex MCP config env', () => {
mockReadEnvFile.mockReturnValue({});
writeMcpServer();
prepareGroupEnvironment(group, false, 'dc:test', {
roomRole: 'owner',
});
expect(readCodexConfigToml()).toContain('EJCLAW_ROOM_ROLE = "owner"');
});
it('omits room role from non-paired Codex MCP config env', () => {
mockReadEnvFile.mockReturnValue({});
writeMcpServer();
prepareGroupEnvironment(group, true, 'dc:test');
expect(readCodexConfigToml()).not.toContain('EJCLAW_ROOM_ROLE');
});
});
describe('prepareGroupEnvironment room skill overrides', () => {
let tempRoot: string;
let previousCwd: string;
@@ -694,6 +782,7 @@ args = ["other.js"]
expect(toml).toContain('[mcp_servers.ejclaw]');
expect(toml).toContain('EJCLAW_IPC_DIR = "/workspace/ipc"');
expect(toml).toContain('EJCLAW_GROUP_FOLDER = "codex-test-group"');
expect(toml).toContain('EJCLAW_ROOM_ROLE = "reviewer"');
expect(toml).toContain('EJCLAW_WORK_DIR = "/workspace/project"');
expect(toml).not.toContain('old-ipc.js');
expect(toml).not.toContain('"/old/ipc"');

View File

@@ -34,7 +34,7 @@ import {
getEffectiveChannelLease,
hasReviewerLease,
} from './service-routing.js';
import type { AgentType, RegisteredGroup } from './types.js';
import type { AgentType, PairedRoomRole, RegisteredGroup } from './types.js';
import type { StoredRoomSkillOverride } from './db/rooms.js';
// writeCodexApiKeyAuth removed — Codex uses OAuth only.
@@ -173,6 +173,7 @@ function upsertEjclawMcpServerSection(args: {
groupFolder: string;
isMain: boolean;
agentType: AgentType;
roomRole?: PairedRoomRole;
workDir?: string;
}): void {
const stripEjclawMcpServerSections = (input: string): string => {
@@ -221,6 +222,7 @@ EJCLAW_CHAT_JID = ${JSON.stringify(args.chatJid)}
EJCLAW_GROUP_FOLDER = ${JSON.stringify(args.groupFolder)}
EJCLAW_IS_MAIN = ${JSON.stringify(args.isMain ? '1' : '0')}
EJCLAW_AGENT_TYPE = ${JSON.stringify(args.agentType)}
${args.roomRole ? `EJCLAW_ROOM_ROLE = ${JSON.stringify(args.roomRole)}\n` : ''}
${args.workDir ? `EJCLAW_WORK_DIR = ${JSON.stringify(args.workDir)}\n` : ''}
`;
fs.writeFileSync(args.sessionConfigPath, toml.trimEnd() + '\n' + mcpSection);
@@ -347,6 +349,7 @@ function prepareCodexSessionEnvironment(args: {
sessionRootDir: string;
chatJid: string;
isMain: boolean;
roomRole?: PairedRoomRole;
isPairedRoom: boolean;
useFailoverPromptPack: boolean;
memoryBriefing?: string;
@@ -489,6 +492,7 @@ function prepareCodexSessionEnvironment(args: {
groupFolder: args.group.folder,
isMain: args.isMain,
agentType: 'codex',
roomRole: args.roomRole,
workDir:
args.env.EJCLAW_WORK_DIR || args.group.workDir || args.projectRoot,
});
@@ -520,6 +524,7 @@ export function prepareGroupEnvironment(
runtimeTaskId?: string;
useTaskScopedSession?: boolean;
skillOverrides?: StoredRoomSkillOverride[];
roomRole?: PairedRoomRole;
},
): PreparedGroupEnvironment {
const projectRoot = process.cwd();
@@ -659,6 +664,7 @@ export function prepareGroupEnvironment(
sessionRootDir,
chatJid,
isMain,
roomRole: options?.roomRole,
isPairedRoom,
useFailoverPromptPack: useCodexReviewFailoverPromptPack,
memoryBriefing: options?.memoryBriefing,
@@ -799,6 +805,7 @@ export function prepareReadonlySessionEnvironment(args: {
groupFolder,
isMain,
agentType,
roomRole: role,
workDir,
});
}

View File

@@ -98,6 +98,7 @@ export async function runAgentProcess(
runtimeTaskId: input.runtimeTaskId,
useTaskScopedSession: input.useTaskScopedSession,
skillOverrides,
roomRole: input.roomRoleContext?.role,
},
);