moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
import { buildTaskFit, type AgentPersona, type ExecutorCandidate } from '@waggle/agent';
import { BUILTIN_TOOL_MANIFESTS, type DetectedTool } from '@waggle/shared';
const DETECTION_CACHE_MS = 30_000;
const DEFAULT_RATE_LIMIT_MS = 15 * 60_000;
const PERSONA_IDS = [
'general-purpose',
'coder',
'writer',
'researcher',
'analyst',
] as const;
const EGRESS_DESTINATIONS: Readonly<Record<string, string>> = {
'claude-code': 'Anthropic',
codex: 'OpenAI',
hermes: 'Nous',
openclaw: 'configured provider',
};
interface ExecutorRegistryDeps {
detectTools: () => Promise<DetectedTool[]>;
personas: () => AgentPersona[];
}
type RateLimitObservation =
| { state: 'available' }
| { state: 'observed_exhausted'; expiresAtMs: number; resumeAtMs?: number };
function normalizeExecutorId(executorId: string): string {
if (executorId.startsWith('external:') || executorId.startsWith('persona:')) {
return executorId;
}
return `external:${executorId}`;
}
export class ExecutorRegistry {
private readonly deps: ExecutorRegistryDeps;
private detectionCache: { detectedAtMs: number; tools: DetectedTool[] } | null = null;
private readonly rateLimits = new Map<string, RateLimitObservation>();
constructor(deps: ExecutorRegistryDeps) {
this.deps = deps;
}
async snapshot(nowMs: number): Promise<ExecutorCandidate[]> {
const detectedTools = await this.getDetectedTools(nowMs);
const detectedById = new Map(detectedTools.map((tool) => [tool.id, tool]));
const personasById = new Map(this.deps.personas().map((persona) => [persona.id, persona]));
const personas = PERSONA_IDS.flatMap((personaId) => {
const persona = personasById.get(personaId);
if (!persona || persona.isReadOnly) return [];
const id = `persona:${persona.id}`;
return [{
id,
kind: 'persona' as const,
displayName: persona.name,
taskFit: buildTaskFit(id),
authClass: 'api-key' as const,
installed: true,
healthy: true,
rateLimit: this.rateLimitFor(id, nowMs, 'unknown'),
supportsHeadless: false,
// Personas run through the workspace's configured LLM provider, which
// is typically hosted — private tasks must not silently route there.
// v1 has no local-provider detection, so fail closed (consensus plan §2).
egressDestination: 'configured model provider',
}];
});
const externals = BUILTIN_TOOL_MANIFESTS
.filter((manifest) => manifest.capabilities?.headlessTask === true && manifest.task)
.map((manifest): ExecutorCandidate => {
const detected = detectedById.get(manifest.id);
const installed = detected?.installed === true;
const id = `external:${manifest.id}`;
// Confirm dispatches with access:'read-only' (v1 hard default); tools
// without a read-only mode would 409 post-confirm, so gate them here.
const supportsReadOnly = manifest.task?.permissionModes?.includes('read-only') === true;
return {
id,
kind: 'external',
displayName: manifest.displayName,
taskFit: buildTaskFit(id),
authClass: 'subscription-cli',
installed,
healthy: installed,
rateLimit: this.rateLimitFor(id, nowMs, 'unknown'),
supportsHeadless: supportsReadOnly,
egressDestination: EGRESS_DESTINATIONS[manifest.id] ?? 'configured provider',
};
});
return [...personas, ...externals];
}
noteRateLimit(executorId: string, resumeAtMs: number | null): void {
const nowMs = Date.now();
this.rateLimits.set(normalizeExecutorId(executorId), {
state: 'observed_exhausted',
expiresAtMs: resumeAtMs ?? nowMs + DEFAULT_RATE_LIMIT_MS,
...(resumeAtMs === null ? {} : { resumeAtMs }),
});
}
noteHealthy(executorId: string): void {
this.rateLimits.set(normalizeExecutorId(executorId), { state: 'available' });
}
private async getDetectedTools(nowMs: number): Promise<DetectedTool[]> {
if (
this.detectionCache
&& nowMs - this.detectionCache.detectedAtMs < DETECTION_CACHE_MS
) {
return this.detectionCache.tools;
}
const tools = await this.deps.detectTools();
this.detectionCache = { detectedAtMs: nowMs, tools };
return tools;
}
private rateLimitFor(
executorId: string,
nowMs: number,
initialState: 'unknown' | 'available',
): ExecutorCandidate['rateLimit'] {
const observation = this.rateLimits.get(executorId);
if (!observation) return { state: initialState };
if (observation.state === 'available') return observation;
if (nowMs >= observation.expiresAtMs) {
const available = { state: 'available' as const };
this.rateLimits.set(executorId, available);
return available;
}
return {
state: observation.state,
...(observation.resumeAtMs === undefined ? {} : { resumeAtMs: observation.resumeAtMs }),
};
}
}
declare module 'fastify' {
interface FastifyInstance {
executorRegistry: ExecutorRegistry;
}
}