Refactor pipeline policies, MCP registry, and unified config/runtime

This commit is contained in:
2026-02-23 13:56:45 -05:00
parent 889087daa1
commit 9b4216dda9
22 changed files with 1441 additions and 587 deletions

View File

@@ -1,3 +1,4 @@
import { getConfig, loadConfig, type AppConfig } from "../config.js";
import { AgentManager, type AgentManagerLimits } from "./manager.js";
import {
createDefaultResourceProvisioningOrchestrator,
@@ -5,143 +6,58 @@ import {
type ResourceProvisioningOrchestrator,
} from "./provisioning.js";
const DEFAULT_LIMITS: AgentManagerLimits = {
maxConcurrentAgents: 4,
maxSessionAgents: 2,
maxRecursiveDepth: 3,
};
const DEFAULT_PROVISIONING_CONFIG: BuiltInProvisioningConfigInput = {
gitWorktree: {
rootDirectory: ".ai_ops/worktrees",
baseRef: "HEAD",
},
portRange: {
basePort: 36000,
blockSize: 32,
blockCount: 512,
primaryPortOffset: 0,
lockDirectory: ".ai_ops/locks/ports",
},
};
function readPositiveIntegerEnv(
key: "AGENT_MAX_CONCURRENT" | "AGENT_MAX_SESSION" | "AGENT_MAX_RECURSIVE_DEPTH",
fallback: number,
): number {
const rawValue = process.env[key]?.trim();
if (!rawValue) {
return fallback;
}
const parsed = Number(rawValue);
if (!Number.isInteger(parsed) || parsed < 1) {
throw new Error(`Environment variable ${key} must be a positive integer.`);
}
return parsed;
}
function readOptionalStringEnv(key: string, fallback: string): string {
const rawValue = process.env[key]?.trim();
if (!rawValue) {
return fallback;
}
return rawValue;
}
function readIntegerEnv(
key: string,
fallback: number,
bounds: {
min: number;
},
): number {
const rawValue = process.env[key]?.trim();
if (!rawValue) {
return fallback;
}
const parsed = Number(rawValue);
if (!Number.isInteger(parsed) || parsed < bounds.min) {
throw new Error(`Environment variable ${key} must be an integer >= ${String(bounds.min)}.`);
}
return parsed;
}
export function loadAgentManagerLimitsFromEnv(): AgentManagerLimits {
function toProvisioningConfig(input: Readonly<AppConfig>): BuiltInProvisioningConfigInput {
return {
maxConcurrentAgents: readPositiveIntegerEnv(
"AGENT_MAX_CONCURRENT",
DEFAULT_LIMITS.maxConcurrentAgents,
),
maxSessionAgents: readPositiveIntegerEnv(
"AGENT_MAX_SESSION",
DEFAULT_LIMITS.maxSessionAgents,
),
maxRecursiveDepth: readPositiveIntegerEnv(
"AGENT_MAX_RECURSIVE_DEPTH",
DEFAULT_LIMITS.maxRecursiveDepth,
),
gitWorktree: {
rootDirectory: input.provisioning.gitWorktree.rootDirectory,
baseRef: input.provisioning.gitWorktree.baseRef,
},
portRange: {
basePort: input.provisioning.portRange.basePort,
blockSize: input.provisioning.portRange.blockSize,
blockCount: input.provisioning.portRange.blockCount,
primaryPortOffset: input.provisioning.portRange.primaryPortOffset,
lockDirectory: input.provisioning.portRange.lockDirectory,
},
};
}
export function loadAgentManagerLimitsFromEnv(env: NodeJS.ProcessEnv = process.env): AgentManagerLimits {
const config = loadConfig(env);
return {
maxConcurrentAgents: config.agentManager.maxConcurrentAgents,
maxSessionAgents: config.agentManager.maxSessionAgents,
maxRecursiveDepth: config.agentManager.maxRecursiveDepth,
};
}
let managerSingleton: AgentManager | undefined;
let provisioningSingleton: ResourceProvisioningOrchestrator | undefined;
export function getAgentManager(): AgentManager {
export function getAgentManager(config: Readonly<AppConfig> = getConfig()): AgentManager {
if (!managerSingleton) {
managerSingleton = new AgentManager(loadAgentManagerLimitsFromEnv());
managerSingleton = new AgentManager({
maxConcurrentAgents: config.agentManager.maxConcurrentAgents,
maxSessionAgents: config.agentManager.maxSessionAgents,
maxRecursiveDepth: config.agentManager.maxRecursiveDepth,
});
}
return managerSingleton;
}
export function loadProvisioningConfigFromEnv(): BuiltInProvisioningConfigInput {
return {
gitWorktree: {
rootDirectory: readOptionalStringEnv(
"AGENT_WORKTREE_ROOT",
DEFAULT_PROVISIONING_CONFIG.gitWorktree?.rootDirectory ?? ".ai_ops/worktrees",
),
baseRef: readOptionalStringEnv(
"AGENT_WORKTREE_BASE_REF",
DEFAULT_PROVISIONING_CONFIG.gitWorktree?.baseRef ?? "HEAD",
),
},
portRange: {
basePort: readIntegerEnv(
"AGENT_PORT_BASE",
DEFAULT_PROVISIONING_CONFIG.portRange?.basePort ?? 36000,
{ min: 1 },
),
blockSize: readIntegerEnv(
"AGENT_PORT_BLOCK_SIZE",
DEFAULT_PROVISIONING_CONFIG.portRange?.blockSize ?? 32,
{ min: 1 },
),
blockCount: readIntegerEnv(
"AGENT_PORT_BLOCK_COUNT",
DEFAULT_PROVISIONING_CONFIG.portRange?.blockCount ?? 512,
{ min: 1 },
),
primaryPortOffset: readIntegerEnv(
"AGENT_PORT_PRIMARY_OFFSET",
DEFAULT_PROVISIONING_CONFIG.portRange?.primaryPortOffset ?? 0,
{ min: 0 },
),
lockDirectory: readOptionalStringEnv(
"AGENT_PORT_LOCK_DIR",
DEFAULT_PROVISIONING_CONFIG.portRange?.lockDirectory ?? ".ai_ops/locks/ports",
),
},
};
export function loadProvisioningConfigFromEnv(
env: NodeJS.ProcessEnv = process.env,
): BuiltInProvisioningConfigInput {
return toProvisioningConfig(loadConfig(env));
}
export function getResourceProvisioningOrchestrator(): ResourceProvisioningOrchestrator {
export function getResourceProvisioningOrchestrator(
config: Readonly<AppConfig> = getConfig(),
): ResourceProvisioningOrchestrator {
if (!provisioningSingleton) {
provisioningSingleton = createDefaultResourceProvisioningOrchestrator(
loadProvisioningConfigFromEnv(),
toProvisioningConfig(config),
);
}
return provisioningSingleton;