import type { ChatMessage, ParsedCommand, WorkPackage, WorkPackageOutput, WorkPackageTask, } from "./work-package-types"; import { SIMULATED_EXECUTION_DISCLAIMER } from "./work-package-types"; import type { ChatResponse } from "./board-actions"; import { buildAskGuidance, buildExecutionContent, createSpecTasks, hydrateWorkPackages, } from "./work-package-specs"; function nowIso() { return new Date().toISOString(); } function newId(prefix: string) { return `${prefix}-${crypto.randomUUID()}`; } function pickPrimaryTask(tasks: WorkPackageTask[]): WorkPackageTask | undefined { return tasks.find((t) => t.executable) ?? tasks[0]; } function buildAskResponse(args: { wp: WorkPackage; instruction: string; productIdea?: string; }) { const { wp, instruction, productIdea } = args; const focus = buildAskGuidance(wp) ? `${buildAskGuidance(wp)}` : "I would confirm the required inputs, expected outputs, and the next downstream dependency before drafting the artifact."; const intro = productIdea ? `For ${wp.shortName}, given your product idea about ${productIdea}, the objective here is: ${wp.objective}` : `For ${wp.shortName}, the objective here is: ${wp.objective}`; const questionLine = instruction ? `For your question "${instruction}", the practical interpretation is: ${focus}` : `The practical interpretation is: ${focus}`; return [intro, questionLine].join("\n\n"); } function makeSimulatedOutput(params: { wp: WorkPackage; instruction: string; productIdea?: string; sourceTaskId?: string; }): WorkPackageOutput { const { wp, instruction, productIdea, sourceTaskId } = params; const title = instruction ? `Simulated Execution: ${wp.shortName} — ${instruction}` : `Simulated Execution: ${wp.shortName}`; const generated = buildExecutionContent(wp, instruction, productIdea, sourceTaskId); return { id: newId("out"), title, type: generated.type, content: generated.content, createdAt: nowIso(), sourceTaskId: generated.sourceTaskId, executionMode: "simulated", disclaimer: SIMULATED_EXECUTION_DISCLAIMER, }; } export function runMockAgent(args: { messages: ChatMessage[]; workPackages: WorkPackage[]; selectedWorkPackageId?: string; parsedCommand?: ParsedCommand; }): ChatResponse { const { messages, workPackages, selectedWorkPackageId, parsedCommand } = args; const latestUserText = [...messages].reverse().find((message) => message.role === "user") ?.content; const productBriefFromBoard = workPackages .flatMap((workPackage) => workPackage.inputFiles) .find((input) => input.startsWith("Product brief: ")); const boardProductIdea = productBriefFromBoard?.replace(/^Product brief:\s*/, ""); const productIdea = boardProductIdea || latestUserText; const hydrationIdea = parsedCommand?.mode ? boardProductIdea : latestUserText; if (!parsedCommand?.referencedPackageName || !parsedCommand.mode) { const hydrated = hydrateWorkPackages(workPackages, latestUserText); return { assistantMessage: "Created a working draft of the IoT product package flow from your product idea. The board is now hydrated with package-specific inputs, core sections, and execution tasks, starting from CRS and carrying through service review.", boardAction: { type: "replace_all", workPackageId: null, fields: { replacementWorkPackages: hydrated, }, }, }; } const hydratedPackages = hydrateWorkPackages(workPackages, hydrationIdea); const wp = hydratedPackages.find((x) => x.id === selectedWorkPackageId) ?? hydratedPackages.find( (x) => x.title.toLowerCase() === parsedCommand.referencedPackageName?.toLowerCase() || x.shortName.toLowerCase() === parsedCommand.referencedPackageName?.toLowerCase(), ); if (!wp) { return { assistantMessage: `I couldn't find that work package in the board: "${parsedCommand.referencedPackageName}". ` + "Try using the package short name (e.g. `@SRS ...`) or click a card action to prefill the command.", boardAction: { type: "none", workPackageId: null }, }; } const instruction = (parsedCommand.instruction || "").trim(); if (parsedCommand.mode === "ask") { return { assistantMessage: buildAskResponse({ wp, instruction, productIdea: boardProductIdea || undefined, }), boardAction: { type: "none", workPackageId: null }, }; } if (parsedCommand.mode === "plan") { const plannedTasks = createSpecTasks(wp).map((task, index) => { const existing = wp.tasks[index]; return existing ? { ...existing, ...task } : task; }); return { assistantMessage: `Planned next steps for ${wp.shortName}. (Mock mode)\n\nFocus: ${ instruction || "Refine tasks into an actionable plan." }`, boardAction: { type: "update", workPackageId: wp.id, fields: { tasks: plannedTasks, status: wp.status === "done" ? "done" : "in_progress", }, }, }; } if (parsedCommand.mode === "change") { const updatedObjective = instruction ? `${wp.objective} (Change request: ${instruction})` : `${wp.objective} (Change request applied)`; return { assistantMessage: `Updated ${wp.shortName}. (Mock mode)\n- Objective updated based on your change request.`, boardAction: { type: "update", workPackageId: wp.id, fields: { objective: updatedObjective, status: wp.status === "done" ? "done" : "in_progress", }, }, }; } // execute (simulated) const primaryTask = pickPrimaryTask(wp.tasks); const output = makeSimulatedOutput({ wp, instruction, productIdea, sourceTaskId: primaryTask?.id, }); const updatedTasks = wp.tasks.map((t) => { if (!primaryTask) return t; if (t.id !== primaryTask.id) return t; return { ...t, status: "done" as const }; }); return { assistantMessage: `Simulated execution complete for ${wp.shortName}. (Mock mode)\n\n${SIMULATED_EXECUTION_DISCLAIMER}`, boardAction: { type: "update", workPackageId: wp.id, fields: { tasks: updatedTasks, outputs: [...(wp.outputs ?? []), output] }, }, }; }