Summary#
var interrupted = task.Stop();Marks the task and everything under it as Cancelled, and trips the model call in flight so the agent stops
now rather than after the answer it was already paying for.
Signature#
task.Stop() // → bool — true when a LIVE run was interruptedThe return value distinguishes two real outcomes: stopped (a run was executing and has been cut off) and will stop (nothing was running yet, and the request is remembered so the next run ends immediately). A stop button that conflates them tells the user "done" when the work is still going.
Description#
It stops the work, not just the record#
A task can be sitting inside a provider call that takes tens of seconds. Stop() cancels it: the call is abandoned,
the run unwinds, and nothing further is charged for that turn. The task's row is marked in the same operation, so a
watcher ends and every list stops showing the task as running.
⚑ Both halves matter. Marking the row alone leaves the agent talking to the model; cancelling alone leaves every screen claiming the work is still in progress.
Does it stop child tasks too?#
A task driven by a loop is the parent; the run doing the work is a child. Stop() therefore ends the
task and everything beneath it — stopping only the task you were handed would leave the agent running
underneath it.
⚠ Tasks that have already finished are left alone. A child that completed a second before the stop did complete, and its record of that is worth more than a uniform status across the tree.
Cancelled is not Failed#
A stopped task ends as Cancelled. That is deliberately distinct from Failed: a failure is the work going wrong
and worth investigating, a stop is somebody deciding it should not continue. If they shared a state, every operator
abort would look like a defect on the one screen you scan for defects.
Who can stop work#
Stopping is an update. Declare who may update the task and you have declared who may stop it:
entity ReviewTask : AgentTask {
security {
allow read when IsAuthenticated; // everyone signed in can WATCH
allow update when IsApprover; // …only an approver can STOP
}
}⚠ Read and update are separate on purpose. Watching work and ending it are different privileges, and an app that
lets everyone see a task usually does not mean everyone may kill it. A caller without update is refused with a
message naming the rule they need.
What a watcher sees#
A stop appends a final step, so a Watch() feed ends with a line saying what happened rather
than simply going quiet. A stream that stops producing is otherwise indistinguishable from one that finished
normally.
Examples#
using Osyrin.Agents;
[Principal] entity User {
[Required, MaxLength(255)] string Email;
security { allow read when IsAuthenticated; }
}
entity ReviewTask : AgentTask {
security {
allow read when IsAuthenticated;
allow update when IsAuthenticated;
}
}
/// Ends the work and says whether anything was actually interrupted.
string StopReview(Guid taskId) {
var task = ReviewTask.Where(t => t.Id == taskId).FirstOrDefault();
if (task == null) { return "no such task"; }
return task.Stop() ? "stopped" : "nothing was running — it will not start";
}See also#
- watching a task run (task.Watch) — following work while it runs, and seeing it end
- the agent task log (AgentTask) — the task itself: what caused it, when it ran, how it finished
- the agent loop (app.Agent, Loop) — why a task and the run beneath it are two rows