Skip to main content
// Props
import { Panel } from "smithers-orchestrator";

type PanelistConfig = { agent: AgentLike; role?: string; label?: string };

type PanelProps = {
  id?: string;                                           // default: "panel"
  panelists: PanelistConfig[] | AgentLike[];
  moderator: AgentLike;
  panelistOutput: OutputTarget;
  moderatorOutput: OutputTarget;
  strategy?: "synthesize" | "vote" | "consensus";        // default: "synthesize"
  minAgree?: number;                                     // for "vote" / "consensus"
  maxConcurrency?: number;                               // default: Infinity
  skipIf?: boolean;
  children: string | ReactNode;                          // prompt sent to every panelist
};
<Workflow name="code-review-panel">
  <Panel
    panelists={[
      { agent: securityAgent, role: "Security Reviewer" },
      { agent: qualityAgent, role: "Code Quality Reviewer" },
      { agent: architectureAgent, role: "Architecture Reviewer" },
    ]}
    moderator={moderatorAgent}
    panelistOutput={outputs.review}
    moderatorOutput={outputs.synthesis}
  >
    Review the changes in src/auth/ for security, quality, and architecture concerns.
  </Panel>
</Workflow>

Notes

  • Panelist task ids: {prefix}-{label|role|panelist-N}; moderator is {prefix}-moderator.
  • strategy and minAgree are passed as prompt context to the moderator, which interprets them.
  • All panelists write to the same panelistOutput schema, differentiated by task id.

Source

The <Panel> implementation and the files it imports, straight from the package source. This section is generated; edit the source, not this block.
// @smithers-type-exports-begin
/** @typedef {import("./PanelProps.ts").PanelProps} PanelProps */
// @smithers-type-exports-end

import React from "react";
import { Sequence } from "./Sequence.js";
import { Parallel } from "./Parallel.js";
import { Task } from "./Task.js";
/** @typedef {import("@smithers-orchestrator/agents/AgentLike").AgentLike} AgentLike */
/** @typedef {import("./PanelistConfig.ts").PanelistConfig} PanelistConfig */

/**
 * @param {PanelistConfig | AgentLike} entry
 * @param {number} index
 * @returns {PanelistConfig}
 */
function normalizePanelist(entry, index) {
    if ("generate" in entry && !("agent" in entry)) {
        return { agent: entry, label: `panelist-${index}` };
    }
    return entry;
}
/**
 * <Panel> — Parallel specialists review the same input, then a moderator synthesizes.
 *
 * Composes: Sequence > Parallel[Task per panelist] > Task(moderator)
 * @param {PanelProps} props
 */
export function Panel(props) {
    if (props.skipIf)
        return null;
    const { id, panelists, moderator, panelistOutput, moderatorOutput, strategy = "synthesize", minAgree, maxConcurrency, children, } = props;
    const prefix = id ?? "panel";
    const normalized = panelists.map(normalizePanelist);
    // Build parallel panelist tasks
    const panelistTasks = normalized.map((p, i) => {
        const taskId = `${prefix}-${p.label ?? p.role ?? `panelist-${i}`}`;
        return React.createElement(Task, {
            key: taskId,
            id: taskId,
            output: panelistOutput,
            agent: p.agent,
            label: p.role ?? p.label,
            children,
        });
    });
    const parallelEl = React.createElement(Parallel, { maxConcurrency }, ...panelistTasks);
    // Build needs map: each panelist task id -> its task id
    const needs = {};
    normalized.forEach((p, i) => {
        const taskId = `${prefix}-${p.label ?? p.role ?? `panelist-${i}`}`;
        needs[taskId] = taskId;
    });
    // Moderator prompt includes strategy metadata
    const strategyPrompt = strategy === "vote"
        ? `\n\nStrategy: VOTE. Count how many panelists agree. ${minAgree ? `Minimum agreement required: ${minAgree}.` : ""}`
        : strategy === "consensus"
            ? `\n\nStrategy: CONSENSUS. All panelists must converge. ${minAgree ? `Minimum agreement required: ${minAgree}.` : ""}`
            : `\n\nStrategy: SYNTHESIZE. Combine all panelist outputs into a single coherent result. Preserve each panelist's concrete, grounded findings verbatim (specific file paths, line numbers, identifiers, prior-PR references, and what already exists); reconcile disagreements with evidence. Do not over-generalize, drop specifics, or change the scope the panelists analyzed.`;
    const moderatorChildren = `Synthesize the following panelist outputs.${strategyPrompt}`;
    const moderatorTask = React.createElement(Task, {
        id: `${prefix}-moderator`,
        output: moderatorOutput,
        agent: moderator,
        needs,
        children: moderatorChildren,
    });
    return React.createElement(Sequence, null, parallelEl, moderatorTask);
}