SophonaPluginTemplate 1.0.2

dotnet add package SophonaPluginTemplate --version 1.0.2
                    
NuGet\Install-Package SophonaPluginTemplate -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SophonaPluginTemplate" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SophonaPluginTemplate" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="SophonaPluginTemplate" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SophonaPluginTemplate --version 1.0.2
                    
#r "nuget: SophonaPluginTemplate, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SophonaPluginTemplate@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SophonaPluginTemplate&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=SophonaPluginTemplate&version=1.0.2
                    
Install as a Cake Tool

SophonaPluginTemplate

Nodes for Sophona Studio (RobotForce), built against RobotForce.Sophona.PluginConnector 1.0.18.

Node Category What it does
Hello World System Returns a greeting - the smallest complete node, copy it to start a new one. simulateError = Yes produces an error on purpose to try the throwOnError field.
HTTP GET (text) Apps & integrations Downloads a page or an API response as text; shows every kind of failure a workflow (and the agent running it) must understand and the throwOnError field (stop the workflow or keep going).

Build, test, install

dotnet build -c Release        # also creates bin\Release\SophonaPluginTemplate.1.0.0.nupkg
dotnet test                    # tests\SophonaPluginTemplate.Tests

In Sophona Studio click the package icon in the status barPackage Manager → drop the .nupkg (or Upload .nupkg). The nodes appear without a restart. To publish a new build, raise <Version> first.

What the Package Manager checks

Rule Why Code
Exactly one class named Commands, derived from RobotConnector, with public Commands(object init) RobotExecutor finds the class by name and creates it through that constructor PKG034, PKG035
Connector referenced as Version="[1.0.18]" RobotExecutor loads the Studio's connector, not the plugin's PKG003, PKG004
<RootNamespace> not used by another plugin two plugins with one namespace hide each other's nodes PKG005
<AssemblyName> equal to <PackageId> the library is named after the package (robot_modules\<id>.robotforce) PKG015
every [RobotInstruction] method has a static tile class with the same name and a frontendHtml field without the tile the node is not shown in the Actions panel PKG022, PKG023
node definitions load without exceptions a throwing tile hides only that node, the rest still installs PKG012

Errors block the installation and say what to change; warnings are shown and the package installs.

Adding a node

  1. Copy SophonaHelloWorld.cs, rename the tile class and the method to the same name.
  2. description of the tile and tooltip of every field are for the person who builds the workflow - they are shown in the Actions panel and in the node settings: what the node does, what it returns, allowed values, format, default. The agent never sees them (see below).
  3. Keep the [RobotInstruction] method thin - it reads its fields and calls Run(...). Every decision goes into an internal static XxxCore(string …) method returning ToolResult, which the tests call directly:
[RobotInstruction]
public Task<object> CountWords(/* fields… */ ScriptVariable text, ScriptVariable outputVariable)
{
    return Run(nameof(CountWords), outputVariable, () => CountWordsCore(ToolKit.Text(text)));
}

internal static ToolResult CountWordsCore(string text)
{
    if (string.IsNullOrWhiteSpace(text))
        return ToolResult.Error("text is empty.", "Pass the text to count, e.g. the output of ReadFile.");

    var words = text.Split((char[])null, StringSplitOptions.RemoveEmptyEntries).Length;
    return ToolResult.Ok(words.ToString());
}
  1. Nodes run synchronously - RobotExecutor does not await the returned Task. Asynchronous APIs are called through Task.Run(...).GetAwaiter().GetResult() (see HttpGetText.cs).
  2. Once a node is used in workflows, do not change its fields (add, remove, reorder). RobotExecutor passes the saved values by position - such workflows fail with Parameter count mismatch until the node is dropped on them again. Need another field? Add a new node.

Results a workflow and an agent understand

Who sees what:

Who Sees
the person who builds the workflow node title, description, field tooltips, the robot console
an agent (LLM) not the nodes - it runs a whole skill as a tool, chosen by the skill's description and filled through its input variables; it gets back the workflow variables and, when a step failed, the error message of the run (Execution failed at step …: <exception message of the node>)

So a node reaches the agent only through the text it writes into its output variable (when that variable is, or feeds, what the skill returns) and through its exception message. ToolKit.cs implements the contract for both:

Situation Return Output variable Step
success ToolResult.Ok(text) the result; large or partial results start with a one-line header succeeds
nothing found ToolResult.Empty("what was searched, where") EMPTY: … succeeds
wrong input, failure ToolResult.Error("what happened, naming the value.", "what to do next.") ERROR: … fails (ToolException)
the same, node field throwOnError = No (the same ToolResult.Error) ERROR: … succeeds - the workflow goes on
a bug nobody foresaw (any exception) ERROR: unexpected failure: … with the exception chain fails, original exception attached

Write messages for someone who cannot see the screen:

Instead of Write
Operation failed ERROR: C:\data\report.xlsx does not exist. Use ListDirectory to find the file - do not guess paths.
Invalid mode ERROR: mode: unknown value "Markdown". Allowed values: Text \| Raw. Call the node again with one of them.
(20 000 characters without comment) …text… + [NOTE: showing 20000 of 81532 characters - raise maxChars or fetch a more specific page]
ERROR: no results EMPTY: no file in C:\src matches *.cs

Say when not to retry (401, 404, invalid certificate) - otherwise an agent that runs the skill will try variations of the same call.

Stop the workflow or keep going (throwOnError)

Some workflows must stop at the first error; others handle errors themselves (a loop over 500 addresses should not die on the first 404). HelloWorld and HttpGetText have the field that lets the workflow author choose - the same idea as throwOnNonZero of RunPowerShell in SystemActions. Put it right before the output variable:

[RobotFieldConfig(
    fieldType = typeof(string),
    isDropdown = true,
    dropdownArray = new string[] { "Yes", "No" },
    defaultValue = "Yes",
    tooltip = "Allowed values: Yes | No. Yes = an error stops the workflow. " +
              "No = the workflow continues and the output variable starts with \"ERROR:\". Optional, default Yes."
)]
ScriptVariable throwOnError,

// …

return Run(nameof(MyNode), outputVariable, () => MyNodeCore(ToolKit.Text(input)),
    stopWorkflowOnError: ToolKit.StopOnError(ToolKit.Text(throwOnError)));
throwOnError Error described by the node (ToolResult.Error, ToolException) Output variable Robot console
Yes (default, also empty or unknown) the step fails, the workflow stops ERROR: … Execution failed at step …: HelloWorld: …
No the step succeeds, the workflow goes on ERROR: … - the next step checks it, e.g. result.StartsWith("ERROR:") ⚠️ HelloWorld: … → throwOnError = No: the workflow goes on, …

Two things stop the workflow even with No, because the error would otherwise be lost: an unexpected exception (a bug) and a node without an output variable. A value typed into the output field instead of a picked variable counts as no variable - RobotExecutor keeps it in an internal temp_… variable. Without an output variable throwOnError makes no difference: an error stops the workflow, no error leaves it green.

Try it on a workflow

Hello World has a simulateError field (No | Yes, default No) just for this - Studio fills an empty name with the default World, so clearing the name does not produce an error.

  1. Install the plugin and drop Hello World on a workflow (a node added before this field existed must be added again). In the Variables panel create a string variable result and pick it as the output variable.
  2. Set simulateError = Yes, throwOnError = Yes and run: the run stops at Hello World with HelloWorld: simulated error (simulateError = Yes) ….
  3. Switch to throwOnError = No and run again: the step is green, the robot console shows the ⚠️ line and result holds ERROR: simulated error ….
  4. Add an If after Hello World with the condition result.StartsWith("ERROR:") - one branch handles the error, the other one uses the greeting. Set simulateError = No to see the second branch.
  5. Clear the output variable: with simulateError = Yes the run stops for both values of throwOnError.

Dependencies

Add other NuGet packages with a normal <PackageReference>; the Package Manager installs them together with the plugin and checks version conflicts with other plugins. Packages Sophona Studio already contains (the .NET runtime, the connector and its dependencies) are not copied - use the versions the Studio has or older ones.

Publishing on nuget.org

dotnet nuget push bin\Release\SophonaPluginTemplate.1.0.0.nupkg -k <api key> -s https://api.nuget.org/v3/index.json

The Browse tab lists packages owned by the nuget.org account set in packageManager:pluginOwner (default RobotForce). Keep the sophona-plugin tag. A version on nuget.org cannot be replaced - publish a new one.

A new plugin from this template

dotnet new install RobotForce.Sophona.Plugin.Template      # or: dotnet new install <path to this folder>
dotnet new sophona-plugin -n Acme.InvoiceNodes             # --IncludeTests false = without the test project

The name becomes the project, the namespace, the package id and the library name.

Product Compatible and additional computed target framework versions.
.NET net8.0-windows7.0 is compatible.  net9.0-windows was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 54 9/17/2026
1.0.1 49 9/17/2026