import "server-only"; import type { ConversationActionResolution } from "@/lib/ai/conversation-action-resolver"; import type { TaskBoundaryDecision } from "@/lib/ai/runtime/task-boundary-classifier"; export interface MemoryRelevancePolicy { includePromptMemory: boolean; includeImageWorkflowMemory: boolean; includeStoryboardPreferenceMemory: boolean; includeWorkflowState: boolean; maxMemoryHints: number; maxContextLines: number; reason: string; } export function resolveMemoryRelevancePolicy(params: { taskBoundaryDecision: TaskBoundaryDecision; actionResolution: ConversationActionResolution; hasActiveStoryboardTask: boolean; hasPendingGridConfirmation: boolean; hasCurrentImageInput: boolean; hasCanvasSelection: boolean; }) { const { taskBoundaryDecision, actionResolution } = params; switch (taskBoundaryDecision.taskType) { case "greeting": return { includePromptMemory: false, includeImageWorkflowMemory: false, includeStoryboardPreferenceMemory: false, includeWorkflowState: false, maxMemoryHints: 0, maxContextLines: 2, reason: "greeting_turn_ignores_task_memory", } satisfies MemoryRelevancePolicy; case "chat": return { includePromptMemory: false, includeImageWorkflowMemory: false, includeStoryboardPreferenceMemory: false, includeWorkflowState: false, maxMemoryHints: 1, maxContextLines: 3, reason: "chat_turn_prefers_clean_context", } satisfies MemoryRelevancePolicy; case "prompt_only": return { includePromptMemory: false, includeImageWorkflowMemory: false, includeStoryboardPreferenceMemory: false, includeWorkflowState: false, maxMemoryHints: 2, maxContextLines: 4, reason: "prompt_only_turn_ignores_execution_memory", } satisfies MemoryRelevancePolicy; case "analysis_only": return { includePromptMemory: false, includeImageWorkflowMemory: false, includeStoryboardPreferenceMemory: false, includeWorkflowState: false, maxMemoryHints: 2, maxContextLines: 4, reason: "analysis_only_turn_ignores_workflow_memory", } satisfies MemoryRelevancePolicy; case "execution_video": return { includePromptMemory: false, includeImageWorkflowMemory: false, includeStoryboardPreferenceMemory: true, includeWorkflowState: params.hasActiveStoryboardTask || params.hasPendingGridConfirmation, maxMemoryHints: 4, maxContextLines: 6, reason: "video_execution_prefers_storyboard_preferences", } satisfies MemoryRelevancePolicy; case "workflow_followup": return { includePromptMemory: actionResolution.kind === "image_generation_followup" || actionResolution.kind === "prompt_carry_forward", includeImageWorkflowMemory: actionResolution.kind === "image_edit_followup" || actionResolution.kind === "image_generation_followup" || actionResolution.kind === "prompt_carry_forward", includeStoryboardPreferenceMemory: actionResolution.kind === "grid_confirmation_followup" || actionResolution.kind === "active_storyboard_followup", includeWorkflowState: true, maxMemoryHints: 6, maxContextLines: 8, reason: "workflow_followup_requires_workflow_memory", } satisfies MemoryRelevancePolicy; case "execution_image": default: return { includePromptMemory: actionResolution.usePromptCarryForward || taskBoundaryDecision.taskMode === "continue", includeImageWorkflowMemory: taskBoundaryDecision.taskMode === "continue" || (!params.hasCurrentImageInput && !params.hasCanvasSelection), includeStoryboardPreferenceMemory: false, includeWorkflowState: taskBoundaryDecision.taskMode === "continue" && (actionResolution.kind === "image_edit_followup" || actionResolution.kind === "image_generation_followup" || actionResolution.kind === "prompt_carry_forward"), maxMemoryHints: taskBoundaryDecision.taskMode === "continue" ? 5 : 3, maxContextLines: taskBoundaryDecision.taskMode === "continue" ? 7 : 5, reason: taskBoundaryDecision.taskMode === "continue" ? "image_execution_continue_reuses_relevant_memory" : "image_execution_new_task_limits_old_memory", } satisfies MemoryRelevancePolicy; } } export function applyMemoryHintLimit(hints: string[], maxHints: number) { if (!Array.isArray(hints) || maxHints <= 0) return [] as string[]; return hints.slice(0, maxHints); } export function applyContextLineLimit(summary: string | undefined, maxLines: number) { const normalized = String(summary || "").trim(); if (!normalized || maxLines <= 0) return ""; return normalized .split("\n") .map((line) => line.trim()) .filter(Boolean) .slice(0, maxLines) .join("\n"); }