Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Agent

the agent task log (AgentTask)

partial entity AgentTask { security { … } }

Every piece of work an agent does is recorded as an `AgentTask` — which agent, what set it going, when it started and finished, and what came of it. Tasks nest: work an agent starts from inside another piece of work becomes a sub-task, so "what did this whole job involve" is one query however many agents it took. The rows are written by the platform and can never be created, edited or deleted by app code; your app decides who may READ them.

stable1 example compiled by CIagenttaskauditcapability

Summary#

You can see what an agent has said — that is the conversation. The task log is what it did.

Each piece of work an agent performs is one AgentTask row: whose work it was, what caused it, when it ran, and how it ended. Because agent work sets off more agent work — an agent hands part of a job to another agent, or files a record that starts a process which itself needs an agent — tasks form a tree. A task knows its parent and carries its full ancestry, so you can ask about one step or about everything that step led to, at any depth.

AgentTask arrives with using Osyrin.Agents;, alongside the rest of the agent surface.

Signature#

using Osyrin.Agents;

partial entity AgentTask {
  security { allow read when IsAuthenticated; }
}

The fields you will read:

fieldwhat it holds
Agentwhose work this was
Typewhat set it going — Workflow, Event, Schedule, Chat, or Spawn (another agent's task)
StatusRunning, Completed or Failed
Title · Triggerone line for a list; the full reason it ran
Parent · Children · Path · RootTaskthe tree — see below
StartedAt · CompletedAt · Outcomewhen, and how it ended
WorkflowRun · ChatSessionthe process or conversation it belongs to, when it belongs to one
EntityTypeName · EntityIdthe record it is about, when it is about one
Principal · OnBehalfOfwho it ran as, and whose behalf it ran on

Cost is not a field — see below.

Description#

The organising unit is the AGENT, not the process. The question the log answers is "what has this agent done" — a list of its recent work with its type, its timing and its outcome. A business process is one of the things that can cause agent work; plenty of processes never involve an agent at all, and those create nothing here.

Not every agent turn is work. A question the agent answers there and then — "what did we spend on travel in Q3?" — is a conversation, not a job, and it writes no task. A task exists when something set the agent to WORK: a process step, an event, a schedule, a request that turned into a piece of work, or another agent handing part of a job over. That is deliberate: a log that recorded every exchange would bury the work in chatter.

Tasks nest, across mechanisms. An agent's work can create a record that starts a process, whose step needs agent work, whose agent hands part of the job to another agent. Each of those is a sub-task of the one before it, and the chain is recorded the same way regardless of what made each hop. A sub-task means the work went to a different actor — another agent, or a person — not that the same agent moved on to its next step.

Path is what makes "and everything it led to" cheap. Every task carries its ancestry as a path ending in its own id, so one filter selects a task together with all its descendants, however deep. Reading Children gives you the immediate sub-tasks; matching on Path gives you the whole subtree.

Cost is not a field either, and that is the same decision. Every model call the task caused is recorded with the task's id and its path, so "what did this cost" is a sum over those calls, and "what did this cost all in" is a sum over every call whose path starts with this task's. Both are exactly the sum of the calls they summarise — a stored total can disagree with its own calls after a run that failed half-way, with nothing to reconcile it against.

The calls are LlmCallRecord rows, which your app can already query. AgentTask is the task each call belongs to, TaskPath is that task's ancestry, and the same aggregate over InputTokens/OutputTokens gives tokens instead of money.

It counts what the agent's TOOLS spent, not just its own turns. A tool that runs an extraction or a classification is doing model work the task caused, and it lands under the same task without anyone passing anything — because the attribution happens where every model call goes through, not where turns are counted.

Waiting is not recorded here. A task that is waiting for a person is waiting inside a process, which already records who it was assigned to, when it opened, when they picked it up and whether it ran late. The task points at that process rather than keeping a second copy of the same clock — two copies of one fact drift the day somebody fixes one of them.

The rows are written by the platform. App code can never create, change or delete an AgentTask: a fabricated row would attribute work to an agent that never did it, and a deleted one would hide work that happened. Attempting a write is refused.

Who may READ them is entirely yours. The platform ships no opinion about that, which means a plain using Osyrin.Agents; gives you a table nobody can read yet. Declare the rule you want, the same way you would for any other entity — everyone signed in, only managers, only the person the work was done for. Building the queue screen, the review list and the activity dashboard is your app's job too; the log is the material they are built from.

Examples#

An app that lets any signed-in person see the agent work log:

using Osyrin.Agents;

[Principal] entity User {
  [Required, MaxLength(255)] string Email;
  security { allow read when IsAuthenticated; }
}

partial entity AgentTask {
  security { allow read when IsAuthenticated; }
}

With that in place, "what has this agent been doing" is an ordinary query over an ordinary entity — and so is "show me everything that came out of this one job", by matching descendants on their Path.

See also#

Related

running an agent from your code

Call an agent you declared the way you would call anything else you declared — by name. You hand it the turns you want…

what an agent hands back (AgentDeliverable)

An agent presents its outcome as a LIST of deliverables, not a sentence: documents it wrote, files it produced, and…

what a task cost, and what it did (task.Calls)

Every model call an agent task paid for, read off the task itself — the model, the turn, the tokens, the cache hits…

agent conversation memory (using Osyrin.Agents)

A conversation is stored as a `ChatSession` plus its `ChatMessage` turns, so an agent asked a follow-up can refer back…