import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
initStderrTee("workflows");
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { eagerTool } from "../../../../lib/mcp-eager/dist/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { workflowCreate } from "./tools/workflow-create.js";
import { workflowList } from "./tools/workflow-list.js";
import { workflowGet } from "./tools/workflow-get.js";
import { workflowUpdate } from "./tools/workflow-update.js";
import { workflowDelete } from "./tools/workflow-delete.js";
import { workflowValidate } from "./tools/workflow-validate.js";
import { workflowExecute } from "./tools/workflow-execute.js";
import { workflowRuns } from "./tools/workflow-runs.js";
import { closeDriver } from "./lib/neo4j.js";
import { reapActiveRuns } from "./lib/active-runs.js";
// Cast to `any` to avoid TS2589. TypeScript hits its 50-level instantiation
// limit when accumulating typed tool signatures on a generic McpServer.
// Individual tool handlers retain full type safety in their own files.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const server = new McpServer({
    name: "maxy-workflows",
    version: "0.3.0",
});
// Plugin-system boot tolerance — see Task 202.
// Module-init must never throw; plugin-system spawn enumerates tools/list
// before ACCOUNT_ID is set. Per-call refusal preserves account isolation.
const accountId = process.env.ACCOUNT_ID ?? null;
process.stderr.write(`[workflows] boot accountId=${accountId ?? "null"}\n`);
function refuseNoAccount(toolName) {
    process.stderr.write(`[workflows] tool=${toolName} refuse reason=no-account-context\n`);
    return {
        content: [
            {
                type: "text",
                text: `${toolName} cannot execute: this MCP instance was launched without ACCOUNT_ID (plugin-system metadata-only mode). Per-account workflow writes require ACCOUNT_ID at spawn.`,
            },
        ],
        isError: true,
    };
}
const agentSlug = process.env.AGENT_SLUG || undefined;
const sessionId = process.env.SESSION_ID || undefined;
// Task 476 — boot signal so a stale spawn-env contract is visible at the
// first byte of stderr.
process.stderr.write(`[workflows] boot accountId=${accountId ?? "-"} sessionId=${sessionId ?? "-"} agentSlug=${agentSlug ?? "-"}\n`);
// ---------------------------------------------------------------------------
// Shared schema fragments
// ---------------------------------------------------------------------------
const workflowIdSchema = z.string().describe("Workflow ID (UUID)");
const stepSchema = z.object({
    type: z.literal("tool").describe("Step type: direct MCP tool call"),
    plugin: z.string().min(1).describe("Plugin name (e.g. 'memory', 'scheduling')"),
    tool: z.string().min(1).describe("Tool name within the plugin (e.g. 'contact-create')"),
    params: z.record(z.unknown()).optional().describe("Parameters for the tool call. Use {{outputKey.field}} for template variables that resolve from prior step outputs."),
    dependsOn: z.array(z.string()).optional().describe("Step IDs or position indexes (as strings) that must complete before this step runs"),
    onFailure: z.enum(["abort", "skip", "retry"]).optional().describe("What to do if this step fails (default: abort)"),
    retryLimit: z.number().int().min(1).max(3).optional().describe("Max retry attempts when onFailure is 'retry' (max 3)"),
    outputKey: z.string().optional().describe("Key name to store this step's output under, for use in subsequent step templates"),
    label: z.string().min(1).describe("Human-readable description of what this step does"),
    timeout: z.number().int().min(1).max(300).optional().describe("Step execution timeout in seconds (default: 30). Increase for steps calling slow tools."),
}).strict();
// ---------------------------------------------------------------------------
// workflow-create
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-create", "Create a persistent workflow as an ordered list of MCP tool calls. Validates that every referenced plugin exists — sets status to 'draft' if any are missing, 'active' if all are valid. Plugins that need LLM reasoning own their own SDK call inside a tool; the workflow runner is an MCP-tool orchestrator only.", {
    name: z.string().min(1).describe("Short workflow name (e.g. 'Viewing follow-up')"),
    description: z
        .string()
        .min(1)
        .describe("What this workflow does — used for discovery via semantic search"),
    steps: z.array(stepSchema).min(1).max(50).describe("Ordered list of tool-call steps."),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("workflow-create");
    try {
        const result = await workflowCreate({
            ...params,
            accountId,
            sessionKey: sessionId,
            createdBy: { agent: agentSlug, session: sessionId, tool: "workflow-create" },
        });
        const parts = [`Workflow created: "${params.name}" (ID: ${result.workflowId}, status: ${result.status})`];
        if (result.validationErrors.length > 0) {
            parts.push("");
            parts.push("Validation issues (workflow set to draft):");
            for (const err of result.validationErrors) {
                parts.push(`  - ${err}`);
            }
        }
        return {
            content: [{ type: "text", text: parts.join("\n") }],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Failed to create workflow: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// workflow-list
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-list", "List workflows for this account, optionally filtered by status. Returns name, description, status, and step count for each.", {
    status: z
        .enum(["active", "paused", "draft"])
        .optional()
        .describe("Filter by workflow status"),
    limit: z.number().int().min(1).max(100).optional().describe("Max results (default 50)"),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("workflow-list");
    try {
        const workflows = await workflowList({ ...params, accountId });
        if (workflows.length === 0) {
            return {
                content: [{ type: "text", text: "No workflows found." }],
            };
        }
        const lines = workflows.map((w) => `[${w.status.toUpperCase()}] ${w.name} — ${w.description} (${w.stepCount} steps, ${w.workflowId})`);
        return {
            content: [
                {
                    type: "text",
                    text: `${workflows.length} workflow(s):\n\n${lines.join("\n")}`,
                },
            ],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `List failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// workflow-get
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-get", "Fetch a single workflow by ID. Returns all fields including each step's tool reference and parameters.", {
    workflowId: workflowIdSchema,
}, async ({ workflowId: wfId }) => {
    if (!accountId)
        return refuseNoAccount("workflow-get");
    try {
        const workflow = await workflowGet(wfId, accountId);
        if (!workflow) {
            return {
                content: [
                    { type: "text", text: `Workflow not found: ${wfId}` },
                ],
                isError: true,
            };
        }
        const stepLines = workflow.steps.map((s, i) => {
            const parts = [`  ${i + 1}. [tool] ${s.label} → ${s.plugin}/${s.tool}`];
            if (s.params)
                parts.push(`     Params: ${JSON.stringify(s.params)}`);
            if (s.outputKey)
                parts.push(`     Output key: ${s.outputKey}`);
            if (s.dependsOn && s.dependsOn.length > 0)
                parts.push(`     Depends on: ${s.dependsOn.join(", ")}`);
            parts.push(`     On failure: ${s.onFailure}${s.retryLimit ? ` (max ${s.retryLimit} retries)` : ""}`);
            return parts.join("\n");
        });
        const output = [
            `Workflow: ${workflow.name}`,
            `ID: ${workflow.workflowId}`,
            `Status: ${workflow.status}`,
            `Steps: ${workflow.steps.length}`,
            ``,
            `Description:\n${workflow.description}`,
            ``,
            `Steps:`,
            ...stepLines,
            ``,
            `Created: ${workflow.createdAt} | Updated: ${workflow.updatedAt}`,
        ];
        return {
            content: [{ type: "text", text: output.join("\n") }],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Get failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// workflow-update
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-update", "Update a workflow's name, description, steps, or status. When steps are provided, replaces all existing steps and re-validates capabilities. Re-embeds automatically when name or description changes.", {
    workflowId: workflowIdSchema,
    name: z.string().min(1).optional().describe("New workflow name"),
    description: z.string().min(1).optional().describe("New description"),
    steps: z.array(stepSchema).min(1).max(50).optional().describe("New step list — replaces all existing steps. Triggers re-validation."),
    status: z
        .enum(["active", "paused"])
        .optional()
        .describe("New status (use 'paused' to deactivate without deleting)"),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("workflow-update");
    const { workflowId: wfId, ...updates } = params;
    const hasUpdate = Object.values(updates).some((v) => v !== undefined);
    if (!hasUpdate) {
        return {
            content: [
                {
                    type: "text",
                    text: "No update fields provided. Specify at least one of: name, description, steps, status.",
                },
            ],
            isError: true,
        };
    }
    try {
        await workflowUpdate({ ...params, accountId });
        return {
            content: [
                { type: "text", text: `Workflow updated: ${wfId}` },
            ],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Update failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// workflow-delete
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-delete", "Soft-delete a workflow and cascade to its steps, runs, and step results. Nodes are marked :Trashed (restorable via memory-restore within the grace period) and permanently removed later by memory-empty-trash. Confirm with the user first.", {
    workflowId: workflowIdSchema,
}, async ({ workflowId: wfId }) => {
    if (!accountId)
        return refuseNoAccount("workflow-delete");
    try {
        const result = await workflowDelete(wfId, accountId);
        const summary = result.alreadyTrashed
            ? `Workflow already trashed: ${wfId}`
            : `Workflow soft-deleted: ${wfId} (cascade — steps: ${result.cascade.steps}, runs: ${result.cascade.runs}, stepResults: ${result.cascade.stepResults})`;
        return {
            content: [
                { type: "text", text: summary },
            ],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Delete failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// workflow-validate
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-validate", "Validate a workflow's step capabilities. Checks that every referenced plugin directory and MCP server exist. Updates status to 'active' if all valid, 'draft' if any are missing.", {
    workflowId: workflowIdSchema,
}, async ({ workflowId: wfId }) => {
    if (!accountId)
        return refuseNoAccount("workflow-validate");
    try {
        const result = await workflowValidate(wfId, accountId);
        if (result.valid) {
            return {
                content: [
                    {
                        type: "text",
                        text: `Workflow "${wfId}" validated successfully — status: active. All step capabilities are available.`,
                    },
                ],
            };
        }
        const parts = [
            `Workflow "${wfId}" has validation issues — status: draft.`,
            "",
            "Missing capabilities:",
        ];
        for (const err of result.errors) {
            parts.push(`  - ${err}`);
        }
        return {
            content: [{ type: "text", text: parts.join("\n") }],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Validation failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// workflow-execute
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-execute", "Execute a workflow. Runs each step in dependency order, dispatching MCP tool calls to the referenced plugins. Creates a WorkflowRun record with per-step results. Only active workflows can be executed.", {
    workflowId: workflowIdSchema,
    context: z.record(z.unknown()).optional().describe("Runtime variables available to step templates (e.g. { email: 'user@example.com' })"),
    trigger: z
        .enum(["conversational", "schedule", "manual"])
        .optional()
        .describe("What triggered this execution (default: conversational)"),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("workflow-execute");
    try {
        const result = await workflowExecute({ ...params, accountId });
        const parts = [
            `Workflow run ${result.status}: ${result.runId}`,
            `Steps: ${result.stepsCompleted} completed, ${result.stepsSkipped} skipped, ${result.stepsFailed} failed (${result.stepsTotal} total)`,
        ];
        if (result.error) {
            parts.push(`Error: ${result.error}`);
        }
        return {
            content: [{ type: "text", text: parts.join("\n") }],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Execution failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// workflow-runs
// ---------------------------------------------------------------------------
eagerTool(server, "workflow-runs", "Query workflow execution history. Returns recent runs with status, timing, and per-step outcome counts. Optionally filter by workflow ID or run status.", {
    workflowId: z.string().optional().describe("Filter to a specific workflow's runs"),
    status: z
        .enum(["running", "completed", "partial", "failed"])
        .optional()
        .describe("Filter by run status"),
    limit: z.number().int().min(1).max(100).optional().describe("Max results (default 20)"),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("workflow-runs");
    try {
        const runs = await workflowRuns({ ...params, accountId });
        if (runs.length === 0) {
            return {
                content: [{ type: "text", text: "No workflow runs found." }],
            };
        }
        const lines = runs.map((r) => {
            const duration = r.completedAt
                ? `${new Date(r.completedAt).getTime() - new Date(r.startedAt).getTime()}ms`
                : "running";
            return [
                `[${r.status.toUpperCase()}] ${r.workflowName} — ${r.trigger} trigger`,
                `  Run: ${r.runId}`,
                `  Started: ${r.startedAt} | Duration: ${duration}`,
                `  Steps: ${r.stepsCompleted}/${r.stepsTotal} completed, ${r.stepsSkipped} skipped, ${r.stepsFailed} failed`,
                r.error ? `  Error: ${r.error}` : null,
            ]
                .filter(Boolean)
                .join("\n");
        });
        return {
            content: [
                {
                    type: "text",
                    text: `${runs.length} run(s):\n\n${lines.join("\n\n")}`,
                },
            ],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Query failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// Shutdown + start
// ---------------------------------------------------------------------------
process.on("SIGINT", async () => {
    await closeDriver();
    process.exit(0);
});
process.on("SIGTERM", async () => {
    try {
        await reapActiveRuns("SIGTERM");
    }
    finally {
        await closeDriver();
        process.exit(0);
    }
});
const transport = new StdioServerTransport();
await server.connect(transport);
//# sourceMappingURL=index.js.map