From 8507d7337fee28fa12a1cde0806109b9d2e2d21f Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 21 May 2026 00:14:07 +0100 Subject: [PATCH] Use Spark for repo Face heartbeats --- .env.example | 2 ++ apps/worker/src/index.ts | 14 ++++++++++++++ packages/config/src/index.ts | 8 ++++++++ packages/providers/src/owner-codex-provider.ts | 15 +++++++++++++-- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 3aca350..9805709 100644 --- a/.env.example +++ b/.env.example @@ -73,6 +73,8 @@ REPO_FACE_HEARTBEAT_TASK_NAME=VoidBot Repo Face Heartbeats REPO_FACE_HEARTBEAT_INTERVAL_MINUTES=15 REPO_FACE_HEARTBEAT_MAX_JOBS_PER_TICK=2 REPO_FACE_HEARTBEAT_DEFAULT_CHANNEL_ID= +REPO_FACE_HEARTBEAT_CODEX_MODEL=gpt-5.3-codex-spark +REPO_FACE_HEARTBEAT_CODEX_REASONING_EFFORT=low EPIPHANY_AGENT_ROOT=E:/Projects/EpiphanyAgent BIFROST_ROOT=E:/Projects/Bifrost BIFROST_HEALTH_URL=http://127.0.0.1:5080/healthz diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts index 2d9c673..798bcc9 100644 --- a/apps/worker/src/index.ts +++ b/apps/worker/src/index.ts @@ -297,6 +297,7 @@ async function processJob(job: JobRecord): Promise { const request = provider.buildRequest(job.contextBundle, { jobId: job.id, command: job.command, + ...repoFaceHeartbeatCodexOptions(job), }); const response = await provider.execute(request); const artifactPaths = await writeArtifacts(job.id, response.artifacts ?? []); @@ -459,6 +460,19 @@ async function processJob(job: JobRecord): Promise { } } +function repoFaceHeartbeatCodexOptions(job: JobRecord): Record { + if (job.command !== "repo-face-rumination") { + return {}; + } + + return { + ...(config.repoFaceHeartbeats.codexModel ? { model: config.repoFaceHeartbeats.codexModel } : {}), + ...(config.repoFaceHeartbeats.codexModelReasoningEffort + ? { reasoningEffort: config.repoFaceHeartbeats.codexModelReasoningEffort } + : {}), + }; +} + async function maybeNotifyOwnerOfJobFailure(job: JobRecord, errorMessage: string): Promise { if (job.requester.id !== config.ownerDiscordId) { return; diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index e5a171f..fe187f7 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -116,6 +116,10 @@ const envSchema = z.object({ REPO_FACE_HEARTBEAT_GLOBAL_HEAT: z.coerce.number().positive().default(1), REPO_FACE_HEARTBEAT_SPEED_OVERRIDES: z.string().default(""), REPO_FACE_HEARTBEAT_HEAT_OVERRIDES: z.string().default(""), + REPO_FACE_HEARTBEAT_CODEX_MODEL: optionalNonEmptyString, + REPO_FACE_HEARTBEAT_CODEX_REASONING_EFFORT: z + .enum(["low", "medium", "high", "xhigh"]) + .optional(), EPIPHANY_AGENT_ROOT: z.string().min(1).default("E:/Projects/EpiphanyAgent"), INDEX_ALL_CHANNELS: booleanFromEnv.default(false), INDEXED_CHANNEL_IDS: z.string().default(""), @@ -167,6 +171,8 @@ export interface AppConfig { globalHeat: number; speedOverrides: Record; heatOverrides: Record; + codexModel?: string; + codexModelReasoningEffort?: "low" | "medium" | "high" | "xhigh"; }; epiphanyAgentRoot: string; indexAllChannels: boolean; @@ -387,6 +393,8 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig { globalHeat: parsed.REPO_FACE_HEARTBEAT_GLOBAL_HEAT, speedOverrides: parseNumericMap(parsed.REPO_FACE_HEARTBEAT_SPEED_OVERRIDES), heatOverrides: parseNumericMap(parsed.REPO_FACE_HEARTBEAT_HEAT_OVERRIDES), + codexModel: parsed.REPO_FACE_HEARTBEAT_CODEX_MODEL, + codexModelReasoningEffort: parsed.REPO_FACE_HEARTBEAT_CODEX_REASONING_EFFORT, }, epiphanyAgentRoot: resolve(parsed.EPIPHANY_AGENT_ROOT), indexAllChannels: parsed.INDEX_ALL_CHANNELS, diff --git a/packages/providers/src/owner-codex-provider.ts b/packages/providers/src/owner-codex-provider.ts index c7c7bb2..7d5c396 100644 --- a/packages/providers/src/owner-codex-provider.ts +++ b/packages/providers/src/owner-codex-provider.ts @@ -185,8 +185,8 @@ export class OwnerCodexProvider implements ProviderAdapter { result = await runCodexExec({ executable: this.options.executable, executableArgs: this.options.executableArgs, - model: this.options.model, - reasoningEffort: this.options.reasoningEffort, + model: getStringOption(request.options?.model) ?? this.options.model, + reasoningEffort: getReasoningEffortOption(request.options?.reasoningEffort) ?? this.options.reasoningEffort, timeoutMs: this.options.timeoutMs, workingDirectory: this.options.workingDirectory, prompt: codexPrompt, @@ -387,3 +387,14 @@ export class OwnerCodexProvider implements ProviderAdapter { }; } } + +function getStringOption(value: unknown): string | undefined { + return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined; +} + +function getReasoningEffortOption(value: unknown): "low" | "medium" | "high" | "xhigh" | undefined { + if (value === "low" || value === "medium" || value === "high" || value === "xhigh") { + return value; + } + return undefined; +}