Octopus
Agent as Approval Actor
An Octopus agent can act as an automated Human-In-the-Loop (HIL) actor. Instead of a human reviewing a task, the agent reasons about it and submits an approve, reject, or escalate decision — all within the Flow Studio HIL suspension-resume mechanism.
HIL Actor Pattern
1
Workflow reaches a HIL node assigned to "OctopusAgent"
Flow Studio suspends the workflow and puts the HIL task in the actor queue for "OctopusAgent".
2
IOctopusHILActor polls the ProcessServer task queue
Every
QueuePollMs milliseconds, the HIL actor polls for new tasks assigned to the configured actor name.
3
Task dispatched to the Octopus agent
The HIL task data is serialised as a prompt and sent to the
HILActor.AgentId agent as a chat turn.
4
Agent reasons and calls submit_hil_decision tool
The agent evaluates the task data, calls its own tools as needed, then calls the
submit_hil_decision MCP tool.
5
ProcessServer resumes the workflow with the decision
The workflow continues from the HIL node with the agent's decision as the HIL actor response.
IOctopusHILActor Implementation
public class OctopusHILActor : IOctopusHILActor
{
private readonly IProcessServerClient _processServer;
private readonly IOctopusAgentEngine _agentEngine;
private readonly ProcessPluginConfig _config;
private CancellationTokenSource? _cts;
public async Task StartPollingAsync(CancellationToken ct)
{
_cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
_ = Task.Run(async () =>
{
while (!_cts.Token.IsCancellationRequested)
{
try
{
var tasks = await _processServer.GetPendingHILTasksAsync(
actorName: "OctopusAgent",
limit: 10,
_cts.Token);
foreach (var task in tasks)
_ = ProcessHILTaskAsync(task, _cts.Token);
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
// Log and continue polling
}
await Task.Delay(_config.HILActor.QueuePollMs, _cts.Token);
}
}, _cts.Token);
}
private async Task ProcessHILTaskAsync(HILTask task, CancellationToken ct)
{
// Build a prompt describing the HIL task
var prompt = $"""
You are reviewing a HIL approval task.
Task Type: {task.TaskType}
Description: {task.Description}
Data: {JsonSerializer.Serialize(task.Data, JsonOptions.Indented)}
Review the task and call submit_hil_decision with:
- decision: "Approve", "Reject", or "Escalate"
- reason: your explanation
- form_data: any required fields (if this is a form task)
""";
await _agentEngine.ChatAsync(new AgentChatRequest
{
AgentId = _config.HILActor.AgentId,
SessionId = $"hil-{task.TaskId}",
Message = prompt,
TenantId = task.TenantId
}, ct);
}
}
submit_hil_decision Tool Schema
{
"name": "submit_hil_decision",
"description": "Submit a decision for a Human-In-the-Loop approval task. " +
"Call this after reviewing the task data provided in the system context.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": { "type": "string", "description": "The HIL task ID to respond to" },
"decision": { "type": "string", "enum": ["Approve", "Reject", "Escalate"] },
"reason": { "type": "string", "description": "Explanation of the decision" },
"form_data": { "type": "object", "description": "Form field responses if required" }
},
"required": ["task_id", "decision", "reason"]
}
}
Automated decisions carry full responsibility. Any decision the agent makes has the same effect as a human decision. Ensure the agent's system prompt, tools, and data access are appropriate for the approval type before enabling automated HIL.