Skip to main content
The resolver takes user-selected services and skill packs, then resolves all transitive dependencies, detects conflicts, and validates platform compatibility.

Overview

The resolver is the core engine that transforms user selections into a complete, valid service graph. It handles:
  • Transitive dependency resolution
  • Conflict detection
  • Platform compatibility checks
  • Memory estimation
  • Topological sorting for correct startup order

resolve

Resolves user selections into a complete, valid service list.
import { resolve } from '@better-openclaw/core';
import type { ResolverInput, ResolverOutput } from '@better-openclaw/core';

const input: ResolverInput = {
  services: ['redis', 'postgresql'],
  skillPacks: ['ai-toolkit'],
  proxy: 'caddy',
  monitoring: true,
  platform: 'linux/amd64',
  gpu: false
};

const resolved: ResolverOutput = resolve(input);

if (!resolved.isValid) {
  console.error('Validation errors:', resolved.errors);
} else {
  console.log('Services:', resolved.services.map(s => s.definition.name));
  console.log('Memory estimate:', resolved.estimatedMemoryMB, 'MB');
}

Parameters

input
ResolverInput
required
Configuration object specifying services, skill packs, and options
input.services
string[]
required
Array of service IDs to include
input.skillPacks
string[]
default:"[]"
Array of skill pack IDs to include
input.aiProviders
AiProvider[]
default:"[]"
AI provider configurations
input.proxy
ProxyType
default:"none"
Reverse proxy to add (“caddy”, “traefik”, or “none”)
input.platform
Platform
default:"linux/amd64"
Target deployment platform
input.gpu
boolean
default:"false"
Whether GPU passthrough is available
input.monitoring
boolean
default:"false"
Include monitoring stack (Grafana, Prometheus, Uptime Kuma)
input.memoryThresholds
MemoryThresholds
Custom memory warning thresholds

Returns

services
ResolvedService[]
All services in topological order with metadata
addedDependencies
AddedDependency[]
Dependencies automatically added by the resolver
removedConflicts
object[]
Services removed due to conflicts (currently unused)
warnings
Warning[]
Non-blocking warnings (platform compatibility, memory, etc.)
errors
ResolverError[]
Blocking errors (conflicts, missing services, circular deps)
isValid
boolean
True if no errors were found
estimatedMemoryMB
number
Total estimated memory requirement in MB
aiProviders
AiProvider[]
AI providers passed through from input
gsdRuntimes
GsdRuntime[]
Get-Shit-Done runtimes (currently unused)

Algorithm

The resolver uses a deterministic multi-pass algorithm:
  1. Expand skill packs - Add required services from skill pack definitions
  2. Add proxy - Include reverse proxy if specified
  3. Add monitoring - Include monitoring stack if requested
  4. Validate service IDs - Check all services exist in registry
  5. Resolve transitive dependencies - Iterate until stable (max 50 iterations)
  6. Check recommendations - Warn about recommended but missing services
  7. Detect conflicts - Find services with conflicting requirements
  8. Platform compatibility - Warn if services don’t support target platform
  9. GPU requirements - Warn if GPU-required services run without GPU
  10. Memory estimation - Sum minMemoryMB for all services + 512MB base
  11. Topological sort - Order by dependency graph (Kahn’s algorithm with alphabetical tie-breaking)

Topological Sorting

Services are sorted by their dependency graph to ensure correct Docker Compose startup order. Uses Kahn’s algorithm with alphabetical tie-breaking for deterministic output.
// Example: if Redis depends on nothing, PostgreSQL depends on nothing,
// and n8n depends on both:
// Output: [postgresql, redis, n8n]

Types

ResolverInput

interface ResolverInput {
  services: string[];
  skillPacks?: string[];
  aiProviders?: AiProvider[];
  proxy?: ProxyType;
  platform?: Platform;
  gpu?: boolean;
  monitoring?: boolean;
  memoryThresholds?: MemoryThresholds;
}

MemoryThresholds

interface MemoryThresholds {
  info: number;     // Default: 2048 MB
  warning: number;  // Default: 4096 MB
  critical: number; // Default: 8192 MB
}

ResolvedService

interface ResolvedService {
  definition: ServiceDefinition;
  addedBy: "user" | "dependency" | "skill-pack" | "proxy" | "monitoring";
}

AddedDependency

interface AddedDependency {
  service: string;      // Service name
  serviceId?: string;   // Service ID
  requiredBy?: string;  // Service that required it
  reason: string;       // Human-readable explanation
}

Warning

interface Warning {
  type: string;   // "platform" | "gpu" | "memory" | "recommendation" | etc.
  message: string;
}

ResolverError

interface ResolverError {
  type: string;   // "conflict" | "unknown_service" | "cycle" | etc.
  message: string;
}

Examples

Basic Resolution

import { resolve } from '@better-openclaw/core';

const result = resolve({
  services: ['redis', 'postgresql'],
  platform: 'linux/amd64'
});

console.log(result.services.map(s => s.definition.id));
// => ['postgresql', 'redis']

With Dependencies

// n8n requires PostgreSQL
const result = resolve({
  services: ['n8n'],  // PostgreSQL will be added automatically
  platform: 'linux/amd64'
});

console.log(result.addedDependencies);
// => [{ service: 'postgresql', requiredBy: 'n8n', reason: 'Required by n8n' }]

With Proxy and Monitoring

const result = resolve({
  services: ['redis', 'postgresql'],
  proxy: 'caddy',
  monitoring: true,
  platform: 'linux/amd64'
});

// Result includes: redis, postgresql, caddy, grafana, prometheus, uptime-kuma
console.log(result.services.length);
// => 6

Handling Errors

const result = resolve({
  services: ['service-a', 'service-b'],  // service-b conflicts with service-a
  platform: 'linux/amd64'
});

if (!result.isValid) {
  for (const error of result.errors) {
    console.error(`${error.type}: ${error.message}`);
  }
}

Custom Memory Thresholds

const result = resolve({
  services: ['postgresql', 'redis', 'n8n'],
  platform: 'linux/amd64',
  memoryThresholds: {
    info: 1024,
    warning: 2048,
    critical: 4096
  }
});

for (const warning of result.warnings) {
  if (warning.type === 'memory') {
    console.warn(warning.message);
  }
}

Error Types

  • unknown_service - Service ID not found in registry
  • unknown_skill_pack - Skill pack ID not found
  • conflict - Two services cannot coexist
  • cycle - Circular dependency detected

Warning Types

  • platform - Service may not support target platform
  • gpu - Service requires GPU but GPU not enabled
  • memory - Total memory exceeds threshold
  • recommendation - Service recommends additional service
  • resolution - Dependency resolution hit iteration limit

See Also

Build docs developers (and LLMs) love