runners: share protocol and reviewer policy
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
/**
|
||||
* Agent Protocol Constants (SSOT).
|
||||
*
|
||||
* Shared constants for host ↔ runner communication.
|
||||
* Runners are separate packages and can't import this directly —
|
||||
* they define local copies with comments referencing this file.
|
||||
*/
|
||||
|
||||
/** Sentinel markers for robust stdout output parsing. */
|
||||
export const OUTPUT_START_MARKER = '---EJCLAW_OUTPUT_START---';
|
||||
export const OUTPUT_END_MARKER = '---EJCLAW_OUTPUT_END---';
|
||||
|
||||
/** Regex to extract [Image: /path/to/file] tags from agent text. */
|
||||
export const IMAGE_TAG_RE = /\[Image:\s*(\/[^\]]+)\]/g;
|
||||
|
||||
/** IPC polling interval (ms) used by runners to check for follow-up messages. */
|
||||
export const IPC_POLL_MS = 500;
|
||||
export const IPC_INPUT_SUBDIR = 'input';
|
||||
export const IPC_CLOSE_SENTINEL = '_close';
|
||||
export {
|
||||
extractImageTagPaths,
|
||||
IMAGE_TAG_RE,
|
||||
IPC_CLOSE_SENTINEL,
|
||||
IPC_INPUT_SUBDIR,
|
||||
IPC_POLL_MS,
|
||||
normalizeEjclawStructuredOutput,
|
||||
normalizePublicTextOutput,
|
||||
OUTPUT_END_MARKER,
|
||||
OUTPUT_START_MARKER,
|
||||
writeProtocolOutput,
|
||||
type NormalizedRunnerOutput,
|
||||
type RunnerOutputPhase,
|
||||
type RunnerOutputVerdict,
|
||||
type RunnerOutputVisibility,
|
||||
type RunnerStructuredOutput,
|
||||
} from 'ejclaw-runners-shared';
|
||||
|
||||
@@ -3,6 +3,10 @@ import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import {
|
||||
CLAUDE_REVIEWER_READONLY_ENV,
|
||||
REVIEWER_RUNTIME_ENV,
|
||||
} from 'ejclaw-runners-shared';
|
||||
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('./db.js', () => {
|
||||
@@ -88,9 +92,9 @@ const failoverOwnerContext: RoomRoleContext = {
|
||||
|
||||
const ORIGINAL_UNSAFE_HOST_PAIRED_MODE =
|
||||
process.env.EJCLAW_UNSAFE_HOST_PAIRED_MODE;
|
||||
const ORIGINAL_REVIEWER_RUNTIME = process.env.EJCLAW_REVIEWER_RUNTIME;
|
||||
const ORIGINAL_REVIEWER_RUNTIME = process.env[REVIEWER_RUNTIME_ENV];
|
||||
const ORIGINAL_CLAUDE_REVIEWER_READONLY =
|
||||
process.env.EJCLAW_CLAUDE_REVIEWER_READONLY;
|
||||
process.env[CLAUDE_REVIEWER_READONLY_ENV];
|
||||
|
||||
function createCanonicalRepoWithCommit(commitMessage: string): string {
|
||||
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-finalize-'));
|
||||
@@ -165,8 +169,8 @@ function buildWorkspace(
|
||||
describe('paired execution context', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.EJCLAW_UNSAFE_HOST_PAIRED_MODE;
|
||||
delete process.env.EJCLAW_REVIEWER_RUNTIME;
|
||||
delete process.env.EJCLAW_CLAUDE_REVIEWER_READONLY;
|
||||
delete process.env[REVIEWER_RUNTIME_ENV];
|
||||
delete process.env[CLAUDE_REVIEWER_READONLY_ENV];
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(db.getLatestOpenPairedTaskForChat).mockReturnValue(undefined);
|
||||
vi.mocked(db.getPairedTaskById).mockReturnValue(undefined);
|
||||
@@ -202,15 +206,15 @@ describe('paired execution context', () => {
|
||||
}
|
||||
|
||||
if (ORIGINAL_REVIEWER_RUNTIME == null) {
|
||||
delete process.env.EJCLAW_REVIEWER_RUNTIME;
|
||||
delete process.env[REVIEWER_RUNTIME_ENV];
|
||||
} else {
|
||||
process.env.EJCLAW_REVIEWER_RUNTIME = ORIGINAL_REVIEWER_RUNTIME;
|
||||
process.env[REVIEWER_RUNTIME_ENV] = ORIGINAL_REVIEWER_RUNTIME;
|
||||
}
|
||||
|
||||
if (ORIGINAL_CLAUDE_REVIEWER_READONLY == null) {
|
||||
delete process.env.EJCLAW_CLAUDE_REVIEWER_READONLY;
|
||||
delete process.env[CLAUDE_REVIEWER_READONLY_ENV];
|
||||
} else {
|
||||
process.env.EJCLAW_CLAUDE_REVIEWER_READONLY =
|
||||
process.env[CLAUDE_REVIEWER_READONLY_ENV] =
|
||||
ORIGINAL_CLAUDE_REVIEWER_READONLY;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@ import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { buildPairedReadonlyRuntimeEnvOverrides } from 'ejclaw-runners-shared';
|
||||
|
||||
import {
|
||||
ARBITER_DEADLOCK_THRESHOLD,
|
||||
ARBITER_AGENT_TYPE,
|
||||
@@ -337,14 +339,14 @@ export function preparePairedExecutionContext(args: {
|
||||
);
|
||||
fs.mkdirSync(reviewerSessionDir, { recursive: true });
|
||||
envOverrides.CLAUDE_CONFIG_DIR = reviewerSessionDir;
|
||||
if (unsafeHostPairedMode) {
|
||||
envOverrides.EJCLAW_UNSAFE_HOST_PAIRED_MODE = '1';
|
||||
if (REVIEWER_AGENT_TYPE === 'claude-code') {
|
||||
envOverrides.EJCLAW_CLAUDE_REVIEWER_READONLY = '1';
|
||||
}
|
||||
} else {
|
||||
envOverrides.EJCLAW_REVIEWER_RUNTIME = '1';
|
||||
}
|
||||
Object.assign(
|
||||
envOverrides,
|
||||
buildPairedReadonlyRuntimeEnvOverrides({
|
||||
role: 'reviewer',
|
||||
agentType: REVIEWER_AGENT_TYPE,
|
||||
unsafeHostPairedMode,
|
||||
}),
|
||||
);
|
||||
} else if (roomRoleContext.role === 'arbiter') {
|
||||
const arbiterSessionDir = path.join(
|
||||
DATA_DIR,
|
||||
@@ -356,11 +358,14 @@ export function preparePairedExecutionContext(args: {
|
||||
fs.rmSync(arbiterSessionDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(arbiterSessionDir, { recursive: true });
|
||||
envOverrides.CLAUDE_CONFIG_DIR = arbiterSessionDir;
|
||||
if (unsafeHostPairedMode) {
|
||||
envOverrides.EJCLAW_UNSAFE_HOST_PAIRED_MODE = '1';
|
||||
} else {
|
||||
envOverrides.EJCLAW_ARBITER_RUNTIME = '1';
|
||||
}
|
||||
Object.assign(
|
||||
envOverrides,
|
||||
buildPairedReadonlyRuntimeEnvOverrides({
|
||||
role: 'arbiter',
|
||||
agentType: ARBITER_AGENT_TYPE ?? REVIEWER_AGENT_TYPE,
|
||||
unsafeHostPairedMode,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user