@growthub/cli 0.13.4 → 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.
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/metadata-graph/route.js +184 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +25 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +326 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +6 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +88 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +41 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/WorkspaceGraphInspectorPanel.jsx +226 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +16 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +49 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +14 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +923 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +14 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +216 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +28 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-inputs.js +43 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +3 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +36 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-chart-values.js +53 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-graph.js +646 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-selectors.js +249 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +1186 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +5 -0
- package/package.json +1 -1
|
@@ -215,6 +215,43 @@ async function copyToClipboard(text) {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
function formatRewardScore(value) {
|
|
219
|
+
const n = Number(value);
|
|
220
|
+
if (!Number.isFinite(n)) return "—";
|
|
221
|
+
return n.toFixed(2);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function SwarmSection({ swarm }) {
|
|
225
|
+
if (!swarm || typeof swarm !== "object") return null;
|
|
226
|
+
const tasks = Array.isArray(swarm.tasks) ? swarm.tasks : [];
|
|
227
|
+
if (tasks.length === 0 && !swarm.orchestrator?.plan && !swarm.synthesis?.answer) return null;
|
|
228
|
+
const completed = tasks.filter((t) => t?.status === "completed").length;
|
|
229
|
+
const score = swarm.reward ? formatRewardScore(swarm.reward.score) : "—";
|
|
230
|
+
const kind = swarm.reward?.kind || "structural-v1";
|
|
231
|
+
const synthesis = swarm.synthesis || null;
|
|
232
|
+
return (
|
|
233
|
+
<section className="dm-run-console__section">
|
|
234
|
+
<h3>Swarm</h3>
|
|
235
|
+
<p className="dm-swarm-summary__line">
|
|
236
|
+
<span><strong>{completed}/{tasks.length}</strong></span>
|
|
237
|
+
<span>score <strong>{score}</strong></span>
|
|
238
|
+
<span className="dm-swarm-summary__kind" title={swarm.reward?.note || ""}>{kind}</span>
|
|
239
|
+
</p>
|
|
240
|
+
{synthesis?.answer ? (
|
|
241
|
+
<details className="dm-swarm-phase" open>
|
|
242
|
+
<summary>
|
|
243
|
+
synthesizer
|
|
244
|
+
{synthesis.parsedOutcomeScore != null
|
|
245
|
+
? ` · ${Number(synthesis.parsedOutcomeScore).toFixed(2)}`
|
|
246
|
+
: ""}
|
|
247
|
+
</summary>
|
|
248
|
+
<pre>{synthesis.answer}</pre>
|
|
249
|
+
</details>
|
|
250
|
+
) : null}
|
|
251
|
+
</section>
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
218
255
|
function InputsSection({ payload }) {
|
|
219
256
|
const runInputs = payload?.runInputs;
|
|
220
257
|
const summary = payload?.inputSummary;
|
|
@@ -477,6 +514,9 @@ export function OrchestrationRunTracePanel({
|
|
|
477
514
|
const payload = activeConsoleRecord?.payload || {};
|
|
478
515
|
const output = activeConsoleRecord?.output || {};
|
|
479
516
|
const context = activeConsoleRecord?.context || {};
|
|
517
|
+
const swarmPayload = activeConsoleRecord?.swarm
|
|
518
|
+
|| (activeRawRecord && activeRawRecord.swarm)
|
|
519
|
+
|| null;
|
|
480
520
|
|
|
481
521
|
return (
|
|
482
522
|
<section className="dm-run-console" aria-label="Live runs console">
|
|
@@ -710,6 +750,7 @@ export function OrchestrationRunTracePanel({
|
|
|
710
750
|
<CodeBlock label="Command" body={payload.command} />
|
|
711
751
|
<CodeBlock label="Instructions" body={payload.instructions} />
|
|
712
752
|
</section>
|
|
753
|
+
<SwarmSection swarm={swarmPayload} />
|
|
713
754
|
<InputsSection payload={payload} />
|
|
714
755
|
</div>
|
|
715
756
|
)}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Growthub Workspace Metadata Graph V1 — read-only inspector panel.
|
|
5
|
+
*
|
|
6
|
+
* Surfaces a selected metadata node's dependencies + dependents + warnings.
|
|
7
|
+
*
|
|
8
|
+
* Mounting status (V1):
|
|
9
|
+
* This component SHIPS with the worker kit but is NOT yet mounted by the
|
|
10
|
+
* existing builder / workflow / data-model surfaces. Mounting is
|
|
11
|
+
* intentionally deferred to a follow-up PR so the V1 scope stays focused
|
|
12
|
+
* on the typed projection + GET route. Operators that want the inspector
|
|
13
|
+
* now can import it directly:
|
|
14
|
+
*
|
|
15
|
+
* import { WorkspaceGraphInspectorPanel } from "@/app/data-model/components/WorkspaceGraphInspectorPanel.jsx";
|
|
16
|
+
*
|
|
17
|
+
* Intended future entry points:
|
|
18
|
+
*
|
|
19
|
+
* - Widget sidecar: View dependencies
|
|
20
|
+
* - Workflow sidecar: View dependencies
|
|
21
|
+
* - Run console: View graph lineage
|
|
22
|
+
* - Data Model row sidecar: View dependents
|
|
23
|
+
* - Workspace Settings: Metadata Graph
|
|
24
|
+
*
|
|
25
|
+
* V1 invariants:
|
|
26
|
+
* - Read-only. No edits. No deletes.
|
|
27
|
+
* - No fetch of provider data — only `GET /api/workspace/metadata-graph`.
|
|
28
|
+
* - No secrets rendered.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { useEffect, useMemo, useState } from "react";
|
|
32
|
+
import { findDependencies, findDependents } from "@/lib/workspace-metadata-graph";
|
|
33
|
+
|
|
34
|
+
const RELATION_LABELS = {
|
|
35
|
+
containsWidget: "contains widget",
|
|
36
|
+
bindsToObject: "binds to object",
|
|
37
|
+
usesField: "uses field",
|
|
38
|
+
filteredByField: "filtered by field",
|
|
39
|
+
sortedByField: "sorted by field",
|
|
40
|
+
backedBySourceRecord: "backed by source record",
|
|
41
|
+
scopedToEntity: "scoped to entity",
|
|
42
|
+
containsNode: "contains node",
|
|
43
|
+
usesSandbox: "uses sandbox",
|
|
44
|
+
readsObject: "reads object",
|
|
45
|
+
writesObject: "writes object",
|
|
46
|
+
requiresRunInput: "requires run input",
|
|
47
|
+
callsIntegration: "calls integration",
|
|
48
|
+
usesAgentHost: "uses agent host",
|
|
49
|
+
executedWorkflow: "executed workflow",
|
|
50
|
+
executedSandbox: "executed sandbox",
|
|
51
|
+
usedAgentHost: "used agent host",
|
|
52
|
+
producedArtifact: "produced artifact",
|
|
53
|
+
belongsToIntegration: "belongs to integration",
|
|
54
|
+
boundToIntegration: "bound to integration"
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const NODE_TYPE_LABELS = {
|
|
58
|
+
dashboard: "Dashboard",
|
|
59
|
+
widget: "Widget",
|
|
60
|
+
dataModelObject: "Data Model object",
|
|
61
|
+
field: "Field",
|
|
62
|
+
view: "View",
|
|
63
|
+
workflow: "Workflow",
|
|
64
|
+
workflowNode: "Workflow node",
|
|
65
|
+
runInput: "Run input",
|
|
66
|
+
sandbox: "Sandbox",
|
|
67
|
+
agentHost: "Agent host",
|
|
68
|
+
integration: "Integration",
|
|
69
|
+
integrationEntity: "Integration entity",
|
|
70
|
+
sourceRecord: "Source record",
|
|
71
|
+
run: "Run",
|
|
72
|
+
outputArtifact: "Output artifact"
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function relationLabel(relation) {
|
|
76
|
+
return RELATION_LABELS[relation] || relation;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function nodeTypeLabel(type) {
|
|
80
|
+
return NODE_TYPE_LABELS[type] || type;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function WorkspaceGraphInspectorPanel({ selectedNodeId, onSelectNode }) {
|
|
84
|
+
const [graph, setGraph] = useState(null);
|
|
85
|
+
const [loading, setLoading] = useState(false);
|
|
86
|
+
const [error, setError] = useState(null);
|
|
87
|
+
const [internalSelectedId, setInternalSelectedId] = useState(selectedNodeId || "");
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
let canceled = false;
|
|
91
|
+
setLoading(true);
|
|
92
|
+
setError(null);
|
|
93
|
+
fetch("/api/workspace/metadata-graph")
|
|
94
|
+
.then((response) => {
|
|
95
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
96
|
+
return response.json();
|
|
97
|
+
})
|
|
98
|
+
.then((envelope) => {
|
|
99
|
+
if (canceled) return;
|
|
100
|
+
setGraph(envelope?.graph || { nodes: [], edges: [] });
|
|
101
|
+
})
|
|
102
|
+
.catch((err) => {
|
|
103
|
+
if (canceled) return;
|
|
104
|
+
setError(err?.message || "Failed to load metadata graph");
|
|
105
|
+
})
|
|
106
|
+
.finally(() => {
|
|
107
|
+
if (!canceled) setLoading(false);
|
|
108
|
+
});
|
|
109
|
+
return () => { canceled = true; };
|
|
110
|
+
}, []);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (selectedNodeId && selectedNodeId !== internalSelectedId) {
|
|
114
|
+
setInternalSelectedId(selectedNodeId);
|
|
115
|
+
}
|
|
116
|
+
}, [selectedNodeId, internalSelectedId]);
|
|
117
|
+
|
|
118
|
+
const nodes = graph?.nodes || [];
|
|
119
|
+
const selected = useMemo(
|
|
120
|
+
() => nodes.find((node) => node.id === internalSelectedId) || null,
|
|
121
|
+
[nodes, internalSelectedId]
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const dependencies = useMemo(
|
|
125
|
+
() => (graph && selected ? findDependencies(graph, selected.id) : []),
|
|
126
|
+
[graph, selected]
|
|
127
|
+
);
|
|
128
|
+
const dependents = useMemo(
|
|
129
|
+
() => (graph && selected ? findDependents(graph, selected.id) : []),
|
|
130
|
+
[graph, selected]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const handleSelect = (nodeId) => {
|
|
134
|
+
setInternalSelectedId(nodeId);
|
|
135
|
+
if (typeof onSelectNode === "function") onSelectNode(nodeId);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className="workspace-graph-inspector" aria-label="Workspace metadata graph inspector">
|
|
140
|
+
<header className="workspace-graph-inspector-header">
|
|
141
|
+
<h3>Workspace metadata graph</h3>
|
|
142
|
+
<p className="workspace-graph-inspector-subtitle">
|
|
143
|
+
Read-only projection of dashboards, widgets, workflows, sandboxes, and runs.
|
|
144
|
+
</p>
|
|
145
|
+
</header>
|
|
146
|
+
|
|
147
|
+
{loading && <p className="workspace-graph-inspector-loading">Loading metadata graph…</p>}
|
|
148
|
+
{error && (
|
|
149
|
+
<p className="workspace-graph-inspector-error" role="alert">
|
|
150
|
+
Could not load metadata graph: {error}
|
|
151
|
+
</p>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
<div className="workspace-graph-inspector-body">
|
|
155
|
+
<aside className="workspace-graph-inspector-list">
|
|
156
|
+
<h4>Nodes ({nodes.length})</h4>
|
|
157
|
+
<ul>
|
|
158
|
+
{nodes.map((node) => (
|
|
159
|
+
<li key={node.id}>
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
className={`workspace-graph-inspector-node-button${node.id === internalSelectedId ? " is-selected" : ""}`}
|
|
163
|
+
onClick={() => handleSelect(node.id)}
|
|
164
|
+
>
|
|
165
|
+
<span className="workspace-graph-inspector-node-type">{nodeTypeLabel(node.type)}</span>
|
|
166
|
+
<span className="workspace-graph-inspector-node-label">{node.label || node.id}</span>
|
|
167
|
+
</button>
|
|
168
|
+
</li>
|
|
169
|
+
))}
|
|
170
|
+
</ul>
|
|
171
|
+
</aside>
|
|
172
|
+
|
|
173
|
+
<section className="workspace-graph-inspector-detail">
|
|
174
|
+
{!selected && <p className="workspace-graph-inspector-empty">Select a node to view dependencies.</p>}
|
|
175
|
+
{selected && (
|
|
176
|
+
<>
|
|
177
|
+
<h4>{nodeTypeLabel(selected.type)} · {selected.label}</h4>
|
|
178
|
+
<dl className="workspace-graph-inspector-summary">
|
|
179
|
+
{Object.entries(selected.summary || {}).map(([key, value]) => (
|
|
180
|
+
<div key={key}>
|
|
181
|
+
<dt>{key}</dt>
|
|
182
|
+
<dd>{Array.isArray(value) ? value.join(", ") || "—" : (value === null || value === "" ? "—" : String(value))}</dd>
|
|
183
|
+
</div>
|
|
184
|
+
))}
|
|
185
|
+
</dl>
|
|
186
|
+
|
|
187
|
+
<section className="workspace-graph-inspector-edges">
|
|
188
|
+
<h5>Depends on ({dependencies.length})</h5>
|
|
189
|
+
<ul>
|
|
190
|
+
{dependencies.length === 0 && <li className="workspace-graph-inspector-empty">No dependencies.</li>}
|
|
191
|
+
{dependencies.map(({ node, relation }) => (
|
|
192
|
+
<li key={`dep::${node.id}::${relation}`}>
|
|
193
|
+
<button type="button" onClick={() => handleSelect(node.id)}>
|
|
194
|
+
<span className="workspace-graph-inspector-relation">{relationLabel(relation)}</span>
|
|
195
|
+
<span className="workspace-graph-inspector-node-type">{nodeTypeLabel(node.type)}</span>
|
|
196
|
+
<span className="workspace-graph-inspector-node-label">{node.label || node.id}</span>
|
|
197
|
+
</button>
|
|
198
|
+
</li>
|
|
199
|
+
))}
|
|
200
|
+
</ul>
|
|
201
|
+
</section>
|
|
202
|
+
|
|
203
|
+
<section className="workspace-graph-inspector-edges">
|
|
204
|
+
<h5>Used by ({dependents.length})</h5>
|
|
205
|
+
<ul>
|
|
206
|
+
{dependents.length === 0 && <li className="workspace-graph-inspector-empty">Nothing depends on this node yet.</li>}
|
|
207
|
+
{dependents.map(({ node, relation }) => (
|
|
208
|
+
<li key={`from::${node.id}::${relation}`}>
|
|
209
|
+
<button type="button" onClick={() => handleSelect(node.id)}>
|
|
210
|
+
<span className="workspace-graph-inspector-relation">{relationLabel(relation)}</span>
|
|
211
|
+
<span className="workspace-graph-inspector-node-type">{nodeTypeLabel(node.type)}</span>
|
|
212
|
+
<span className="workspace-graph-inspector-node-label">{node.label || node.id}</span>
|
|
213
|
+
</button>
|
|
214
|
+
</li>
|
|
215
|
+
))}
|
|
216
|
+
</ul>
|
|
217
|
+
</section>
|
|
218
|
+
</>
|
|
219
|
+
)}
|
|
220
|
+
</section>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export default WorkspaceGraphInspectorPanel;
|
package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css
CHANGED
|
@@ -8398,3 +8398,19 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
|
|
|
8398
8398
|
.dm-run-setup__actions { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
|
|
8399
8399
|
.dm-run-setup__actions .dm-btn-outline,
|
|
8400
8400
|
.dm-run-setup__actions .dm-workflow-chip-btn { display: inline-flex; align-items: center; gap: 6px; }
|
|
8401
|
+
|
|
8402
|
+
/* Agent swarm — sidecar editor (re-uses dm-orchestration-config tokens) */
|
|
8403
|
+
.dm-agent-swarm-panel__row { display: flex; align-items: center; gap: 6px; }
|
|
8404
|
+
.dm-agent-swarm-panel__row > span:first-child { flex: 1; font-size: 11px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
8405
|
+
.dm-agent-swarm-panel__row .dm-btn-outline { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; padding: 3px 7px; }
|
|
8406
|
+
.dm-agent-swarm-panel__subagent { border: 1px solid #edf0f3; border-radius: 4px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; }
|
|
8407
|
+
.dm-agent-swarm-panel__role { flex: 1; border: 1px solid #d1d5db; border-radius: 4px; padding: 4px 6px; font-size: 12px; color: #111827; background: #fff; }
|
|
8408
|
+
.dm-agent-swarm-panel__weights { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
|
|
8409
|
+
|
|
8410
|
+
/* Agent swarm — condensed run trace summary */
|
|
8411
|
+
.dm-swarm-summary__line { display: flex; flex-wrap: wrap; gap: 6px 12px; font-size: 11px; color: #6b7280; align-items: center; margin: 0 0 6px; }
|
|
8412
|
+
.dm-swarm-summary__line strong { color: #111827; font-weight: 600; }
|
|
8413
|
+
.dm-swarm-summary__kind { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; color: #6b7280; border: 1px solid #e5e7eb; border-radius: 999px; padding: 1px 6px; }
|
|
8414
|
+
.dm-swarm-phase { border: 1px solid #edf0f3; border-radius: 4px; padding: 6px 8px; font-size: 11px; }
|
|
8415
|
+
.dm-swarm-phase summary { cursor: pointer; color: #6b7280; font-weight: 500; }
|
|
8416
|
+
.dm-swarm-phase pre { margin: 6px 0 0; font-size: 11px; white-space: pre-wrap; color: #111827; max-height: 240px; overflow: auto; }
|
|
@@ -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,8 +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";
|
|
48
51
|
import { RunSetupPanel } from "./RunSetupPanel.jsx";
|
|
49
|
-
import { discoverRunInputSchema } from "@/lib/orchestration-run-inputs";
|
|
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
|
+
});
|
|
50
63
|
|
|
51
64
|
function resolveRegistryRowForSandbox(workspaceConfig, sandboxRow) {
|
|
52
65
|
const graph = parseOrchestrationGraph(sandboxRow?.orchestrationGraph);
|
|
@@ -382,9 +395,10 @@ export default function WorkflowSurface() {
|
|
|
382
395
|
const graphUiState = getOrchestrationGraphUiState(orchestrationGraph);
|
|
383
396
|
const graphUnset = graphUiState === "unset";
|
|
384
397
|
const graphBlankShell = graphUiState === "blank-shell";
|
|
398
|
+
const swarmMode = useMemo(() => isAgentSwarmGraph(orchestrationGraph), [orchestrationGraph]);
|
|
385
399
|
const nextNodeId = useMemo(
|
|
386
|
-
() => (orchestrationGraph ? getNextCanonicalNodeId(orchestrationGraph) :
|
|
387
|
-
[orchestrationGraph]
|
|
400
|
+
() => (orchestrationGraph && !swarmMode ? getNextCanonicalNodeId(orchestrationGraph) : null),
|
|
401
|
+
[orchestrationGraph, swarmMode]
|
|
388
402
|
);
|
|
389
403
|
|
|
390
404
|
const selectedNode = useMemo(() => {
|
|
@@ -649,6 +663,16 @@ export default function WorkflowSurface() {
|
|
|
649
663
|
setDirty(true);
|
|
650
664
|
}
|
|
651
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
|
+
|
|
652
676
|
function applyPastedGraph(text) {
|
|
653
677
|
const parsed = parseOrchestrationGraph(text);
|
|
654
678
|
if (parsed) {
|
|
@@ -871,6 +895,7 @@ export default function WorkflowSurface() {
|
|
|
871
895
|
disabled={false}
|
|
872
896
|
onStartFromRegistry={registryRow ? startFromRegistry : undefined}
|
|
873
897
|
onStartBlank={startBlank}
|
|
898
|
+
onStartAgentSwarm={startAgentSwarm}
|
|
874
899
|
onPasteGraph={applyPastedGraph}
|
|
875
900
|
/>
|
|
876
901
|
) : graphBlankShell ? (
|
|
@@ -932,7 +957,27 @@ export default function WorkflowSurface() {
|
|
|
932
957
|
/>
|
|
933
958
|
</div>
|
|
934
959
|
)}
|
|
935
|
-
{graphUiState === "populated" && !runSetupOpen && !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") && (
|
|
936
981
|
<div className="dm-orchestration-sidecar__config-col">
|
|
937
982
|
<div className="dm-workflow-panel-head">
|
|
938
983
|
<button type="button" className="dm-workflow-icon-btn" onClick={() => setSelectedNodeId("")} aria-label="Close side panel">
|
|
@@ -81,11 +81,24 @@ import { governedWorkspaceIntegrationCatalog } from "@/lib/domain/integrations";
|
|
|
81
81
|
import { OBJECT_TYPE_PRESETS, listWorkspaceDataModelTables } from "@/lib/workspace-data-model";
|
|
82
82
|
import {
|
|
83
83
|
computeChartProjectionDebug,
|
|
84
|
-
computeChartValuesFromRows
|
|
84
|
+
computeChartValuesFromRows,
|
|
85
|
+
deriveWidgetDependencyContract
|
|
85
86
|
} from "@/lib/workspace-chart-values";
|
|
87
|
+
import { selectObjectFilterableFields, selectObjectSortableFields } from "@/lib/workspace-metadata-selectors";
|
|
86
88
|
import { HelperSidecar } from "./data-model/components/HelperSidecar.jsx";
|
|
87
89
|
import { WorkspaceRail } from "./workspace-rail.jsx";
|
|
88
90
|
|
|
91
|
+
// Workspace Metadata Graph V1 — typed dependency contracts.
|
|
92
|
+
// Used by sidecar dependency summaries; the existing chart hydration path
|
|
93
|
+
// continues to compute values via `computeChartValuesFromRows`. These
|
|
94
|
+
// selectors only describe the widget's typed contract — they never mutate
|
|
95
|
+
// config or trigger network calls.
|
|
96
|
+
const WORKSPACE_METADATA_SELECTORS = Object.freeze({
|
|
97
|
+
deriveWidgetDependencyContract,
|
|
98
|
+
selectObjectFilterableFields,
|
|
99
|
+
selectObjectSortableFields
|
|
100
|
+
});
|
|
101
|
+
|
|
89
102
|
const DEFAULT_CHART_TYPE = "bar-vertical";
|
|
90
103
|
const DEFAULT_FILTER_OP = "and";
|
|
91
104
|
const DEFAULT_FILTER_OPERATOR = "contains";
|