// Left column: agent picker + model params + system prompt. // // Skills / MCP toggles used to live here too, but they overlapped with the // in-composer ChipPicker in chat-panel.jsx (which toggles session-only, // while these toggled agent.enabledSkills / enabledMCPs as the agent // default). The chat surface picker is enough for day-to-day toggling; // editing the agent's persistent defaults is a Manage-page concern (same // reason "New agent" was moved to Manage in PR pg-new-agent-to-manage). // Removed here 2026-06-06 per user request. function AgentPicker({ agents, activeId, onSelect, onAdd }) { return (
Agents
{agents.map((a) => (
onSelect(a.id)} >
{a.initials}
{a.name}
{a.desc}
))}
{/* PR pg-new-agent-to-manage: "+ New agent" now navigates to /manage?new=agent (Manage page picks up the hint to open the creation form). "Browse" removed — Manage is the single place for agent CRUD; having both inline-create and "browse" in Playground was redundant + the inline path skipped the proper naming/system-prompt fields. */}
); } function ModelSection({ agent, onUpdate, overrideKeys, onReset }) { const provider = agent.modelProvider || "baizhi"; const temporaryCount = overrideKeys.size; const scope = (key) => overrideKeys.has(key) ? "temporary" : "inherited"; const modelOptions = modelOptionsForProvider(provider); const modelValue = agent.model || ""; const displayModel = modelValue || defaultModelForProvider(provider); const setProvider = (provider) => { const knownProvider = modelProviderFor(agent.model); let nextModel = agent.model; if (!nextModel) { nextModel = defaultModelForProvider(provider); } else if (knownProvider && knownProvider !== provider) { nextModel = defaultModelForProvider(provider); } onUpdate({ modelProvider: provider, model: nextModel, }); }; const setModel = (model) => { const nextProvider = modelProviderFor(model); const patch = { model }; const modelBelongsToCurrentProvider = modelOptions.some((item) => item.id === model); if (nextProvider && nextProvider !== provider && !modelBelongsToCurrentProvider) { patch.modelProvider = nextProvider; } onUpdate(patch); }; return (
Model Endpoint {temporaryCount ? Temporary overrides · {temporaryCount} : Agent default} {temporaryCount ? ( ) : null}
setModel(e.target.value)} list="model-options" placeholder={defaultModelForProvider(provider)} /> {modelOptions.map((m) => ( ))}
onUpdate({ temperature: parseFloat(e.target.value) })} />
onUpdate({ top_p: parseFloat(e.target.value) })} />
onUpdate({ max_tokens: parseInt(e.target.value, 10) })} />
模型支持不一,勾选后也可能无思考输出,后续继续适配。
); } function SystemSection({ agent, onUpdate }) { const [open, setOpen] = React.useState(false); const [popoverStyle, setPopoverStyle] = React.useState({}); const triggerRef = React.useRef(null); const popRef = React.useRef(null); const placePopover = React.useCallback(() => { const trigger = triggerRef.current; if (!trigger) return; const rect = trigger.getBoundingClientRect(); const gap = 12; const viewportPad = 16; const left = Math.min( rect.right + gap, window.innerWidth - viewportPad - 360 ); const width = Math.max( 360, Math.min(560, window.innerWidth - left - viewportPad) ); const top = Math.max(56, Math.min(rect.top - 8, window.innerHeight - 520)); const maxHeight = Math.max(320, window.innerHeight - top - viewportPad); setPopoverStyle({ left, top, width, maxHeight }); }, []); React.useEffect(() => { if (!open) return; placePopover(); const onMove = () => placePopover(); window.addEventListener("resize", onMove); window.addEventListener("scroll", onMove, true); return () => { window.removeEventListener("resize", onMove); window.removeEventListener("scroll", onMove, true); }; }, [open, placePopover]); React.useEffect(() => { if (!open) return; const onDown = (e) => { if (popRef.current?.contains(e.target) || triggerRef.current?.contains(e.target)) return; setOpen(false); }; const onKey = (e) => { if (e.key === "Escape") setOpen(false); }; document.addEventListener("mousedown", onDown); document.addEventListener("keydown", onKey); return () => { document.removeEventListener("mousedown", onDown); document.removeEventListener("keydown", onKey); }; }, [open]); const detected = scanPromptVariables(agent.system || ""); const summary = (agent.system || "").trim().replace(/\s+/g, " "); return (
System
{open ? (
System prompt
Variables are rendered by the runtime before each run.
onUpdate({ system, promptVariables })} style={{ minHeight: 132, maxHeight: 260, resize: "vertical" }} />
) : null}
); } function LeftPanel({ agents, activeId, onSelect, onAdd, onUpdateAgent, modelAgent, modelOverride, onUpdateModel, onResetModel }) { const agent = agents.find((a) => a.id === activeId); return (
); } window.LeftPanel = LeftPanel;