CoDEVX / lib /mock-agent.ts
CodexMacTiger
feat: live package-scoped chat and thinking logs
837e3ac
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] },
},
};
}