@growthub/cli 0.13.2 → 0.13.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/metadata-graph/route.js +184 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +24 -2
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/route.js +14 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/login/route.js +74 -0
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/logout/route.js +67 -0
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/status/route.js +77 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +72 -4
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +326 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +123 -27
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +6 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +224 -1
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +754 -92
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxAgentAuthPanel.jsx +224 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxRunPanel.jsx +32 -1
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/WorkspaceGraphInspectorPanel.jsx +226 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +530 -9
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +8 -1
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +10 -7
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/RunSetupPanel.jsx +261 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +119 -9
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +779 -138
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +91 -14
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/sandbox-environment-primitive.md +35 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +923 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +28 -3
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +216 -5
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +412 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-inputs.js +366 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +34 -3
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-eligibility.js +50 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-redaction.js +64 -0
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +665 -0
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-host-catalog.js +168 -0
  34. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-chart-values.js +595 -0
  35. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +164 -7
  36. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +11 -0
  37. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-graph.js +646 -0
  38. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-selectors.js +249 -0
  39. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +1186 -0
  40. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +111 -1
  41. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +14 -0
  42. package/package.json +1 -1
@@ -0,0 +1,261 @@
1
+ "use client";
2
+
3
+ import { useMemo, useState } from "react";
4
+ import { Play, X } from "lucide-react";
5
+ import { RUN_INPUTS_KIND } from "@/lib/orchestration-run-inputs";
6
+
7
+ /**
8
+ * RunSetupPanel — sidecar surface for collecting manual run inputs before a
9
+ * workflow execution. Mounted by WorkflowSurface only when the active
10
+ * orchestration graph declares a `human-input` / form node with required
11
+ * fields. For workflows with no manual inputs, this panel never renders.
12
+ *
13
+ * Invariants:
14
+ * - UI-only validation. The server validates again before persisting.
15
+ * - No secret values are accepted. Fields typed `secretRef` collect only
16
+ * the ref slug. Secret-looking keys (api_key, token, password, etc.)
17
+ * are coerced to secretRef on the server.
18
+ * - The panel reuses .dm-workflow-panel-head, .dm-orchestration-config*,
19
+ * .dm-btn-outline, .dm-workflow-chip-btn. No new design system.
20
+ */
21
+ export function RunSetupPanel({ schema, running, onSubmit, onCancel }) {
22
+ const fields = Array.isArray(schema?.fields) ? schema.fields : [];
23
+ const [values, setValues] = useState(() => seedValues(fields));
24
+ const [touched, setTouched] = useState({});
25
+
26
+ const missing = useMemo(
27
+ () => fields.filter((f) => f.required && !hasValue(values[f.id], f)).map((f) => f.id),
28
+ [fields, values]
29
+ );
30
+
31
+ function patch(fieldId, raw) {
32
+ setValues((current) => ({ ...current, [fieldId]: raw }));
33
+ }
34
+
35
+ function markTouched(fieldId) {
36
+ setTouched((current) => ({ ...current, [fieldId]: true }));
37
+ }
38
+
39
+ function submit() {
40
+ if (missing.length > 0) {
41
+ const next = {};
42
+ for (const id of missing) next[id] = true;
43
+ setTouched((current) => ({ ...current, ...next }));
44
+ return;
45
+ }
46
+ const envelope = {
47
+ kind: RUN_INPUTS_KIND,
48
+ source: "manual",
49
+ values: Object.fromEntries(
50
+ fields
51
+ .map((field) => [field.id, normalizeForSubmission(field, values[field.id])])
52
+ .filter(([, value]) => value !== undefined)
53
+ ),
54
+ files: []
55
+ };
56
+ onSubmit?.(envelope);
57
+ }
58
+
59
+ if (!schema?.requiresInput || fields.length === 0) {
60
+ return (
61
+ <div className="dm-run-setup">
62
+ <p className="dm-run-setup__hint">This workflow has no manual inputs configured.</p>
63
+ <div className="dm-run-setup__actions">
64
+ <button type="button" className="dm-btn-outline" onClick={onCancel}>Close</button>
65
+ </div>
66
+ </div>
67
+ );
68
+ }
69
+
70
+ return (
71
+ <div className="dm-run-setup">
72
+ {schema.instructions ? (
73
+ <p className="dm-run-setup__instructions">{schema.instructions}</p>
74
+ ) : null}
75
+
76
+ <div className="dm-run-setup__fields">
77
+ {fields.map((field) => (
78
+ <RunSetupField
79
+ key={field.id}
80
+ field={field}
81
+ value={values[field.id]}
82
+ touched={Boolean(touched[field.id])}
83
+ onChange={(raw) => patch(field.id, raw)}
84
+ onBlur={() => markTouched(field.id)}
85
+ />
86
+ ))}
87
+ </div>
88
+
89
+ <div className="dm-run-setup__notice">
90
+ Inputs are validated server-side before the run. Secret-typed fields collect a ref slug only — never a raw value.
91
+ </div>
92
+
93
+ <div className="dm-run-setup__actions">
94
+ <button type="button" className="dm-btn-outline" onClick={onCancel} disabled={running}>
95
+ <X size={13} aria-hidden="true" /> Cancel
96
+ </button>
97
+ <button
98
+ type="button"
99
+ className="dm-workflow-chip-btn"
100
+ onClick={submit}
101
+ disabled={running || missing.length > 0}
102
+ title={missing.length > 0 ? `Fill required fields: ${missing.join(", ")}` : "Run workflow with these inputs"}
103
+ >
104
+ <Play size={13} aria-hidden="true" /> {running ? "Running" : "Run workflow"}
105
+ </button>
106
+ </div>
107
+ </div>
108
+ );
109
+ }
110
+
111
+ function RunSetupField({ field, value, touched, onChange, onBlur }) {
112
+ const showError = touched && field.required && !hasValue(value, field);
113
+ const inputId = `dm-run-setup-${field.id}`;
114
+ const help = field.helpText || (field.type === "secretRef" ? "Reference slug only — server resolves the secret." : "");
115
+
116
+ return (
117
+ <label className="dm-orchestration-config__field" htmlFor={inputId}>
118
+ <span>
119
+ {field.label}
120
+ {field.required ? <em aria-hidden="true" className="dm-run-setup__required"> *</em> : null}
121
+ </span>
122
+ {renderInput(field, value, onChange, onBlur, inputId)}
123
+ {help ? <small className="dm-run-setup__help">{help}</small> : null}
124
+ {showError ? <small className="dm-run-setup__error">Required.</small> : null}
125
+ </label>
126
+ );
127
+ }
128
+
129
+ function renderInput(field, value, onChange, onBlur, inputId) {
130
+ const safeValue = value == null ? "" : value;
131
+ switch (field.type) {
132
+ case "textarea":
133
+ case "json":
134
+ return (
135
+ <textarea
136
+ id={inputId}
137
+ rows={field.type === "json" ? 6 : 4}
138
+ value={typeof safeValue === "string" ? safeValue : JSON.stringify(safeValue, null, 2)}
139
+ onChange={(e) => onChange(e.target.value)}
140
+ onBlur={onBlur}
141
+ placeholder={field.type === "json" ? "{ }" : ""}
142
+ />
143
+ );
144
+ case "boolean":
145
+ case "checkbox":
146
+ return (
147
+ <span className="dm-run-setup__checkbox">
148
+ <input
149
+ id={inputId}
150
+ type="checkbox"
151
+ checked={Boolean(safeValue)}
152
+ onChange={(e) => onChange(e.target.checked)}
153
+ onBlur={onBlur}
154
+ />
155
+ <span>{field.helpText || "Enable"}</span>
156
+ </span>
157
+ );
158
+ case "number":
159
+ case "integer":
160
+ return (
161
+ <input
162
+ id={inputId}
163
+ type="number"
164
+ value={safeValue === "" ? "" : safeValue}
165
+ onChange={(e) => onChange(e.target.value)}
166
+ onBlur={onBlur}
167
+ />
168
+ );
169
+ case "email":
170
+ return (
171
+ <input
172
+ id={inputId}
173
+ type="email"
174
+ value={safeValue}
175
+ onChange={(e) => onChange(e.target.value)}
176
+ onBlur={onBlur}
177
+ placeholder="name@example.com"
178
+ />
179
+ );
180
+ case "url":
181
+ return (
182
+ <input
183
+ id={inputId}
184
+ type="url"
185
+ value={safeValue}
186
+ onChange={(e) => onChange(e.target.value)}
187
+ onBlur={onBlur}
188
+ placeholder="https://"
189
+ />
190
+ );
191
+ case "secretRef":
192
+ return (
193
+ <input
194
+ id={inputId}
195
+ type="text"
196
+ value={safeValue}
197
+ onChange={(e) => onChange(e.target.value)}
198
+ onBlur={onBlur}
199
+ placeholder="ENV_REF_SLUG"
200
+ autoComplete="off"
201
+ />
202
+ );
203
+ default:
204
+ return (
205
+ <input
206
+ id={inputId}
207
+ type="text"
208
+ value={safeValue}
209
+ onChange={(e) => onChange(e.target.value)}
210
+ onBlur={onBlur}
211
+ />
212
+ );
213
+ }
214
+ }
215
+
216
+ function seedValues(fields) {
217
+ const out = {};
218
+ for (const field of fields) {
219
+ if (field.type === "boolean" || field.type === "checkbox") out[field.id] = false;
220
+ else out[field.id] = "";
221
+ }
222
+ return out;
223
+ }
224
+
225
+ function hasValue(value, field) {
226
+ if (field?.type === "boolean" || field?.type === "checkbox") return value === true;
227
+ if (value == null) return false;
228
+ if (typeof value === "string") return value.trim().length > 0;
229
+ if (typeof value === "number") return Number.isFinite(value);
230
+ return Boolean(value);
231
+ }
232
+
233
+ function normalizeForSubmission(field, value) {
234
+ if (field.type === "boolean" || field.type === "checkbox") return Boolean(value);
235
+ if (field.type === "secretRef") {
236
+ const text = typeof value === "string" ? value.trim() : "";
237
+ return text ? { secretRef: text } : undefined;
238
+ }
239
+ if (field.type === "number" || field.type === "integer") {
240
+ if (value === "" || value == null) return undefined;
241
+ const num = Number(value);
242
+ return Number.isFinite(num) ? num : undefined;
243
+ }
244
+ if (field.type === "json") {
245
+ if (typeof value === "string") {
246
+ const trimmed = value.trim();
247
+ if (!trimmed) return undefined;
248
+ try {
249
+ return JSON.parse(trimmed);
250
+ } catch {
251
+ return trimmed;
252
+ }
253
+ }
254
+ return value;
255
+ }
256
+ if (typeof value === "string") {
257
+ const text = value.trim();
258
+ return text ? text : undefined;
259
+ }
260
+ return value;
261
+ }
@@ -31,9 +31,11 @@ import { findSandboxRowByWorkflowRef } from "@/lib/nav-workflows";
31
31
  import {
32
32
  addCanonicalNodeToGraph,
33
33
  buildBlankOrchestrationGraphShell,
34
+ buildDefaultAgentSwarmGraph,
34
35
  buildDefaultOrchestrationGraphFromRegistry,
35
36
  getNextCanonicalNodeId,
36
37
  getOrchestrationGraphUiState,
38
+ isAgentSwarmGraph,
37
39
  parseOrchestrationGraph,
38
40
  redactSecretsFromText,
39
41
  serializeOrchestrationGraph,
@@ -45,6 +47,19 @@ import { OrchestrationGraphCanvas } from "../data-model/components/Orchestration
45
47
  import { OrchestrationGraphEmptyCanvas } from "../data-model/components/OrchestrationGraphEmptyCanvas.jsx";
46
48
  import { OrchestrationNodeConfigPanel } from "../data-model/components/OrchestrationNodeConfigPanel.jsx";
47
49
  import { OrchestrationRunTracePanel } from "../data-model/components/OrchestrationRunTracePanel.jsx";
50
+ import { AgentSwarmPanel } from "../data-model/components/AgentSwarmPanel.jsx";
51
+ import { RunSetupPanel } from "./RunSetupPanel.jsx";
52
+ import { describeRunInputMetadataItems, discoverRunInputSchema } from "@/lib/orchestration-run-inputs";
53
+ import { selectWorkflowNodeInputSchema } from "@/lib/workspace-metadata-selectors";
54
+
55
+ // Workspace Metadata Graph V1 — read-only dependency metadata for workflow
56
+ // sidecars. The runtime path (sandbox-run, publish, draft/live) is
57
+ // unchanged; this only exposes typed dependency descriptors so the sidecar
58
+ // can render "this node requires N inputs from M source nodes".
59
+ const WORKFLOW_METADATA_SELECTORS = Object.freeze({
60
+ describeRunInputMetadataItems,
61
+ selectWorkflowNodeInputSchema
62
+ });
48
63
 
49
64
  function resolveRegistryRowForSandbox(workspaceConfig, sandboxRow) {
50
65
  const graph = parseOrchestrationGraph(sandboxRow?.orchestrationGraph);
@@ -328,6 +343,7 @@ export default function WorkflowSurface() {
328
343
  const [graphError, setGraphError] = useState("");
329
344
  const [orchestrationGraph, setOrchestrationGraph] = useState(null);
330
345
  const [dirty, setDirty] = useState(false);
346
+ const [runSetupOpen, setRunSetupOpen] = useState(false);
331
347
 
332
348
  const load = useCallback(async () => {
333
349
  setLoading(true);
@@ -379,9 +395,10 @@ export default function WorkflowSurface() {
379
395
  const graphUiState = getOrchestrationGraphUiState(orchestrationGraph);
380
396
  const graphUnset = graphUiState === "unset";
381
397
  const graphBlankShell = graphUiState === "blank-shell";
398
+ const swarmMode = useMemo(() => isAgentSwarmGraph(orchestrationGraph), [orchestrationGraph]);
382
399
  const nextNodeId = useMemo(
383
- () => (orchestrationGraph ? getNextCanonicalNodeId(orchestrationGraph) : "input"),
384
- [orchestrationGraph]
400
+ () => (orchestrationGraph && !swarmMode ? getNextCanonicalNodeId(orchestrationGraph) : null),
401
+ [orchestrationGraph, swarmMode]
385
402
  );
386
403
 
387
404
  const selectedNode = useMemo(() => {
@@ -446,6 +463,21 @@ export default function WorkflowSurface() {
446
463
  }
447
464
  }
448
465
 
466
+ async function patchSandboxRuntimeFields(fields) {
467
+ if (resolved.rowIndex < 0 || !objectId || !fields || typeof fields !== "object") return;
468
+ setSaving(true);
469
+ setSaveMessage("");
470
+ try {
471
+ const next = patchSandboxRowInConfig(workspaceConfig, objectId, resolved.rowIndex, fields);
472
+ await persistWorkspace(next);
473
+ setSaveMessage("Updated workflow runtime settings.");
474
+ } catch (err) {
475
+ setSaveMessage(err.message || "Runtime update failed");
476
+ } finally {
477
+ setSaving(false);
478
+ }
479
+ }
480
+
449
481
  async function publishGraph() {
450
482
  if (resolved.rowIndex < 0 || !objectId) return;
451
483
  const serialized = serializeCurrentGraph();
@@ -536,17 +568,27 @@ export default function WorkflowSurface() {
536
568
  }
537
569
  }
538
570
 
539
- async function runSandbox() {
571
+ const runInputSchema = useMemo(
572
+ () => discoverRunInputSchema(orchestrationGraph),
573
+ [orchestrationGraph]
574
+ );
575
+
576
+ async function runSandbox(options = {}) {
540
577
  if (!objectId || !rowId) return;
578
+ const runInputs = options && typeof options === "object" && options.runInputs && typeof options.runInputs === "object"
579
+ ? options.runInputs
580
+ : null;
541
581
  setRunning(true);
542
582
  setRunMessage("");
543
583
  try {
544
584
  const draft = await saveDraft({ orchestrationDraftStatus: "testing" });
545
585
  const draftGraph = draft?.serialized || serializeCurrentGraph();
586
+ const body = { objectId, name: rowId, useDraft: true, draftGraph };
587
+ if (runInputs) body.runInputs = runInputs;
546
588
  const res = await fetch("/api/workspace/sandbox-run", {
547
589
  method: "POST",
548
590
  headers: { "content-type": "application/json" },
549
- body: JSON.stringify({ objectId, name: rowId, useDraft: true, draftGraph }),
591
+ body: JSON.stringify(body),
550
592
  });
551
593
  const payload = await res.json();
552
594
  const responseText = redactSecretsFromText(JSON.stringify(payload.response ?? payload, null, 2));
@@ -586,6 +628,21 @@ export default function WorkflowSurface() {
586
628
  setSidecarMode("trace");
587
629
  }
588
630
 
631
+ function handleTestClick() {
632
+ if (runInputSchema.requiresInput) {
633
+ setRunSetupOpen(true);
634
+ setSelectedNodeId("");
635
+ setAddTarget(null);
636
+ return;
637
+ }
638
+ runSandbox();
639
+ }
640
+
641
+ async function handleRunWithInputs(runInputs) {
642
+ setRunSetupOpen(false);
643
+ await runSandbox({ runInputs });
644
+ }
645
+
589
646
  function openGraphMode() {
590
647
  const params = new URLSearchParams(searchParams.toString());
591
648
  params.delete("run");
@@ -606,6 +663,16 @@ export default function WorkflowSurface() {
606
663
  setDirty(true);
607
664
  }
608
665
 
666
+ function startAgentSwarm() {
667
+ const graph = buildDefaultAgentSwarmGraph({
668
+ agentHost: String(sandboxRow?.agentHost || "").trim()
669
+ });
670
+ setOrchestrationGraph(graph);
671
+ setSelectedNodeId("orchestrator");
672
+ setConfigTab("swarm");
673
+ setDirty(true);
674
+ }
675
+
609
676
  function applyPastedGraph(text) {
610
677
  const parsed = parseOrchestrationGraph(text);
611
678
  if (parsed) {
@@ -759,8 +826,8 @@ export default function WorkflowSurface() {
759
826
  </button>
760
827
  )}
761
828
  {canTest && (
762
- <button type="button" className="dm-workflow-chip-btn" disabled={running || saving || publishing} onClick={runSandbox}>
763
- <Play size={13} /> {running ? "Running" : "Test"}
829
+ <button type="button" className="dm-workflow-chip-btn" disabled={running || saving || publishing} onClick={handleTestClick}>
830
+ <Play size={13} /> {running ? "Running" : runInputSchema.requiresInput ? "Test with inputs" : "Test"}
764
831
  </button>
765
832
  )}
766
833
  {showPublish && (
@@ -816,9 +883,11 @@ export default function WorkflowSurface() {
816
883
  selectedRunId={runId}
817
884
  onBack={openGraphMode}
818
885
  onOpenGraph={openGraphMode}
886
+ onReplay={runSandbox}
887
+ running={running}
819
888
  />
820
889
  ) : (
821
- <div className={`dm-orchestration-sidecar dm-workflow-orchestration${selectedNode || addTarget ? " has-panel" : ""}`}>
890
+ <div className={`dm-orchestration-sidecar dm-workflow-orchestration${selectedNode || addTarget || runSetupOpen ? " has-panel" : ""}`}>
822
891
  <div className="dm-orchestration-sidecar__body">
823
892
  <div className="dm-orchestration-sidecar__canvas-col">
824
893
  {graphUnset ? (
@@ -826,6 +895,7 @@ export default function WorkflowSurface() {
826
895
  disabled={false}
827
896
  onStartFromRegistry={registryRow ? startFromRegistry : undefined}
828
897
  onStartBlank={startBlank}
898
+ onStartAgentSwarm={startAgentSwarm}
829
899
  onPasteGraph={applyPastedGraph}
830
900
  />
831
901
  ) : graphBlankShell ? (
@@ -855,7 +925,24 @@ export default function WorkflowSurface() {
855
925
  </>
856
926
  )}
857
927
  </div>
858
- {graphUiState === "populated" && addTarget && (
928
+ {graphUiState === "populated" && runSetupOpen && (
929
+ <div className="dm-orchestration-sidecar__config-col">
930
+ <div className="dm-workflow-panel-head">
931
+ <button type="button" className="dm-workflow-icon-btn" onClick={() => setRunSetupOpen(false)} aria-label="Close run setup panel">
932
+ <X size={14} />
933
+ </button>
934
+ <span>Run setup</span>
935
+ <em>Manual inputs</em>
936
+ </div>
937
+ <RunSetupPanel
938
+ schema={runInputSchema}
939
+ running={running}
940
+ onSubmit={handleRunWithInputs}
941
+ onCancel={() => setRunSetupOpen(false)}
942
+ />
943
+ </div>
944
+ )}
945
+ {graphUiState === "populated" && !runSetupOpen && addTarget && (
859
946
  <div className="dm-orchestration-sidecar__config-col">
860
947
  <div className="dm-workflow-panel-head">
861
948
  <button type="button" className="dm-workflow-icon-btn" onClick={() => setAddTarget(null)} aria-label="Close side panel">
@@ -870,7 +957,27 @@ export default function WorkflowSurface() {
870
957
  />
871
958
  </div>
872
959
  )}
873
- {graphUiState === "populated" && !addTarget && selectedNode && (
960
+ {graphUiState === "populated" && !runSetupOpen && !addTarget && selectedNode && swarmMode && selectedNode?.type === "thinAdapter" && (
961
+ <div className="dm-orchestration-sidecar__config-col">
962
+ <div className="dm-workflow-panel-head">
963
+ <button type="button" className="dm-workflow-icon-btn" onClick={() => setSelectedNodeId("")} aria-label="Close side panel">
964
+ <X size={14} />
965
+ </button>
966
+ <span>Agent swarm</span>
967
+ <em>agent-swarm-v1</em>
968
+ </div>
969
+ <AgentSwarmPanel
970
+ graph={orchestrationGraph}
971
+ disabled={false}
972
+ onGraphChange={(updater) => {
973
+ setOrchestrationGraph((g) => (typeof updater === "function" ? updater(g) : updater));
974
+ setDirty(true);
975
+ }}
976
+ />
977
+ {graphError && <p className="dm-orchestration-config__error">{graphError}</p>}
978
+ </div>
979
+ )}
980
+ {graphUiState === "populated" && !runSetupOpen && !addTarget && selectedNode && !(swarmMode && selectedNode?.type === "thinAdapter") && (
874
981
  <div className="dm-orchestration-sidecar__config-col">
875
982
  <div className="dm-workflow-panel-head">
876
983
  <button type="button" className="dm-workflow-icon-btn" onClick={() => setSelectedNodeId("")} aria-label="Close side panel">
@@ -884,6 +991,9 @@ export default function WorkflowSurface() {
884
991
  registryRow={registryRow}
885
992
  workspaceConfig={workspaceConfig}
886
993
  sandboxRow={sandboxRow}
994
+ objectId={objectId}
995
+ rowName={rowId}
996
+ onSandboxRowPatch={patchSandboxRuntimeFields}
887
997
  onDeleteNode={deleteSelectedNode}
888
998
  disabled={false}
889
999
  activeTab={configTab}