feat: add reviewer evidence presets (#184)
This commit is contained in:
@@ -4,11 +4,14 @@ import path from 'path';
|
|||||||
export const HOST_EVIDENCE_ACTIONS = [
|
export const HOST_EVIDENCE_ACTIONS = [
|
||||||
'ejclaw_service_status',
|
'ejclaw_service_status',
|
||||||
'ejclaw_service_logs',
|
'ejclaw_service_logs',
|
||||||
|
'ejclaw_role_runtime_config',
|
||||||
'ejclaw_deploy_state',
|
'ejclaw_deploy_state',
|
||||||
'ejclaw_artifact_metadata',
|
'ejclaw_artifact_metadata',
|
||||||
'db_paired_task_status',
|
'db_paired_task_status',
|
||||||
'db_paired_task_flow',
|
'db_paired_task_flow',
|
||||||
'db_recent_paired_failures',
|
'db_recent_paired_failures',
|
||||||
|
'db_recent_scheduled_tasks',
|
||||||
|
'db_scheduled_task_runs',
|
||||||
'github_pr_status',
|
'github_pr_status',
|
||||||
'github_pr_diff_stat',
|
'github_pr_diff_stat',
|
||||||
'github_run_status',
|
'github_run_status',
|
||||||
@@ -20,6 +23,8 @@ export const DB_EVIDENCE_ACTIONS = [
|
|||||||
'db_paired_task_status',
|
'db_paired_task_status',
|
||||||
'db_paired_task_flow',
|
'db_paired_task_flow',
|
||||||
'db_recent_paired_failures',
|
'db_recent_paired_failures',
|
||||||
|
'db_recent_scheduled_tasks',
|
||||||
|
'db_scheduled_task_runs',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const DEPLOY_EVIDENCE_ACTIONS = [
|
export const DEPLOY_EVIDENCE_ACTIONS = [
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function registerHostEvidenceTools(
|
|||||||
|
|
||||||
server.tool(
|
server.tool(
|
||||||
'read_host_evidence',
|
'read_host_evidence',
|
||||||
'Read host-side deployment evidence through a narrow allowlist. Use this instead of broad shell access when reviewer/arbiter needs service status, deploy state, DB state, GitHub PR state, or artifact metadata.',
|
'Read host-side deployment evidence through a narrow allowlist. Use this instead of broad shell access when reviewer/arbiter needs service status, role runtime config, deploy state, DB state, GitHub PR state, or artifact metadata.',
|
||||||
{
|
{
|
||||||
action: z
|
action: z
|
||||||
.enum(HOST_EVIDENCE_ACTIONS)
|
.enum(HOST_EVIDENCE_ACTIONS)
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import type { Database } from 'bun:sqlite';
|
import type { Database } from 'bun:sqlite';
|
||||||
|
|
||||||
|
import { parseGitHubCiMetadata } from './github-ci.js';
|
||||||
|
import { extractWatchCiTarget } from './task-watch-status.js';
|
||||||
|
|
||||||
type SqlBinding = string | number | bigint | boolean | null | Uint8Array;
|
type SqlBinding = string | number | bigint | boolean | null | Uint8Array;
|
||||||
|
|
||||||
export const DB_EVIDENCE_ACTIONS = [
|
export const DB_EVIDENCE_ACTIONS = [
|
||||||
'db_paired_task_status',
|
'db_paired_task_status',
|
||||||
'db_paired_task_flow',
|
'db_paired_task_flow',
|
||||||
'db_recent_paired_failures',
|
'db_recent_paired_failures',
|
||||||
|
'db_recent_scheduled_tasks',
|
||||||
|
'db_scheduled_task_runs',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type DbEvidenceAction = (typeof DB_EVIDENCE_ACTIONS)[number];
|
export type DbEvidenceAction = (typeof DB_EVIDENCE_ACTIONS)[number];
|
||||||
@@ -331,6 +336,155 @@ function runRecentPairedFailures(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeScheduledTaskEvidenceRow(
|
||||||
|
row: Record<string, unknown>,
|
||||||
|
): Record<string, unknown> {
|
||||||
|
const prompt = typeof row.prompt === 'string' ? row.prompt : '';
|
||||||
|
const metadata = parseGitHubCiMetadata(
|
||||||
|
typeof row.ci_metadata === 'string' ? row.ci_metadata : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
group_folder: row.group_folder,
|
||||||
|
chat_jid: row.chat_jid,
|
||||||
|
agent_type: row.agent_type,
|
||||||
|
room_role: row.room_role,
|
||||||
|
ci_provider: row.ci_provider,
|
||||||
|
ci_repo: metadata?.repo ?? null,
|
||||||
|
ci_run_id: metadata?.run_id ?? null,
|
||||||
|
ci_poll_count: metadata?.poll_count ?? null,
|
||||||
|
ci_consecutive_errors: metadata?.consecutive_errors ?? null,
|
||||||
|
ci_last_checked_at: metadata?.last_checked_at ?? null,
|
||||||
|
ci_target: extractWatchCiTarget(prompt),
|
||||||
|
max_duration_ms: row.max_duration_ms,
|
||||||
|
status_message_id: row.status_message_id,
|
||||||
|
status_started_at: row.status_started_at,
|
||||||
|
schedule_type: row.schedule_type,
|
||||||
|
schedule_value: row.schedule_value,
|
||||||
|
next_run: row.next_run,
|
||||||
|
last_run: row.last_run,
|
||||||
|
last_result_chars: row.last_result_chars,
|
||||||
|
status: row.status,
|
||||||
|
created_at: row.created_at,
|
||||||
|
prompt_chars: row.prompt_chars,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function runRecentScheduledTasks(
|
||||||
|
database: Database,
|
||||||
|
request: DbEvidenceRequest,
|
||||||
|
scope: DbEvidenceScope,
|
||||||
|
): string {
|
||||||
|
const minutes = normalizeDbEvidenceMinutes(request.minutes);
|
||||||
|
const limit = normalizeDbEvidenceLimit(request.limit);
|
||||||
|
const cutoff = new Date(Date.now() - minutes * 60_000).toISOString();
|
||||||
|
const groupScope = groupScopeClause(scope);
|
||||||
|
|
||||||
|
const rows = database
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
SELECT id,
|
||||||
|
group_folder,
|
||||||
|
chat_jid,
|
||||||
|
agent_type,
|
||||||
|
room_role,
|
||||||
|
ci_provider,
|
||||||
|
ci_metadata,
|
||||||
|
max_duration_ms,
|
||||||
|
status_message_id,
|
||||||
|
status_started_at,
|
||||||
|
prompt,
|
||||||
|
length(prompt) AS prompt_chars,
|
||||||
|
schedule_type,
|
||||||
|
schedule_value,
|
||||||
|
next_run,
|
||||||
|
last_run,
|
||||||
|
length(last_result) AS last_result_chars,
|
||||||
|
status,
|
||||||
|
created_at
|
||||||
|
FROM scheduled_tasks
|
||||||
|
WHERE (
|
||||||
|
created_at >= ?
|
||||||
|
OR last_run >= ?
|
||||||
|
OR next_run >= ?
|
||||||
|
OR status_started_at >= ?
|
||||||
|
OR status IN ('active', 'paused')
|
||||||
|
)
|
||||||
|
${groupScope.clause}
|
||||||
|
ORDER BY COALESCE(last_run, status_started_at, next_run, created_at) DESC
|
||||||
|
LIMIT ?
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.all(cutoff, cutoff, cutoff, cutoff, ...groupScope.params, limit) as Array<
|
||||||
|
Record<string, unknown>
|
||||||
|
>;
|
||||||
|
|
||||||
|
return stringifyEvidence({
|
||||||
|
action: request.action,
|
||||||
|
window_minutes: minutes,
|
||||||
|
cutoff,
|
||||||
|
tasks: rows.map(normalizeScheduledTaskEvidenceRow),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runScheduledTaskRuns(
|
||||||
|
database: Database,
|
||||||
|
request: DbEvidenceRequest,
|
||||||
|
scope: DbEvidenceScope,
|
||||||
|
): string {
|
||||||
|
const minutes = normalizeDbEvidenceMinutes(request.minutes);
|
||||||
|
const limit = normalizeDbEvidenceLimit(request.limit);
|
||||||
|
const cutoff = new Date(Date.now() - minutes * 60_000).toISOString();
|
||||||
|
const taskId = request.taskId
|
||||||
|
? normalizeDbEvidenceTaskId(request.taskId)
|
||||||
|
: null;
|
||||||
|
const params: SqlBinding[] = [cutoff];
|
||||||
|
const clauses = ['logs.run_at >= ?'];
|
||||||
|
|
||||||
|
if (taskId) {
|
||||||
|
clauses.push('logs.task_id = ?');
|
||||||
|
params.push(taskId);
|
||||||
|
}
|
||||||
|
if (!scope.isMain) {
|
||||||
|
clauses.push('tasks.group_folder = ?');
|
||||||
|
params.push(scope.sourceGroup);
|
||||||
|
}
|
||||||
|
params.push(limit);
|
||||||
|
|
||||||
|
const rows = database
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
SELECT logs.task_id,
|
||||||
|
tasks.group_folder,
|
||||||
|
tasks.chat_jid,
|
||||||
|
tasks.agent_type,
|
||||||
|
tasks.room_role,
|
||||||
|
tasks.ci_provider,
|
||||||
|
logs.run_at,
|
||||||
|
logs.duration_ms,
|
||||||
|
logs.status,
|
||||||
|
length(logs.result) AS result_chars,
|
||||||
|
length(logs.error) AS error_chars
|
||||||
|
FROM task_run_logs logs
|
||||||
|
LEFT JOIN scheduled_tasks tasks
|
||||||
|
ON tasks.id = logs.task_id
|
||||||
|
WHERE ${clauses.join(' AND ')}
|
||||||
|
ORDER BY logs.run_at DESC, logs.id DESC
|
||||||
|
LIMIT ?
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.all(...params);
|
||||||
|
|
||||||
|
return stringifyEvidence({
|
||||||
|
action: request.action,
|
||||||
|
window_minutes: minutes,
|
||||||
|
cutoff,
|
||||||
|
task_id: taskId,
|
||||||
|
runs: rows,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function runDbEvidenceRequest(
|
export function runDbEvidenceRequest(
|
||||||
database: Database,
|
database: Database,
|
||||||
request: DbEvidenceRequest,
|
request: DbEvidenceRequest,
|
||||||
@@ -343,5 +497,9 @@ export function runDbEvidenceRequest(
|
|||||||
return runPairedTaskFlow(database, request, scope);
|
return runPairedTaskFlow(database, request, scope);
|
||||||
case 'db_recent_paired_failures':
|
case 'db_recent_paired_failures':
|
||||||
return runRecentPairedFailures(database, request, scope);
|
return runRecentPairedFailures(database, request, scope);
|
||||||
|
case 'db_recent_scheduled_tasks':
|
||||||
|
return runRecentScheduledTasks(database, request, scope);
|
||||||
|
case 'db_scheduled_task_runs':
|
||||||
|
return runScheduledTaskRuns(database, request, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
120
src/db-scheduled-evidence.test.ts
Normal file
120
src/db-scheduled-evidence.test.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { _initTestDatabase } from './db.js';
|
||||||
|
import { requireDatabase } from './db/runtime-database.js';
|
||||||
|
import { runDbEvidenceRequest } from './db-evidence.js';
|
||||||
|
|
||||||
|
function seedScheduledTaskEvidence(): void {
|
||||||
|
_initTestDatabase();
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
requireDatabase()
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
INSERT INTO scheduled_tasks (
|
||||||
|
id, group_folder, chat_jid, agent_type, room_role, ci_provider,
|
||||||
|
ci_metadata, max_duration_ms, status_message_id, status_started_at,
|
||||||
|
prompt, schedule_type, schedule_value, context_mode, next_run,
|
||||||
|
last_run, last_result, status, created_at
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.run(
|
||||||
|
'watch-1',
|
||||||
|
'room-folder',
|
||||||
|
'room-1',
|
||||||
|
'codex',
|
||||||
|
'owner',
|
||||||
|
'github',
|
||||||
|
JSON.stringify({
|
||||||
|
repo: 'phj1081/EJClaw',
|
||||||
|
run_id: 123,
|
||||||
|
poll_count: 2,
|
||||||
|
consecutive_errors: 1,
|
||||||
|
last_checked_at: now,
|
||||||
|
}),
|
||||||
|
300_000,
|
||||||
|
'status-msg-1',
|
||||||
|
now,
|
||||||
|
'[BACKGROUND CI WATCH]\ntarget=PR #180 checks\nSECRET PROMPT BODY',
|
||||||
|
'interval',
|
||||||
|
'15000',
|
||||||
|
'isolated',
|
||||||
|
now,
|
||||||
|
now,
|
||||||
|
'raw CI result body',
|
||||||
|
'active',
|
||||||
|
now,
|
||||||
|
);
|
||||||
|
requireDatabase()
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
INSERT INTO task_run_logs (
|
||||||
|
task_id, run_at, duration_ms, status, result, error
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.run(
|
||||||
|
'watch-1',
|
||||||
|
now,
|
||||||
|
42,
|
||||||
|
'success',
|
||||||
|
'raw scheduler result',
|
||||||
|
'raw scheduler error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('scheduled task DB evidence presets', () => {
|
||||||
|
it('returns scheduled task metadata without raw prompt or result bodies', () => {
|
||||||
|
seedScheduledTaskEvidence();
|
||||||
|
|
||||||
|
const tasksText = runDbEvidenceRequest(
|
||||||
|
requireDatabase(),
|
||||||
|
{ action: 'db_recent_scheduled_tasks', minutes: 1440 },
|
||||||
|
{ sourceGroup: 'room-folder', isMain: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tasksText).toContain('"ci_repo": "phj1081/EJClaw"');
|
||||||
|
expect(tasksText).toContain('"ci_run_id": 123');
|
||||||
|
expect(tasksText).toContain('"status_message_id": "status-msg-1"');
|
||||||
|
expect(tasksText).toContain('"prompt_chars"');
|
||||||
|
expect(tasksText).toContain('"last_result_chars"');
|
||||||
|
expect(tasksText).not.toContain('SECRET PROMPT BODY');
|
||||||
|
expect(tasksText).not.toContain('raw CI result body');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns scheduled run metadata without raw result or error bodies', () => {
|
||||||
|
seedScheduledTaskEvidence();
|
||||||
|
|
||||||
|
const runsText = runDbEvidenceRequest(
|
||||||
|
requireDatabase(),
|
||||||
|
{
|
||||||
|
action: 'db_scheduled_task_runs',
|
||||||
|
taskId: 'watch-1',
|
||||||
|
minutes: 1440,
|
||||||
|
},
|
||||||
|
{ sourceGroup: 'room-folder', isMain: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(runsText).toContain('"duration_ms": 42');
|
||||||
|
expect(runsText).toContain('"result_chars"');
|
||||||
|
expect(runsText).toContain('"error_chars"');
|
||||||
|
expect(runsText).not.toContain('raw scheduler result');
|
||||||
|
expect(runsText).not.toContain('raw scheduler error');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scopes scheduled task evidence for non-main rooms', () => {
|
||||||
|
seedScheduledTaskEvidence();
|
||||||
|
|
||||||
|
const scopedOut = JSON.parse(
|
||||||
|
runDbEvidenceRequest(
|
||||||
|
requireDatabase(),
|
||||||
|
{ action: 'db_recent_scheduled_tasks', minutes: 1440 },
|
||||||
|
{ sourceGroup: 'other-folder', isMain: false },
|
||||||
|
),
|
||||||
|
) as { tasks: unknown[] };
|
||||||
|
|
||||||
|
expect(scopedOut.tasks).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
buildRoleRuntimeConfigEvidence,
|
||||||
buildHostEvidenceCommand,
|
buildHostEvidenceCommand,
|
||||||
clampHostEvidenceTailLines,
|
clampHostEvidenceTailLines,
|
||||||
isHostEvidenceAction,
|
isHostEvidenceAction,
|
||||||
@@ -10,7 +11,10 @@ describe('host evidence helpers', () => {
|
|||||||
it('recognizes only allowlisted actions', () => {
|
it('recognizes only allowlisted actions', () => {
|
||||||
expect(isHostEvidenceAction('ejclaw_service_status')).toBe(true);
|
expect(isHostEvidenceAction('ejclaw_service_status')).toBe(true);
|
||||||
expect(isHostEvidenceAction('ejclaw_service_logs')).toBe(true);
|
expect(isHostEvidenceAction('ejclaw_service_logs')).toBe(true);
|
||||||
|
expect(isHostEvidenceAction('ejclaw_role_runtime_config')).toBe(true);
|
||||||
expect(isHostEvidenceAction('db_paired_task_status')).toBe(true);
|
expect(isHostEvidenceAction('db_paired_task_status')).toBe(true);
|
||||||
|
expect(isHostEvidenceAction('db_recent_scheduled_tasks')).toBe(true);
|
||||||
|
expect(isHostEvidenceAction('db_scheduled_task_runs')).toBe(true);
|
||||||
expect(isHostEvidenceAction('ejclaw_deploy_state')).toBe(true);
|
expect(isHostEvidenceAction('ejclaw_deploy_state')).toBe(true);
|
||||||
expect(isHostEvidenceAction('github_pr_status')).toBe(true);
|
expect(isHostEvidenceAction('github_pr_status')).toBe(true);
|
||||||
expect(isHostEvidenceAction('github_run_jobs')).toBe(true);
|
expect(isHostEvidenceAction('github_run_jobs')).toBe(true);
|
||||||
@@ -44,4 +48,22 @@ describe('host evidence helpers', () => {
|
|||||||
expect.arrayContaining(['--user', '-u', 'ejclaw', '-n', '42']),
|
expect.arrayContaining(['--user', '-u', 'ejclaw', '-n', '42']),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns role runtime config without secret-shaped fields', () => {
|
||||||
|
const text = buildRoleRuntimeConfigEvidence();
|
||||||
|
const parsed = JSON.parse(text) as {
|
||||||
|
roles: {
|
||||||
|
owner: { agent_type: string; effective_model: string };
|
||||||
|
reviewer: { agent_type: string; effective_model: string };
|
||||||
|
arbiter: { agent_type: string | null; effective_model: string | null };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(parsed.roles.owner.agent_type).toMatch(/^(codex|claude-code)$/);
|
||||||
|
expect(parsed.roles.reviewer.agent_type).toMatch(/^(codex|claude-code)$/);
|
||||||
|
expect(text).not.toMatch(/api[_-]?key/i);
|
||||||
|
expect(text).not.toMatch(/token/i);
|
||||||
|
expect(text).not.toMatch(/secret/i);
|
||||||
|
expect(text).not.toMatch(/password/i);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,27 @@ import { execFile } from 'child_process';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { DATA_DIR, WEB_DASHBOARD } from './config.js';
|
import {
|
||||||
|
ARBITER_AGENT_TYPE,
|
||||||
|
ARBITER_MODEL_CONFIG,
|
||||||
|
ARBITER_SERVICE_ID,
|
||||||
|
CLAUDE_SERVICE_ID,
|
||||||
|
CODEX_MAIN_SERVICE_ID,
|
||||||
|
CODEX_REVIEW_SERVICE_ID,
|
||||||
|
CURRENT_RUNTIME_AGENT_TYPE,
|
||||||
|
DEFAULT_CLAUDE_MODEL,
|
||||||
|
DEFAULT_CODEX_MODEL,
|
||||||
|
OWNER_AGENT_TYPE,
|
||||||
|
OWNER_MODEL_CONFIG,
|
||||||
|
REVIEWER_AGENT_TYPE,
|
||||||
|
REVIEWER_MODEL_CONFIG,
|
||||||
|
REVIEWER_SERVICE_ID_FOR_TYPE,
|
||||||
|
SERVICE_ID,
|
||||||
|
SERVICE_SESSION_SCOPE,
|
||||||
|
DATA_DIR,
|
||||||
|
WEB_DASHBOARD,
|
||||||
|
} from './config.js';
|
||||||
|
import type { AgentType } from './types.js';
|
||||||
import {
|
import {
|
||||||
collectArtifactMetadata,
|
collectArtifactMetadata,
|
||||||
collectDeployState,
|
collectDeployState,
|
||||||
@@ -27,6 +47,7 @@ import { resolveGroupIpcPath } from './group-folder.js';
|
|||||||
export const HOST_EVIDENCE_ACTIONS = [
|
export const HOST_EVIDENCE_ACTIONS = [
|
||||||
'ejclaw_service_status',
|
'ejclaw_service_status',
|
||||||
'ejclaw_service_logs',
|
'ejclaw_service_logs',
|
||||||
|
'ejclaw_role_runtime_config',
|
||||||
...DEPLOY_EVIDENCE_ACTIONS,
|
...DEPLOY_EVIDENCE_ACTIONS,
|
||||||
...DB_EVIDENCE_ACTIONS,
|
...DB_EVIDENCE_ACTIONS,
|
||||||
...GITHUB_EVIDENCE_ACTIONS,
|
...GITHUB_EVIDENCE_ACTIONS,
|
||||||
@@ -163,6 +184,95 @@ export function truncateHostEvidenceText(value: string | undefined): string {
|
|||||||
return `${value.slice(0, MAX_OUTPUT_CHARS)}\n...[truncated]`;
|
return `${value.slice(0, MAX_OUTPUT_CHARS)}\n...[truncated]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function effectiveModelForAgentType(
|
||||||
|
agentType: AgentType | null | undefined,
|
||||||
|
configuredModel: string | undefined,
|
||||||
|
): string | null {
|
||||||
|
if (!agentType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (configuredModel) {
|
||||||
|
return configuredModel;
|
||||||
|
}
|
||||||
|
return agentType === 'claude-code'
|
||||||
|
? DEFAULT_CLAUDE_MODEL
|
||||||
|
: DEFAULT_CODEX_MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ownerServiceIdForAgentType(agentType: AgentType): string {
|
||||||
|
return agentType === 'claude-code'
|
||||||
|
? CLAUDE_SERVICE_ID
|
||||||
|
: CODEX_MAIN_SERVICE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRoleRuntimeConfigRole(
|
||||||
|
role: 'owner' | 'reviewer' | 'arbiter',
|
||||||
|
agentType: AgentType | null | undefined,
|
||||||
|
modelConfig: {
|
||||||
|
model?: string;
|
||||||
|
effort?: string;
|
||||||
|
fallbackEnabled: boolean;
|
||||||
|
},
|
||||||
|
serviceId: string | null,
|
||||||
|
): Record<string, unknown> {
|
||||||
|
return {
|
||||||
|
role,
|
||||||
|
enabled: role !== 'arbiter' || Boolean(agentType),
|
||||||
|
agent_type: agentType ?? null,
|
||||||
|
service_id: serviceId,
|
||||||
|
configured_model: modelConfig.model ?? null,
|
||||||
|
effective_model: effectiveModelForAgentType(agentType, modelConfig.model),
|
||||||
|
effort: modelConfig.effort ?? null,
|
||||||
|
fallback_enabled: modelConfig.fallbackEnabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildRoleRuntimeConfigEvidence(): string {
|
||||||
|
return JSON.stringify(
|
||||||
|
{
|
||||||
|
action: 'ejclaw_role_runtime_config',
|
||||||
|
current_service: {
|
||||||
|
service_id: SERVICE_ID,
|
||||||
|
session_scope: SERVICE_SESSION_SCOPE,
|
||||||
|
runtime_agent_type: CURRENT_RUNTIME_AGENT_TYPE,
|
||||||
|
},
|
||||||
|
defaults: {
|
||||||
|
claude_model: DEFAULT_CLAUDE_MODEL,
|
||||||
|
codex_model: DEFAULT_CODEX_MODEL,
|
||||||
|
},
|
||||||
|
services: {
|
||||||
|
claude: CLAUDE_SERVICE_ID,
|
||||||
|
codex_main: CODEX_MAIN_SERVICE_ID,
|
||||||
|
codex_review: CODEX_REVIEW_SERVICE_ID,
|
||||||
|
reviewer_for_type: REVIEWER_SERVICE_ID_FOR_TYPE,
|
||||||
|
arbiter: ARBITER_SERVICE_ID,
|
||||||
|
},
|
||||||
|
roles: {
|
||||||
|
owner: buildRoleRuntimeConfigRole(
|
||||||
|
'owner',
|
||||||
|
OWNER_AGENT_TYPE,
|
||||||
|
OWNER_MODEL_CONFIG,
|
||||||
|
ownerServiceIdForAgentType(OWNER_AGENT_TYPE),
|
||||||
|
),
|
||||||
|
reviewer: buildRoleRuntimeConfigRole(
|
||||||
|
'reviewer',
|
||||||
|
REVIEWER_AGENT_TYPE,
|
||||||
|
REVIEWER_MODEL_CONFIG,
|
||||||
|
REVIEWER_SERVICE_ID_FOR_TYPE,
|
||||||
|
),
|
||||||
|
arbiter: buildRoleRuntimeConfigRole(
|
||||||
|
'arbiter',
|
||||||
|
ARBITER_AGENT_TYPE,
|
||||||
|
ARBITER_MODEL_CONFIG,
|
||||||
|
ARBITER_SERVICE_ID,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function execFileCapture(
|
function execFileCapture(
|
||||||
file: string,
|
file: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
@@ -210,6 +320,18 @@ export async function runHostEvidenceRequest(
|
|||||||
): Promise<HostEvidenceResult> {
|
): Promise<HostEvidenceResult> {
|
||||||
let commandText = '';
|
let commandText = '';
|
||||||
try {
|
try {
|
||||||
|
if (request.action === 'ejclaw_role_runtime_config') {
|
||||||
|
commandText = `internal:${request.action}`;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
action: request.action,
|
||||||
|
command: commandText,
|
||||||
|
stdout: truncateHostEvidenceText(buildRoleRuntimeConfigEvidence()),
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (isDbEvidenceAction(request.action)) {
|
if (isDbEvidenceAction(request.action)) {
|
||||||
commandText = `internal:${request.action}`;
|
commandText = `internal:${request.action}`;
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user