/**
 * Workflow node schema + NodeExecutor contract (M2; plan D10, spec §A1/§A6).
 *
 * One declarative schema, three consumers: inline UI (M4) / execution (here) / NL generation (M5).
 * Control/data separation (spec §A1): the DagScheduler owns control; executors are pure data
 * transforms `(inputs, params, ctx) → { outputs, firedPorts }`.
 */
import type { DataItem, PortMap } from "./data-item.js";
/** Port type for connection validation (spec §A6). "any" connects to anything. */
export type PortType = "any" | "string" | "number" | "boolean" | "json" | "image" | "video" | "audio" | "binary";
export interface PortDef {
    name: string;
    type?: PortType;
}
/**
 * Port-type compatibility — the SINGLE truth for connection validation (R5 手动连线)。
 * add_edge 拿它兜底;画布的即时拒绝只是这条规则的前端镜像。
 * 规则:any 通配两侧;同型相通;媒体(image/video/audio)可入 binary(媒体 ⊆ 二进制,
 * 例如"存文件"节点收任意二进制)。其余不通。
 */
export declare function portTypesCompatible(src: PortType | undefined, dst: PortType | undefined): boolean;
/** Declarative workflow node (persisted; may carry `{{ }}` expressions in params). */
export interface WorkflowNodeDef {
    id: string;
    kind: string;
    /** Optional human-friendly display name (rename_node patch op; spec D17 §C). */
    name?: string;
    params?: Record<string, unknown>;
    /** Output ports; default ["default"]. if→["true","false"], loop→["loop","done"]. */
    outPorts?: string[];
    /**
     * Out-port data types(端口名 → 类型;缺省 "any")。与 outPorts(名字,控制流)刻意分离:
     * DagScheduler 只关心端口名,类型只参与连线校验(add_edge)与画布 Handle 配色 —— 改
     * outPorts 为混合型会波及全部调度消费方,不值得。
     */
    outPortTypes?: Record<string, PortType>;
    /** Input ports (for type-checked routing); default ["default"]. */
    inPorts?: PortDef[];
    joinPolicy?: "all" | "any";
    maxIterations?: number;
    /** Retry the executor on failure: total attempts = maxTries (≥1); exponential backoff from backoffMs (default 1000). */
    retry?: {
        maxTries: number;
        backoffMs?: number;
    };
    /** Per-attempt deadline; the executor's signal aborts when exceeded (counts as a failure → retry applies). */
    timeoutMs?: number;
    /**
     * What a FINAL failure (after retries) does to the run:
     *   "stop"(default)= run fails loud; "continue" = node marked failed, fires no ports
     *   (downstream skips), other branches keep running; "errorPort" = a DataItem with the
     *   error routes out the "error" port (wire an error-handler/notify subgraph to it).
     */
    onError?: "stop" | "continue" | "errorPort";
    /**
     * Canvas position pinned by a manual drag (set_node_ui patch op). Layout-only — the
     * engine never reads it; absent ⇒ the canvas auto-layouts the node. Travels with the
     * def (Bundle/export/Hub/n8n-import) so a hand-arranged graph stays arranged.
     */
    ui?: {
        x: number;
        y: number;
    };
}
/** A data edge (carries DataItem[]). fromPort/toPort default "default". */
export interface DataEdge {
    from: string;
    to: string;
    fromPort?: string;
    toPort?: string;
    loopBack?: boolean;
}
export interface WorkflowDef {
    id: string;
    nodes: WorkflowNodeDef[];
    edges: DataEdge[];
    /** Workflow-level variables exposed to expressions as $vars. */
    vars?: Record<string, unknown>;
}
/**
 * SINGLE source of the attended/unattended split. Attended = a person is present right now:
 * manual (pressed 运行), intent (just SAID it, confirmed by default D22), form (just SUBMITTED
 * it, D34). Everything else (schedule / im-message / webhook) fires with nobody watching.
 * Lives here (leaf module) so both the gate and host-executors can import it cycle-free.
 */
export declare function isUnattendedTrigger(kind: string): boolean;
export interface ExecutorContext {
    nodeId: string;
    kind: string;
    /** Owning workflow id (def.id) — lets host-backed nodes (approval) tag outbound notifications. */
    workflowId?: string;
    /** This run's trigger kind (manual/schedule/…) — drives fine-grained unattended audits (IT-7b). */
    triggerKind?: string;
    /** 0-based; increments per loop iteration (drives the cache key, spec §A5). */
    runIndex: number;
    vars: Record<string, unknown>;
    trigger?: Record<string, unknown>;
    /** Injected clock for determinism. */
    now: () => Date;
    log?: {
        info(m: string): void;
        warn(m: string): void;
    };
    /** Host capabilities for host-backed node kinds (agent/tool/http/mcp/channel), D13. */
    host?: ExecutorHost;
    /** Cooperative cancellation for long-running host calls. */
    signal?: AbortSignal;
}
/**
 * Capabilities the HOST (openclaw gateway, or the qlogicagent CLI host) provides to back node
 * kinds the engine cannot fulfil on its own. The engine stays a pure executor (plan D12/D13):
 * agent/tool/mcp/http are qlogicagent-owned host capabilities; channel/IM call back into the
 * openclaw channel layer. fail-loud: a host-backed kind with no host throws.
 */
export interface ExecutorHost {
    /** Run an agent turn. Returns the agent's structured output as DataItem(s). */
    runAgent(req: AgentHostRequest): Promise<DataItem[]>;
    /** Invoke a qlogicagent tool by name. */
    invokeTool(req: ToolHostRequest): Promise<DataItem[]>;
    /** Perform an HTTP request (credentials resolved host-side). */
    httpRequest(req: HttpHostRequest): Promise<DataItem[]>;
    /** Invoke an MCP server tool. */
    invokeMcp(req: McpHostRequest): Promise<DataItem[]>;
    /** Send a message out through a channel (飞书/微信/…) via the openclaw channel layer. */
    sendChannel(req: ChannelHostRequest): Promise<DataItem[]>;
    /** Project-scoped memory (memdir) read/write/search for the `memory` kind (spec D25). */
    memory?(req: MemoryHostRequest): Promise<DataItem[]>;
    /** Run another workflow as a step (D34); the host enforces cycle guards. */
    runSubworkflow?(req: SubworkflowHostRequest): Promise<DataItem[]>;
    /**
     * Human-in-the-loop approval (D34): notify the owner and AWAIT the decision in-memory
     * (the run stays "running"; the node shows pending). A restart loses pending approvals —
     * the run then fails loud (durable suspended-run resumption is a tracked follow-up).
     */
    approval?(req: ApprovalHostRequest): Promise<{
        approved: boolean;
        decidedAt: string;
        note?: string;
    }>;
    /**
     * D34 wait-until-webhook: suspend the branch until the given webhook path is called
     * (await-in-memory, same posture as approval — the run stays "running"; a restart loses the
     * wait and the run fails loud; checkpoint replay recovers prior steps on re-run).
     */
    waitWebhook?(req: WebhookWaitHostRequest): Promise<Record<string, unknown>>;
}
export interface AgentHostRequest {
    agentId?: string;
    prompt: string;
    input: DataItem[];
    signal?: AbortSignal;
}
export interface ToolHostRequest {
    tool: string;
    args: Record<string, unknown>;
    /** True when this run's trigger is unattended — the host applies fine-grained audits (IT-7b). */
    unattended?: boolean;
    input: DataItem[];
    signal?: AbortSignal;
}
export interface HttpHostRequest {
    method: string;
    url: string;
    headers?: Record<string, string>;
    body?: unknown;
    /** Vault credential to inject as auth headers at execution time (D29; resolved host-side). */
    credentialId?: string;
    signal?: AbortSignal;
}
export interface McpHostRequest {
    server: string;
    tool: string;
    args: Record<string, unknown>;
    signal?: AbortSignal;
}
export interface ChannelHostRequest {
    channel: string;
    target?: string;
    payload: unknown;
    input: DataItem[];
    signal?: AbortSignal;
}
export interface SubworkflowHostRequest {
    workflowId: string;
    /** The calling workflow's id — joins the cycle-guard chain so top-level runs count too. */
    callerWorkflowId?: string;
    /** Cross-project call target; absent = the calling workflow's own project root. */
    projectId?: string;
    /** Becomes the sub-run's $trigger payload (falls back to the upstream item's json). */
    payload?: Record<string, unknown>;
    input: DataItem[];
    signal?: AbortSignal;
}
export interface ApprovalHostRequest {
    nodeId: string;
    /** Owning workflow id so the approval surface can name/locate the workflow. */
    workflowId?: string;
    /** Plain-language question shown to the owner (默认 "继续执行?"). */
    prompt?: string;
    /** Auto-deny deadline; default 24h. */
    timeoutMs?: number;
    input: DataItem[];
    signal?: AbortSignal;
}
export interface WebhookWaitHostRequest {
    /** Webhook path to wait for (normalized with a leading "/"). */
    path: string;
    nodeId: string;
    workflowId?: string;
    /** Auto-fail deadline; default 24h (a forever-pending branch is a leak, not patience). */
    timeoutMs?: number;
    signal?: AbortSignal;
}
export interface MemoryHostRequest {
    op: "read" | "write" | "search";
    /** Topic-file name; absent → operate on the always-visible index. */
    key?: string;
    /** Content for `write` (falls back to the upstream item's json when omitted). */
    value?: unknown;
    /** Query for `search`. */
    query?: string;
    input: DataItem[];
    signal?: AbortSignal;
}
export interface NodeExecResult {
    /** outPort → DataItem[]. */
    outputs: PortMap;
    /** Control nodes (if/loop) set this; data nodes omit → all outPorts fire. */
    firedPorts?: string[];
}
/** A node executor: pure with respect to (inputs, params, ctx). NO hidden global state. */
export type NodeExecutor = (inputs: PortMap, params: Record<string, unknown>, ctx: ExecutorContext) => Promise<NodeExecResult> | NodeExecResult;
