/**
 * Core Contracts
 * ==============
 * Self-contained workflow contracts for embeddable usage.
 * No dependency on shared-domain.
 */
type WorkflowNodeType = 'agent' | 'tool' | 'agent_tool' | 'rule' | 'start' | 'end' | 'if_else' | 'code' | 'http_request' | 's3_write' | 'template_transform' | 'iteration' | 'knowledge_base' | 'answer' | 'question_classifier' | 'parameter_extractor' | 'variable_assigner' | 'variable_aggregator' | 'document_extractor' | 'list_operator' | 'iteration_start' | 'note' | 'entity' | 'datasource' | 'model_provider' | 'dashboard_output' | 'group' | 'audio_input' | 'speech_transcriber';
type StartNodeConfig = {
    type: 'start';
    inputVariables: string[];
};
type EndNodeConfig = {
    type: 'end';
    outputVariables: string[];
};
type IfElseNodeConfig = {
    type: 'if_else';
    conditions: Array<{
        variable: string;
        operator: string;
        value: string;
    }>;
    logicalOperator: 'and' | 'or';
};
type CodeOperation = 'passthrough' | 'return' | 'merge' | 'pick' | 'parse_json' | 'regex_extract' | 'xml_titles';
type CodeNodeConfig = {
    type: 'code';
    language: 'javascript' | 'python' | 'typescript';
    code: string;
} | {
    type: 'code';
    operation: CodeOperation;
    /** Dotted ref into the variable pool (`nodeId.path`) for ops that
     *  read a specific upstream value. Leave empty for `passthrough`
     *  which reads the sole incoming edge automatically. */
    from?: string;
    /** `return` — the literal JSON value to emit. */
    value?: unknown;
    /** `pick` — whitelist of keys to project off `incoming`. */
    fields?: string[];
    /** `regex_extract` — regex pattern and flags (`i m s x U R`;
     *  `g` is implicit). `group` defaults to 1 (first capture group). */
    pattern?: string;
    flags?: string;
    group?: number;
    /** `regex_extract` / `xml_titles` — key name to expose matches
     *  under in the node's output object (default: `matches` / `titles`). */
    output?: string;
    /** `xml_titles` — by default drops the channel/feed title; set
     *  `true` to keep it in the result array. */
    keepChannel?: boolean;
};
type HttpRequestNodeConfig = {
    type: 'http_request';
    method: 'GET' | 'POST' | 'PUT' | 'DELETE';
    url: string;
    headers: Record<string, string>;
    body: string;
    timeoutMs: number;
    /** How to decode the response body before storing it under `output.body`.
     *  `json` (default) tries `serde_json::from_slice` and falls back to text.
     *  `text` always stores the body as a raw string — use for RSS/HTML/XML. */
    parseResponse?: 'json' | 'text';
};
type S3WriteNodeConfig = {
    type: 's3_write';
    bucket?: string;
    key: string;
    sourceUrl?: string;
    body?: string;
    contentType?: string;
    timeoutMs?: number;
    connectionId?: string;
    allowMissingTemplateFields?: boolean;
};
type TemplateTransformNodeConfig = {
    type: 'template_transform';
    template: string;
    outputVariable: string;
};
type IterationNodeConfig = {
    type: 'iteration';
    iteratorVariable: string;
    maxIterations: number;
};
type KnowledgeBaseNodeConfig = {
    type: 'knowledge_base';
    sourceId: string;
    topK: number;
    similarityThreshold: number;
};
type AnswerNodeConfig = {
    type: 'answer';
    outputTemplate: string;
    outputVariables: string[];
};
type QuestionClassifierNodeConfig = {
    type: 'question_classifier';
    modelId: string;
    instructions: string;
    categories: Array<{
        id: string;
        name: string;
        description: string;
    }>;
};
type ParameterExtractorNodeConfig = {
    type: 'parameter_extractor';
    modelId: string;
    parameters: Array<{
        name: string;
        type: 'string' | 'number' | 'boolean' | 'array';
        description: string;
        required: boolean;
    }>;
};
type VariableAssignerNodeConfig = {
    type: 'variable_assigner';
    assignments: Array<{
        target: string;
        source: string;
    }>;
};
type VariableAggregatorNodeConfig = {
    type: 'variable_aggregator';
    inputVariables: string[];
    outputVariable: string;
    aggregationMode: 'array' | 'object' | 'concatenate';
};
type DocumentExtractorNodeConfig = {
    type: 'document_extractor';
    extractionMode: 'text' | 'table' | 'structured';
    outputVariable: string;
};
type ListOperatorNodeConfig = {
    type: 'list_operator';
    operation: 'filter' | 'map' | 'sort' | 'limit' | 'deduplicate' | 'flatten';
    inputVariable: string;
    outputVariable: string;
    condition?: string;
    sortKey?: string;
    sortOrder?: 'asc' | 'desc';
    limitCount?: number;
};
type IterationStartNodeConfig = {
    type: 'iteration_start';
    iteratorVariable: string;
    itemVariable: string;
    indexVariable: string;
};
type NoteNodeConfig = {
    type: 'note';
    text: string;
    color: 'yellow' | 'blue' | 'green' | 'pink' | 'purple';
    width: number;
    height: number;
};
type GroupNodeConfig = {
    type: 'group';
    label: string;
    description?: string;
    color: 'indigo' | 'teal' | 'amber' | 'rose' | 'slate';
    isExpanded: boolean;
    width: number;
    height: number;
    collapsedWidth: number;
    collapsedHeight: number;
};
type EntityNodeConfig = {
    type: 'entity';
    entityMasterId: string;
    selectedFields: string[];
    filterVariables: Record<string, string>;
    outputVariable: string;
    limit: number;
};
type DatasourceFilterOp = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'like' | 'in';
type DatasourceFilter = {
    /** Column identifier — must match `[a-zA-Z0-9_]+` (validator enforces). */
    column: string;
    /** Comparison operator; defaults to `eq` when omitted. */
    op?: DatasourceFilterOp;
    /** Scalar (string / number / bool) or array (required when op = `in`).
     *  Strings may reference pool variables via `{{ nodeId.path }}` /
     *  `{{ inputs.field }}` — the engine renders them before binding. */
    value?: string | number | boolean | Array<string | number | boolean> | null;
};
type DatasourceNodeConfig = {
    type: 'datasource';
    datasourceId: string;
    dialect: string;
    table: string;
    selectedColumns: string[];
    /** Preferred shape: full filter array with per-row operator. */
    filters?: DatasourceFilter[];
    /** Legacy: variable-name → column-name map (implicit `eq`). Kept so
     *  older graphs still load; new UI writes `filters` instead. */
    filterVariables?: Record<string, string>;
    outputVariable: string;
    limit: number;
};
type RuleNodeConfig = {
    type: 'rule';
    /** Slug / id of the rule row in `astrlabe.agent_rules`. Required for
     *  the rule executor to resolve a definition at run-time. */
    ruleId?: string;
    /** Execution priority when several rules feed into the same branch. */
    priority?: number;
    /** Master switch. Disabled rules are skipped at run-time. */
    enabled?: boolean;
    entityId?: string;
    outputVariable?: string;
    priceVariable?: string;
    contextVariables?: Record<string, string>;
};
type AgentNodeConfig = {
    type: 'agent';
    /** `astrlabe.agents.id` — identifies which agent config to run. */
    agentId?: string;
    /** Preferred path: references `astrlabe.model_provider_connections.id`. */
    connectionId?: string;
    /** Escape hatch: inline provider + model when no connection row exists. */
    provider?: ModelProviderType;
    model?: string;
    /** Optional override of the agent's stored model id. */
    modelId?: string;
    /** System + user prompts rendered through the template engine
     *  (`{{ nodeId.path }}` / `{{ inputs.x }}`). */
    systemPrompt?: string;
    userPrompt?: string;
    maxTokens?: number;
    temperature?: number;
    /**
     * Optional structured-output contract. When set, the engine parses the
     * LLM's text as JSON, validates against the schema, and exposes the
     * typed object under `output.parsed`. Validation failures fail the
     * node with `AGENT_OUTPUT_SCHEMA_VIOLATION` so downstream nodes never
     * see half-formed data. Schema is the JSON-Schema-Draft-7 subset the
     * Rust validator understands today (`type`, `properties`, `required`,
     * `items`, `enum`).
     */
    outputSchema?: AgentOutputSchema;
};
type AgentOutputSchema = {
    type?: 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null';
    properties?: Record<string, AgentOutputSchema>;
    required?: string[];
    items?: AgentOutputSchema;
    enum?: Array<string | number | boolean | null>;
    description?: string;
};
type ModelProviderType = 'aws_bedrock' | 'openai_api' | 'google_vertex' | 'azure_openai' | 'anthropic_api' | 'groq' | 'mistral' | 'huggingface' | 'ollama' | 'custom';
type ModelProviderNodeConfig = {
    type: 'model_provider';
    /** References astrlabe.model_provider_connections.id when persisted */
    connectionId?: string;
    /** References astrlabe.model_providers.slug */
    providerType: ModelProviderType;
    name?: string;
    region?: string;
    endpoint?: string;
    credentialRef?: string;
    apiKeyRef?: string;
    modelFilter?: string[];
};
/**
 * DashboardOutput is the terminal node that emits a DashboardSpec so the UI
 * (astrlabe, kori-erp) can render the run result without bespoke code. Either
 * inline `dashboard` (literal spec) or `dashboardFrom` (reference to an
 * upstream node's output) is used.
 */
type DashboardOutputNodeConfig = {
    type: 'dashboard_output';
    /** Inline DashboardSpec literal. Shape matches @datatechsolutions/ui DashboardSpec. */
    dashboard?: Record<string, unknown>;
    /** Reference to an upstream node's value (`"nodeId"` or `"nodeId.path"`) that already conforms to DashboardSpec. */
    dashboardFrom?: string;
};
type AudioInputNodeConfig = {
    type: 'audio_input';
    /** Public HTTPS URL or presigned S3 URL. Templates with `{{ }}`
     *  placeholders are rendered against the variable pool at run time. */
    url: string;
    /** Optional container hint (`mp3`, `wav`, `m4a`, …). When omitted the
     *  executor derives it from the URL extension. */
    format?: string;
    allowMissingTemplateFields?: boolean;
};
type SpeechTranscriberNodeConfig = {
    type: 'speech_transcriber';
    /** Template ref to the audio URL (e.g. `"{{ audio_in.url }}"`). */
    audioUrl: string;
    /** Whisper model id. Defaults to `whisper-1` on the executor side. */
    model?: string;
    /** BCP-47 language hint. Leave blank for auto-detect. */
    language?: string;
    /** Vocabulary / style guidance prompt. Template-rendered. */
    prompt?: string;
    /** `verbose_json` (default) is required for segments[] to be populated. */
    responseFormat?: 'verbose_json' | 'json' | 'text';
    /** Override for tests / private proxies. Defaults to the OpenAI endpoint. */
    endpoint?: string;
    /** Default 120_000ms — Whisper can take a while on long audio. */
    timeoutMs?: number;
    allowMissingTemplateFields?: boolean;
};
type LogicNodeConfig = StartNodeConfig | EndNodeConfig | IfElseNodeConfig | CodeNodeConfig | HttpRequestNodeConfig | S3WriteNodeConfig | TemplateTransformNodeConfig | IterationNodeConfig | KnowledgeBaseNodeConfig | AnswerNodeConfig | QuestionClassifierNodeConfig | ParameterExtractorNodeConfig | VariableAssignerNodeConfig | VariableAggregatorNodeConfig | DocumentExtractorNodeConfig | ListOperatorNodeConfig | IterationStartNodeConfig | NoteNodeConfig | GroupNodeConfig | EntityNodeConfig | DatasourceNodeConfig | RuleNodeConfig | AgentNodeConfig | ModelProviderNodeConfig | DashboardOutputNodeConfig | AudioInputNodeConfig | SpeechTranscriberNodeConfig;
type WorkflowNodeData = {
    entityId: string;
    label: string;
    config?: LogicNodeConfig;
    errorStrategy?: 'fail_graph' | 'fail_branch' | 'continue';
};
type WorkflowNode = {
    id: string;
    type: WorkflowNodeType;
    position: {
        x: number;
        y: number;
    };
    data: WorkflowNodeData;
    parentId?: string;
    extent?: 'parent';
    style?: {
        width?: number;
        height?: number;
    };
};
type WorkflowEdge = {
    id: string;
    source: string;
    target: string;
    sourceHandle: string | null;
    targetHandle: string | null;
};
type WorkflowViewport = {
    x: number;
    y: number;
    zoom: number;
};
type WorkflowGraph = {
    nodes: WorkflowNode[];
    edges: WorkflowEdge[];
    viewport?: WorkflowViewport;
};
type Workflow = {
    id: string;
    organizationId: string;
    name: string;
    description?: string;
    graph: WorkflowGraph;
    publishedGraph: WorkflowGraph | null;
    version: number;
    isDraft?: boolean;
    createdAt: Date | string;
    updatedAt: Date | string;
};
type WorkflowNodeStatus = 'pending' | 'running' | 'success' | 'error' | 'skipped';
type VariableValue = string | number | boolean | null | VariableValue[] | {
    [key: string]: VariableValue;
};
type NodeExecutionResult = {
    nodeId: string;
    nodeType: WorkflowNodeType;
    status: WorkflowNodeStatus;
    inputs?: Record<string, VariableValue>;
    outputs: Record<string, VariableValue>;
    error?: string;
    durationMs: number;
    startedAt: string;
    completedAt: string;
};
type WorkflowRunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
type WorkflowRun = {
    id: string;
    workflowId: string;
    organizationId: string;
    status: WorkflowRunStatus;
    inputVariables: Record<string, VariableValue>;
    outputVariables: Record<string, VariableValue>;
    nodeResults: NodeExecutionResult[];
    error: string | null;
    totalDurationMs: number | null;
    triggeredBy: string;
    graphSnapshot: WorkflowGraph;
    createdAt: string;
    updatedAt: string;
};
type AgentConfig = {
    agentId: string;
    id?: string;
    name: string;
    /** Human-friendly display name — name is the slug-ish id, displayName
     *  is what shows up in admin listings and agent pickers. */
    displayName?: string;
    description?: string;
    role?: string;
    avatar?: string;
    modelId?: string;
    modelProviderId?: string;
    /** Preferred — references `astrlabe.model_provider_connections.id`. */
    connectionId?: string;
    framework?: string;
    instruction?: string;
    /** System prompt; historically some seeds use `systemPrompt`, others
     *  `instruction`. UI reads `systemPrompt ?? instruction`. */
    systemPrompt?: string;
    order?: number;
    temperature?: number;
    maxTokens?: number;
    /** Canonical Bedrock-aligned field. Treated as a synonym of
     *  `maxTokens` — new UIs write this one. */
    maxOutputTokens?: number;
    topP?: number;
    topK?: number;
    /** ELO score driving the difficulty tier in the canvas. */
    elo?: number;
    difficulty?: 'beginner' | 'intermediate' | 'advanced' | 'expert' | string;
    tags?: string[];
    status?: 'draft' | 'active' | 'archived' | string;
    metadata?: Record<string, unknown>;
    version?: number;
    enabled?: boolean;
    active?: boolean;
    [key: string]: unknown;
};
/**
 * Canonical persist payload the `AgentModal` emits via `onPersist`.
 * The receiver translates this to the shape the backend handler
 * (POST /agents/configs or PATCH /agents/configs/{id}) expects.
 */
type AgentPersistPayload = {
    agentId?: string;
    name: string;
    displayName?: string;
    description?: string;
    role?: string;
    avatar?: string;
    modelId?: string;
    connectionId?: string;
    framework?: string;
    systemPrompt?: string;
    temperature?: number;
    maxOutputTokens?: number;
    topP?: number;
    topK?: number;
    elo?: number;
    difficulty?: string;
    tags?: string[];
    status?: string;
    enabledToolIds?: string[];
    metadata?: Record<string, unknown>;
    /** Structured-output contract — when set, the engine parses + validates
     *  the LLM's response against this schema. `undefined` clears any
     *  previously-stored schema. See `AgentOutputSchema` for the supported
     *  JSON Schema subset. */
    outputSchema?: AgentOutputSchema;
};
type AgentModel = {
    id: string;
    name: string;
    provider: string;
    enabled: boolean;
};
type WorkflowTool = {
    toolId: string;
    name: string;
    category?: string;
    icon?: string;
    endpoint?: string;
    method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
    enabled: boolean;
    [key: string]: unknown;
};
type AgentTool = {
    agentToolId: string;
    name: string;
    description?: string;
    category?: 'search' | 'code' | 'file' | 'web' | 'math' | 'custom';
    icon?: string;
    enabled: boolean;
    compatibleFrameworks?: string[];
    parameters?: Array<{
        name: string;
        type: string;
        description: string;
        required: boolean;
    }>;
    [key: string]: unknown;
};
type AgentRule = {
    ruleId: string;
    name: string;
    description?: string;
    enabled: boolean;
    /** Visual order in the admin list. Alias for `priority` kept for
     *  legacy clients — new code should read `priority`. */
    order?: number;
    priority?: number;
    /** Canonical singular — object with an `operator` key that the Rust
     *  `RuleExecutor` dispatches on. */
    condition?: Record<string, unknown>;
    /** Canonical singular — object with `type` + `params`. */
    action?: Record<string, unknown>;
    /** Legacy: older servers returned an array of action entries. Keep
     *  for backwards compat so the UI can still render historical
     *  payloads. */
    conditions?: Array<Record<string, unknown>>;
    actions?: Array<Record<string, unknown>>;
    status?: 'draft' | 'active' | 'archived' | string;
    tags?: string[];
    validFrom?: string | null;
    validUntil?: string | null;
    [key: string]: unknown;
};

export type { AgentConfig, AgentModel, AgentNodeConfig, AgentOutputSchema, AgentPersistPayload, AgentRule, AgentTool, AnswerNodeConfig, AudioInputNodeConfig, CodeNodeConfig, CodeOperation, DashboardOutputNodeConfig, DatasourceFilter, DatasourceFilterOp, DatasourceNodeConfig, DocumentExtractorNodeConfig, EndNodeConfig, EntityNodeConfig, GroupNodeConfig, HttpRequestNodeConfig, IfElseNodeConfig, IterationNodeConfig, IterationStartNodeConfig, KnowledgeBaseNodeConfig, ListOperatorNodeConfig, LogicNodeConfig, ModelProviderNodeConfig, ModelProviderType, NodeExecutionResult, NoteNodeConfig, ParameterExtractorNodeConfig, QuestionClassifierNodeConfig, RuleNodeConfig, S3WriteNodeConfig, SpeechTranscriberNodeConfig, StartNodeConfig, TemplateTransformNodeConfig, VariableAggregatorNodeConfig, VariableAssignerNodeConfig, VariableValue, Workflow, WorkflowEdge, WorkflowGraph, WorkflowNode, WorkflowNodeData, WorkflowNodeStatus, WorkflowNodeType, WorkflowRun, WorkflowRunStatus, WorkflowTool, WorkflowViewport };
