feat(agent): localize generated agent workflow

This commit is contained in:
2026-04-10 13:56:11 +02:00
parent 66646bd62f
commit ddb2412349
10 changed files with 950 additions and 89 deletions

View File

@@ -25,6 +25,16 @@ export type AgentExecutionPlan = {
steps: AgentExecutionStep[];
};
export type AgentBriefConstraints = {
briefing: string;
audience: string;
tone: string;
targetChannels: string[];
hardConstraints: string[];
};
export type AgentLocale = "de" | "en";
export type AgentAnalyzeResult = {
clarificationQuestions: AgentClarificationQuestion[];
executionPlan: AgentExecutionPlan | null;
@@ -46,6 +56,102 @@ function normalizeStepId(value: unknown): string {
.replace(/\s+/g, "-");
}
function normalizeStringArray(raw: unknown, options?: { lowerCase?: boolean }): string[] {
if (!Array.isArray(raw)) {
return [];
}
const seen = new Set<string>();
const normalized: string[] = [];
for (const item of raw) {
const trimmed = trimString(item);
if (trimmed === "") {
continue;
}
const value = options?.lowerCase ? trimmed.toLowerCase() : trimmed;
if (seen.has(value)) {
continue;
}
seen.add(value);
normalized.push(value);
}
return normalized;
}
export function normalizeAgentBriefConstraints(raw: unknown): AgentBriefConstraints {
const rawRecord =
raw && typeof raw === "object" && !Array.isArray(raw)
? (raw as Record<string, unknown>)
: null;
return {
briefing: trimString(rawRecord?.briefing),
audience: trimString(rawRecord?.audience),
tone: trimString(rawRecord?.tone),
targetChannels: normalizeStringArray(rawRecord?.targetChannels, { lowerCase: true }),
hardConstraints: normalizeStringArray(rawRecord?.hardConstraints),
};
}
export function normalizeAgentLocale(raw: unknown): AgentLocale {
if (raw === "de" || raw === "en") {
return raw;
}
return "de";
}
export type PreflightClarificationInput = {
briefConstraints: AgentBriefConstraints | unknown;
incomingContextCount: number;
};
const BRIEFING_REQUIRED_QUESTION: AgentClarificationQuestion = {
id: "briefing",
prompt: "What should the agent produce? Provide the brief in one or two sentences.",
required: true,
};
const TARGET_CHANNELS_REQUIRED_QUESTION: AgentClarificationQuestion = {
id: "target-channels",
prompt: "Which channels should this run target? List at least one channel.",
required: true,
};
const INCOMING_CONTEXT_REQUIRED_QUESTION: AgentClarificationQuestion = {
id: "incoming-context",
prompt: "No context was provided. What source context should the agent use?",
required: true,
};
export function buildPreflightClarificationQuestions(
input: PreflightClarificationInput,
): AgentClarificationQuestion[] {
const normalizedBriefConstraints = normalizeAgentBriefConstraints(input.briefConstraints);
const incomingContextCount = Number.isFinite(input.incomingContextCount)
? Math.max(0, Math.trunc(input.incomingContextCount))
: 0;
const questions: AgentClarificationQuestion[] = [];
if (normalizedBriefConstraints.briefing === "") {
questions.push(BRIEFING_REQUIRED_QUESTION);
}
if (normalizedBriefConstraints.targetChannels.length === 0) {
questions.push(TARGET_CHANNELS_REQUIRED_QUESTION);
}
if (incomingContextCount === 0) {
questions.push(INCOMING_CONTEXT_REQUIRED_QUESTION);
}
return questions;
}
export function normalizeAgentExecutionPlan(raw: unknown): AgentExecutionPlan {
const rawRecord =
raw && typeof raw === "object" && !Array.isArray(raw)