- Add 'timed_out' to flow_runs/flow_steps CHECK constraints - Add retry_count and max_retries columns to flow_steps - Add timeout detection in advanceInner loop (configurable FLOW_STEP_TIMEOUT_MS) - Add retriable logic: re-dispatch on timeout if maxRetries > 0 and retryCount < maxRetries - Add isRetriable() + shouldRetry() pure decision functions - Add timed_out handling to reconcileResumeStep and reconcileRun - Add 'timed_out' to ws-frames enum, publishStep status type
75 lines
3.9 KiB
TypeScript
75 lines
3.9 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
// BooCoder's config is a superset of the server's Config type so it can be
|
|
// passed directly into the inference runner's InferenceContext. Fields the
|
|
// inference loop reads: LLAMA_SWAP_URL, PROJECT_ROOT_WHITELIST. The rest
|
|
// default to values that satisfy the server's Zod schema without BooCoder
|
|
// needing to supply them in its environment.
|
|
const ConfigSchema = z.object({
|
|
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
PORT: z.coerce.number().int().positive().default(3000),
|
|
HOST: z.string().default('0.0.0.0'),
|
|
DATABASE_URL: z.string().url(),
|
|
LLAMA_SWAP_URL: z.string().url(),
|
|
PROJECT_ROOT_WHITELIST: z.string().default('/opt'),
|
|
BOOTSTRAP_ROOT: z.string().default('/opt/projects'),
|
|
DEFAULT_MODEL: z.string().default('qwen3.6-35b-a3b-mxfp4'),
|
|
LOG_LEVEL: z.string().default('info'),
|
|
CONTAINER_GUIDANCE_FILE: z.string().optional(),
|
|
// Fields needed to satisfy the server's Config type but unused by BooCoder:
|
|
SEARXNG_URL: z.string().url().default('http://100.114.205.53:8888'),
|
|
GITEA_BASE_URL: z.string().url().default('https://git.indifferentketchup.com'),
|
|
GITEA_USER: z.string().default('indifferentketchup'),
|
|
GITEA_TOKEN: z.string().optional(),
|
|
GITEA_SSH_HOST: z.string().default('100.114.205.53:2222'),
|
|
MCP_CONFIG_PATH: z.string().optional(),
|
|
// v2.3: config-backed provider overrides/custom-ACP entries merged over the
|
|
// hardcoded built-ins. Missing file = built-ins only (see provider-config.ts).
|
|
CODER_PROVIDERS_PATH: z.string().default('/data/coder-providers.json'),
|
|
// v2.3 phase 2: tier-2 (cold ACP probe) is skipped when available_agents was
|
|
// probed more recently than this. 24h default — stale model lists self-heal
|
|
// on the next snapshot; an explicit /refresh always re-probes.
|
|
PROVIDER_PROBE_TTL_MS: z.coerce.number().int().positive().default(86_400_000),
|
|
// v2.0.5: cheaper model for titles, summaries, labeling.
|
|
FAST_MODEL: z.string().optional(),
|
|
// SSH access to the host for external agent dispatch (Phase 5)
|
|
BOOCODER_SSH_HOST: z.string().default('100.114.205.53'),
|
|
BOOCODER_SSH_USER: z.string().default('samkintop'),
|
|
// v2.6 Phase 3 (lifecycle hardening). Idle TTL: evict a non-busy warm backend
|
|
// (opencode server / warm-ACP child) after this long with no turn — its worktree
|
|
// + agent_sessions row persist, so the next turn re-spawns + reattaches. 30 min
|
|
// default (design §6).
|
|
AGENT_POOL_IDLE_TTL_MS: z.coerce.number().int().positive().default(1_800_000),
|
|
// LRU cap: max live warm backends before the least-recently-used (non-busy) ones
|
|
// are evicted. Bounds the long-lived-daemon's per-(chat,agent) Map growth.
|
|
AGENT_POOL_MAX_LIVE: z.coerce.number().int().positive().default(10),
|
|
// Periodic sweep cadence (idle/LRU pool eviction + orphan-worktree reap). 60s
|
|
// mirrors the apps/server truncation/stale-streaming sweeper.
|
|
LIFECYCLE_SWEEP_INTERVAL_MS: z.coerce.number().int().positive().default(60_000),
|
|
// Orphan-worktree grace: an on-disk worktree dir with no live `worktrees` row is
|
|
// only reaped after it's been untouched this long (avoids sweeping a dir mid
|
|
// ensureSessionWorktree create). 1h default.
|
|
ORPHAN_WORKTREE_GRACE_MS: z.coerce.number().int().positive().default(3_600_000),
|
|
DEEPSEEK_API_KEY: z.string().optional(),
|
|
DEEPSEEK_BASE_URL: z.string().url().default('https://api.deepseek.com'),
|
|
// v2.9.x: flow step timeout (default 5 min). When a 'running' step exceeds
|
|
// this duration, it is marked 'timed_out' and may be retried.
|
|
FLOW_STEP_TIMEOUT_MS: z.coerce.number().int().positive().default(300_000),
|
|
});
|
|
|
|
export type Config = z.infer<typeof ConfigSchema>;
|
|
|
|
let cached: Config | null = null;
|
|
|
|
export function loadConfig(): Config {
|
|
if (cached) return cached;
|
|
const parsed = ConfigSchema.safeParse(process.env);
|
|
if (!parsed.success) {
|
|
console.error('Invalid environment configuration:');
|
|
console.error(parsed.error.flatten().fieldErrors);
|
|
process.exit(1);
|
|
}
|
|
cached = parsed.data;
|
|
return cached;
|
|
}
|