Summary#
A task's Outcome is one sentence — enough for a list view, and never enough to act on. The deliverables are what
the sentence is about.
Each thing an agent hands back is one AgentDeliverable row hanging off its task: a document it wrote, a file
it produced, or a record it created. A task has as many as it needs, in the order the agent chose, and the three
shapes sit in one list — so a screen can show a summary, the memo behind it and the twelve receipts that were filed,
together.
AgentDeliverable arrives with using Osyrin.Agents;, alongside the agent task log (AgentTask).
Signature#
using Osyrin.Agents;
partial entity AgentDeliverable {
security { allow read when IsAuthenticated; }
}The fields you will read:
| field | what it holds |
|---|---|
Task | the work this is an outcome of |
Kind | Document, File or Record — which of the three below carries the content |
Sequence | the agent's own ordering, from 0 |
Label | what this IS, in a few words — the line a reviewer scans, and what groups related rows |
Document | Kind = Document: the markdown the agent wrote |
FileAsset | Kind = File: the file it produced |
EntityTypeName · EntityId | Kind = Record: the record it created |
And from the other side, on the task itself:
| field | what it holds |
|---|---|
AgentTask.Deliverables | every outcome of that task, as a collection |
Description#
A list, because a sentence cannot carry an answer. "Checked the report and filed one receipt" tells a reviewer nothing they can open. The deliverables are the openable half: the policy check they can read, the memo they can download, the draft they can approve.
One row per thing, and grouping is yours. Twelve filed receipts are twelve rows sharing a Label, not one row
that mentions twelve. A list can always be grouped for display; a group cannot be ungrouped, and the individual rows
are what a reviewer clicks.
The agent records these DELIBERATELY, and that is what makes the list worth reading. The platform separately knows everything a task touched — that is the audit trail, and it is complete. This list is different: it holds only what the agent chose to present. Most rows an agent touches on the way to an answer are working material, and a list derived from them would bury the answer in bookkeeping.
⚑ So an agent that does the work and presents nothing leaves an empty list, and that is a finding worth showing rather than an error. It means the work happened and nobody can see what came of it. The platform will not refuse to finish such a task — an agent whose honest answer is "nothing to report" has to be able to say so.
A document is markdown, and that buys more than formatting. It is stored as a real document with sections, so it can be read section by section, edited afterwards, and — because it is indexed for recall — found later by what it SAYS. "What did we decide about the Lisbon policy" can find the deliverable that decided it, months on. A plain string would be a dead end in exactly the place it is most useful.
A deliverable never points at nothing. Recording a record or a file that does not exist is refused at the moment the agent tries, while the agent is still working and can fix it — rather than becoming a card on a review screen that looks live and opens nothing.
The rows are written by the platform. App code can never create, change or delete an AgentDeliverable: a
fabricated one would claim an agent produced something it did not, and a deleted one would hide what it did produce.
Attempting a write is refused.
Who may READ them is entirely yours, exactly as for the task log — a plain using Osyrin.Agents; gives you a
table nobody can read yet, and you declare the rule you want. The review screen, the approval queue and the activity
dashboard are your app's to build; these rows are the material.
Examples#
An app that lets any signed-in person read agent work and everything it produced:
using Osyrin.Agents;
[Principal] entity User {
[Required, MaxLength(255)] string Email;
security { allow read when IsAuthenticated; }
}
partial entity AgentTask {
security { allow read when IsAuthenticated; }
}
partial entity AgentDeliverable {
security { allow read when IsAuthenticated; }
}With both rules in place, a task's outcomes are an ordinary collection on an ordinary entity: read
task.Deliverables, order by Sequence, and render each row by its Kind.
See also#
- the agent task log (AgentTask) — the work itself: what caused it, when it ran, what it cost.
- agent conversation memory (using Osyrin.Agents) — what the agent SAID, as against what it handed over.
- running an agent from your code — running an agent from your own code.