Submatter/apps/worker/src/harness.ts

95 lines
3.2 KiB
TypeScript
Raw Normal View History

import { ALLOWED_COMMANDS } from "@tasks/shared/worker";
import type { HarnessType } from "@tasks/shared/worker";
export type HarnessConfig = {
alias: string;
harnessType: HarnessType;
needsWorktree: boolean;
allowedCommands: readonly string[] | null;
endpointUrl?: string | null;
authEnvVar?: string | null;
};
const DOC_WRITER: HarnessConfig = {
alias: "doc-writer",
harnessType: "minimal",
needsWorktree: false,
allowedCommands: [],
};
const DEFAULT: HarnessConfig = {
alias: "minimal",
harnessType: "minimal",
needsWorktree: true,
allowedCommands: null,
};
const HARNESS_DEFAULTS: Record<string, Partial<HarnessConfig>> = {
minimal: { harnessType: "minimal", needsWorktree: true, allowedCommands: null },
crewai: { harnessType: "crewai", needsWorktree: true, allowedCommands: null },
langgraph: { harnessType: "langgraph", needsWorktree: true, allowedCommands: null },
"remote-ag-ui": { harnessType: "remote-ag-ui", needsWorktree: false, allowedCommands: [] },
};
const HARNESS_BY_ALIAS: Record<string, HarnessConfig> = {
"doc-writer": DOC_WRITER,
minimal: DEFAULT,
default: DEFAULT,
};
function configForAlias(alias: string): HarnessConfig {
if (HARNESS_BY_ALIAS[alias]) return HARNESS_BY_ALIAS[alias]!;
const def = HARNESS_DEFAULTS[alias] as Partial<HarnessConfig> | undefined;
if (def?.harnessType) {
return {
alias,
harnessType: def.harnessType,
needsWorktree: def.needsWorktree ?? true,
allowedCommands: def.allowedCommands ?? null,
};
}
return { alias, harnessType: "minimal", needsWorktree: true, allowedCommands: null };
}
/**
* Resolve harness config for a job. Brief 3 adds harness_providers + remote-ag-ui.
* - If job carries `harnessAlias` use it (from orchestrator or DB route).
* - Else if roleId itself is a harness alias use it.
* - Else fallback to minimal.
*/
export function resolveHarnessForJob(job: {
roleId: string;
harnessAlias?: string | null;
harnessType?: string | null;
endpointUrl?: string | null;
authEnvVar?: string | null;
}): HarnessConfig {
const alias = job.harnessAlias ?? (HARNESS_BY_ALIAS[job.roleId] ? job.roleId : null);
if (alias) {
const base = configForAlias(alias);
return {
...base,
...(job.harnessType ? { harnessType: job.harnessType as HarnessType } : {}),
...(job.endpointUrl !== undefined ? { endpointUrl: job.endpointUrl } : {}),
...(job.authEnvVar !== undefined ? { authEnvVar: job.authEnvVar } : {}),
};
}
if (job.roleId.includes("doc")) return DOC_WRITER;
return DEFAULT;
}
export function allowedCommandsForHarness(harness: HarnessConfig): readonly string[] | null {
return harness.allowedCommands === null ? ALLOWED_COMMANDS : harness.allowedCommands;
}
/**
* Executor registry — minimal adapter delegates to MinimalSession; remote adapters
* could call AG-UI endpoint. For brief 3 all adapters share the same local
* MinimalSession implementation but are differentiated by harnessType for
* observability and future extension.
*/
export function getHarnessExecutor(alias: string): { id: string; harnessType: HarnessType } {
const cfg = configForAlias(alias);
return { id: cfg.alias, harnessType: cfg.harnessType };
}