Documentation menu

Event reference

Generated reference of turn events, session journal kinds, begin and error codes, and feed frames.

Generated by bun run docs:generate (scripts/generate-event-reference.ts) from the event and journal schemas; the full JSON schemas are in docs/event-reference.json. Writers, sensitivity, HTTP statuses and error-code meanings are kept by hand in that script, which fails when they no longer match the code.

There are two kinds of events. The bus carries a turn’s typed events (TurnEvent) live, in process, to the turn’s subscribers. The journal is the session’s durable log: the turn’s writer subscribes to the bus first and stores most bus events, memory stores its entries, and the sessions plugin stores the turn’s start, end and the inbox. Another request or process reads the journal with Sessions.read; only the bus carries token deltas. See the events guide for how to subscribe to them, stream a turn to a client and follow a session.

Bus events

_tag is the event’s name. Subscribe with turn.events.subscribe, onEvent, onTool or a host event’s on.

EventTransientStored as
turn.startednoRecorded elsewhere: the session’s own turn.started, written when the turn began
step.startednostep.started
tool.startednotool.started
tool.refusednotool.refused, without input
tool.progressyesNot stored (transient)
tool.completednotool.completed, without input, result, encoded
step.endednostep.ended
completion.evaluatednocompletion.evaluated
skills.activatednoRecorded elsewhere: memory’s memory.skills
context.builtnocontext.built
decision.recordednodecision.recorded
assistant.messagenostep.usage, without text, reasoning, toolCalls
assistant.deltayesNot stored (transient)
stream.fallbacknostream.fallback
turn.endednoRecorded elsewhere: memory’s turn.reply and the session’s own turn.ended
hostnoUnder its name

turn.started

Published once per turn when memory takes the user’s message as turn turn (TurnMemory.persistMessage; Agent.turn runs it before the host’s use). runId names the run, userMessage is what the user said (as the session stored it: its images, inline as base64, normalised), meta is the host’s metadata the turn was begun with ({} when none was given; never shown to the model), and resumes, present only on a turn that resumes an earlier one, is that turn’s number. Not journaled from the bus: the session stored its own turn.started when the turn began, with meta and resumes only when they were given. A subscription made in use never sees it; one in the host’s per-turn layer or a session plugin does.

FieldType
runIdstring
turninteger
userMessage{ text, attachments }
metaobject
resumesinteger (optional)

step.started

Published by the step loop as each step starts. planned is true for step 0 of a host-planned batch, which makes no provider call; activeTools are the tool names the step offers the model. Journaled as step.started.

FieldType
stepinteger
plannedboolean
activeToolsArray<string>

tool.started

Published by the tool registry just before a tool’s handler runs. invocationId (<runId>:call:<n>) pairs it with its tool.progress and tool.completed; input is the decoded parameters; labels and stage come from the tool’s annotations. A refused call (inactive, forbidden, denied, over a limit) publishes tool.refused instead, and no other tool.* event; step.ended.results lists every recorded result, refused calls included. Journaled as tool.started, input included.

FieldType
stepinteger
invocationIdstring
toolstring
inputunknown
labelsRecord<string, string>
stagestring | null

tool.refused

Published by the tool registry when it refuses a call before the handler runs, in place of tool.started: the tool is not active (inactive: not loaded, or not available in this conversation), the turn lacks a permission it needs (forbidden), the host’s ActionPolicy denied it (denied), the turn’s call limit is reached (call-limit) or the tool’s own (tool-call-limit). invocationId is minted for the call as for any other (the refused call takes its <runId>:call:<n>); message is what the model receives as the call’s failed result, which step.ended.results lists as not ok. The model is offered only active tools, so inactive comes from a call made through the handlers directly (a host’s, another tool’s); forbidden also from a tool an earlier turn activated whose permission this turn lacks. Journaled as tool.refused without input.

FieldType
stepinteger
invocationIdstring
toolstring
inputunknown
reason"inactive" | "forbidden" | "denied" | "call-limit" | "tool-call-limit"
messagestring

tool.progress

Published by publishToolProgress from inside a tool handler, between that call’s tool.started and tool.completed (same step, invocationId and tool). channel is stdout or stderr for a command’s output as it arrives, status for anything else the tool reports; text is that output or message. Transient: never journaled; tool.completed carries the result.

FieldType
stepinteger
invocationIdstring
toolstring
channel"stdout" | "stderr" | "status"
textstring

tool.completed

Published by the tool registry when a handler returned or failed (ok). result is the decoded value, encoded its wire form as the model receives it. durationMs runs from just after tool.started to the handler’s exit, so it includes any wait for the tool’s read or write lane. Journaled as tool.completed without input, result and encoded: memory keeps the call and its result (memory.message, memory.tool-result).

FieldType
stepinteger
invocationIdstring
toolstring
inputunknown
okboolean
resultunknown
encodedunknown
durationMsnumber
labelsRecord<string, string>
stagestring | null

step.ended

Published by the step loop after the step’s tail is recorded. status is completed, failed or cancelled; results are the step’s recorded tool results (memory entry, tool call id, tool, ok), in log order, refused calls included. Journaled as step.ended.

FieldType
stepinteger
status"completed" | "failed" | "cancelled"
resultsArray<{ entry, toolCallId, tool, ok }>

completion.evaluated

Published by the step loop after step.ended, with the verdict of the host’s completion policy for that step. A verdict that awaits tasks is evaluated again once they finish, publishing a second event. Journaled as completion.evaluated.

FieldType
stepinteger
verdict{ complete, awaiting, facts }

skills.activated

Published by the tool registry when skills (and their tools) become active: always-on skills, the intent matcher, the model’s load_skill, or the host. Not journaled from the bus: memory records the activation as memory.skills.

FieldType
skillsArray<string>
toolsArray<string>
source"always" | "matcher" | "load_skill" | "host"

context.built

Published before each provider call, once the step’s context is built (none for a planned step 0). It carries the memory strategy and its version, the context and system-prompt fingerprints, the token estimate of the messages, the tokens reserved for the system prompt and tool schemas, the run’s budgetTokens (the whole request’s; memory compacted the messages toward budgetTokens - reservedTokens), how many compactions the build applied, and the active tools. Journaled as context.built (records written before 0.14.0 have no budgetTokens); the full request is journaled beside it as request.prepared.

FieldType
stepinteger
turninteger
strategystring
strategyVersionstring
fingerprintstring
systemFingerprintstring
estimatedTokensinteger
reservedTokensinteger
budgetTokensinteger
compactionsinteger
activeToolsArray<string>

decision.recorded

Published by recordDecision, for example when turn.tools.select or apply records the skill selection: the decision record holds the candidates, the policy version and what was chosen. Journaled as decision.recorded.

FieldType
record{ version, id, family, contextHash, candidateHash, policyVersion, candidates, attempts, selection, validation, fallback, applied, probabilities }

assistant.message

Published by the step loop once per provider response, after that step’s tools ran and its tail is recorded: the text, the reasoning, the model, the tool calls and the token usage (input with its cache reads and writes inside, output with its reasoning inside). A host-planned step 0 carries zero usage. Journaled only as step.usage (step, model, usage): the content itself is memory’s memory.message.

FieldType
stepinteger
textstring
reasoningstring
modelstring | null
toolCallsArray<{ id, tool, input }>
usage{ inputTokens, outputTokens, totalTokens, cacheReadTokens, cacheWriteTokens, reasoningTokens }

assistant.delta

Published by the step loop for each streamed increment while limits.streaming is on (the default), on channel text, reasoning or tool-params; id groups the increments of one content part. Transient: never journaled, and restated in full by assistant.message. A stream that falls back to a non-streamed call (stream.fallback) publishes none.

FieldType
stepinteger
channel"text" | "reasoning" | "tool-params"
idstring
deltastring

stream.fallback

Published by the step loop when a streamed provider call fails before its first part: the step is answered by a non-streamed call instead, and so is every later step of the run. Once per run at most. message is the stream’s error as text, cut to 300 characters; it can quote the provider’s error. A stream that fails after its first part fails the step instead (its tools may have run). A refusal over an image (an InvalidRequestError that names one) is no stream failure: it has no fallback; the turn omits the images and sends the step again (StepRequest.onMediaRejected), or the step fails. Journaled as stream.fallback.

FieldType
stepinteger
messagestring

turn.ended

Published once when the turn’s reply is recorded (TurnMemory.persistReply, from guardTurn), also when the turn fails, with outcome failed; usage is the turn’s usage, the usage of its assistant.message events summed (RunContext.usage). Under Agent.turn it is published after use, the host’s per-turn layer and the turn-dependent plugins have closed, and their subscriptions with them: only a subscription whose scope is still open sees it, such as one made with Scope.provide(observerScope) over the caller’s outer scope, or one registered around TurnLive by a host composing the turn itself. Otherwise read the TurnResult Agent.turn returns (its usage is the same total), or the stored turn.reply and turn.ended, and turnUsageOf over the stored events. Not journaled from the bus: memory stores turn.reply and the session its own turn.ended (reason and failure, no usage).

FieldType
runIdstring
turninteger
outcome"completed" | "partial" | "failed"
replystring | null
usage{ inputTokens, outputTokens, totalTokens, cacheReadTokens, cacheWriteTokens, reasoningTokens }

host

An application’s own event (defineHostEvent), published by the host’s code, tools or tasks. name is the host’s namespace and data the payload as its schema encodes it. Journaled under name as its kind; a name in RESERVED_KINDS fails the publish with events.reserved.

FieldType
namestring
dataobject

Journal kinds

Every event of a session’s journal is { session, seq, turn, kind, at, data }; seq is dense from 1 and is the cursor for Sessions.read({ after }). These are the kinds the framework writes. journalSchemaOf(kind) (from @xandreed/core) gives the schema of each kind’s data. A host that exports, streams or redacts session events must treat the sensitive kinds like the conversation itself: request.prepared, memory.system, memory.section and memory.step hold the system prompt and the host’s context.

KindWritten bySensitive contentFields
turn.startedplugin-sessions, when Sessions.begin opens the turnThe user’s message (its images, inline as base64, included), the host’s command and the host’s metadata (meta), when given.runId, key, origin, userMessage, command, claimed, entry, at, meta, resumes
memory.systemMemory, when the turn’s system prompt (or a variant) changesThe full system prompt text.entry, runId, step, at, body
memory.sectionMemory: turn.context(…) and turn-tier prompt sectionsTurn sections: the host’s context for this turn, as the model sees it.entry, runId, step, at, body
memory.stepMemory: the step policy’s step contextStep context text.entry, runId, step, at, body
memory.messageMemory: the step loop’s recorded messagesUser and assistant text, reasoning, tool call inputs.entry, runId, step, at, body
memory.tool-resultMemory: the step loop’s recorded tool resultsTool output, encoded (a tool’s images, inline as base64, included) and as the model saw it.entry, runId, step, at, body
memory.digestMemory: a digester (plugin-memory-digest)Tool output, digested.entry, runId, step, at, body
memory.skillsMemory: the tool registry’s skill activationsNone: skill and tool names.entry, runId, step, at, body
memory.compactionMemory: the memory strategy’s compactions (window, summary); the turn, when a provider refuses an image (OmitMedia)Conversation text: summaries and compact views; an OmitMedia’s reason quotes the provider’s error (up to 500 characters).entry, runId, step, at, body
turn.replyMemory, when the turn’s reply is recorded (TurnMemory.persistReply)The assistant’s reply.entry, runId, step, at, body
step.startedThe turn’s bus (stored from step.started)None: tool names.step, planned, activeTools
step.endedThe turn’s bus (stored from step.ended)None: entry ids and tool names.step, status, results
step.usageThe turn’s bus (stored from assistant.message)None: model name and token counts.step, model, usage
tool.startedThe turn’s bus (stored from tool.started)Tool input.step, invocationId, tool, input, labels, stage
tool.refusedThe turn’s bus (stored from tool.refused)The refusal message, which may name missing permissions or quote the host’s denial; the input is left out.step, invocationId, tool, reason, message
tool.completedThe turn’s bus (stored from tool.completed)None: input and result are left out.step, invocationId, tool, ok, durationMs, labels, stage
completion.evaluatedThe turn’s bus (stored from completion.evaluated)The host’s facts behind the verdict.step, verdict
context.builtThe turn’s bus (stored from context.built)None: fingerprints, counts and tool names.step, turn, strategy, strategyVersion, fingerprint, systemFingerprint, estimatedTokens, reservedTokens, budgetTokens, compactions, activeTools
decision.recordedThe turn’s bus (stored from decision.recorded)Candidate descriptions; the context itself only as a hash.record
stream.fallbackThe turn’s bus (stored from stream.fallback)The stream’s error text, which may quote the provider’s error.step, message
request.preparedThe turn, before each provider call (TurnMemory.prepareRequest)The full system prompt text and every active tool declaration (and step context in system mode).version, runId, step, strategyVersion, system, render, through, contextFingerprint, tools, toolChoice, model, cacheKey, callPolicy
turn.endedplugin-sessions: the turn writer’s end (Agent.turn and Sessions.drain end with turnEndingOf the turn’s exit), the writer’s scope, Sessions.cancel, or a reapFailure messages, which may quote errors.reason, failure
inbox.queuedplugin-sessions: Sessions.deliver (a task’s result, a host’s item)The item’s content and source.id, source, content
inbox.droppedplugin-sessions: the end of the last turn that took the item and failedNone: the item id.id, reason

A memory kind’s data is { entry, runId, step, at, body }: the memory entry’s id, run, step and time, and its body without the _tag (the kind names it). Memory is rebuilt from these events by entriesOfEvents.

KindBody fields
memory.systemfingerprint: string, text: string, sections: Array<{ id, version, fingerprint }>
memory.sectionsectionId: string, version: string, text: string
memory.stepstep: integer, text: string
memory.messagemessage: { role, content, providerOptions }
memory.tool-resulttoolCallId: string, toolName: string, isError: boolean, encoded: unknown, view: string, viewVersion: string, subjects: Array<{ kind, id, label, data }>, artifacts: Array<{ id, kind, mediaType, url, alt }>, pinned: boolean, media: Array<{ path, fingerprint, mediaType, bytes }>, mediaNotes: Array<string>
memory.digestentry: string, version: string, mode: "select" | "summarize", keep: Array<string>, text: string, digester: string, trigger: "write" | "compaction"
memory.skillsskills: Array<string>, tools: Array<string>, source: "always" | "matcher" | "load_skill" | "host", decision: string | null
memory.compactionstrategy: string, version: string, action: { _tag, entries, texts } | { _tag, entry, preview } | { _tag, throughTurn, ledger } | { _tag, keepFromTurn, summary } | { _tag, entries, reason }
turn.replyoutcome: "completed" | "partial" | "failed", reply: string | null

Any other kind is a host’s or a plugin’s own record (defineHostEvent, Sessions.transact, the tasks plugin’s task.*).

Reserved kinds

A host event, or a Sessions.transact draft, may not use these names: the publish fails with events.reserved, the transact with ReservedKind. They are the journal kinds above and every bus event’s name (TURN_EVENT_NAMES), the ones the journal never stores under their own name included (assistant.message, assistant.delta, tool.progress, skills.activated, host). The list is matched exactly, not by prefix: other names in these namespaces are accepted, but a host should use its own namespace.

turn.started
memory.system
memory.section
memory.step
memory.message
memory.tool-result
memory.digest
memory.skills
memory.compaction
turn.reply
step.started
step.ended
step.usage
tool.started
tool.refused
tool.completed
completion.evaluated
context.built
decision.recorded
stream.fallback
request.prepared
turn.ended
inbox.queued
inbox.dropped
tool.progress
skills.activated
assistant.message
assistant.delta
host

Begin codes

When Agent.turn begins the turn itself and the turn cannot begin (Sessions.begin refuses it, or, for AttachmentRejected, Agent.turn’s own check of the attachments before it), the turn fails with a HarnessError whose code is one of these (turnBeginCodes from @xandreed/sdk). The HTTP status is a suggestion for a host serving turns over HTTP.

Begin errorCodeSuggested HTTP statusMeaning
AttachmentRejectedturn.attachment-rejected400An attachment of the user’s message is not an image the framework takes (not base64, not a PNG, JPEG, GIF or WebP, over the size or pixel limits, too large together; see MEDIA_LIMITS). Agent.turn checks before it calls Sessions.begin: nothing was opened. The HarnessError carries the reason only in its message; a host that wants it typed (to answer 413 for TooLarge and TooLargeTogether, or name the attachment by its index) runs normalizeUserMessage from @xandreed/core on the message first and branches on AttachmentRejected.reason.
SessionBusysession.busy409Another turn is open in the session. Retry after it ends, or queue the message.
TurnDuplicateturn.duplicate200 once that turn ended, 409 while it is openThe key was begun before with the same message, command and resumes: that turn is the answer, so serve its result instead of running again (Sessions.lookup(address, key) finds it).
KeyConflictturn.key-conflict409The key was begun before with another message, command or resumes (the key resume:<n> begun again without resumes conflicts).
NothingPendingsession.nothing-pending204An inbox turn found nothing to take (inbox turns only).
NotResumableturn.not-resumable409The turn named in resumes cannot be resumed: missing (no such turn in the session, or resumes is not a whole number of at least 1), inbox (an inbox turn), superseded (a user’s turn began after it, other than a resume of it that failed, was cancelled or was interrupted) or finished (it ended completed, partial or failed). Only the last user’s turn, ended cancelled or interrupted, can be; a resume that failed is tried again with a new key.
TurnRefusedturn.refused429 for a quota or rate, 403 when not allowed, 503 while draining before a shutdownThe host’s TurnAdmission refused the turn; nothing was opened or counted.
SessionMissingsession.missing404No such session for this owner.
SessionLogErrorsession.log503The session store failed. Retry later.

Common turn error codes

Besides the begin codes, these are common HarnessError codes a turn fails with, or that a stored turn.ended carries in failure.code. A failed turn’s turn.ended carries the code of the error it failed with (one of these, a plugin’s, or a host’s own), else turn.failed, turn.defect or turn.interrupted (turnEndingOf from @xandreed/core), with the error’s message cut to 500 characters; a closer that ends its writer itself stores its own code (plugin-tasks: task.turn, task.deadline). The list is not exhaustive: it has every code the packages raise under turn.*, events.*, request.*, loop.*, tools.* (the generator checks), but other plugins raise codes of their own (a memory strategy’s, a store’s), and a host’s own errors keep their own codes.

CodeWhereMeaning
turn.closedAgent.turn, the turn writerSomeone else closed the turn (cancelled, reaped, the session removed); its later writes are refused.
turn.failedstored turn.ended failure (turnEndingOf)The turn failed with a typed error that has no code of its own; the message is <_tag>: <message>, or the error as text (reason failed).
turn.defectstored turn.ended failure (turnEndingOf)The turn died with a defect, an unexpected error; the message is the defect as text (reason failed). Agent.turn and Sessions.drain first turn a HarnessError carried as a defect back into a failure, so it keeps its own code; a runner calling turnEndingOf itself does the same with harnessDefectsAsFailures.
turn.interruptedstored turn.ended failureThe turn was interrupted before it ended: its body (turnEndingOf), the scope its writer lived in, or its begin after the opening commit (reason interrupted).
turn.unendedstored turn.ended failureThe turn’s scope closed, without an interruption, before anyone tried to end the turn (reason failed). An end that was tried but could not be stored is tried again by the scope with that same ending, not this code.
turn.leasestored turn.ended failureThe turn’s holder stopped writing and another instance reaped it (reason interrupted).
turn.budgeta run of the step loop (turn.run, runTurnLoop)The run’s budgetTokens (the policy’s, else the agent’s) is not an integer of at least 1.
config.budgetAgent.defineThe agent’s budgetTokens is not an integer of at least 1.
turn.unstartedthe turn’s memoryMemory was asked to record, or for the turn’s number, before the user’s message was taken.
turn.persistedTurnMemory.persistMessageThe user’s message was taken twice in one turn.
turn.numberingthe turn’s memoryMemory numbered the user’s message differently from the turn the session stored.
events.reservedpublishing a host eventThe host event’s name is one of the reserved kinds.
events.depthpublishing an eventReactions nested deeper than maxEventDepth (8 by default).
events.backgrounda background subscriptionA background handler died with a defect; the turn fails when the bus drains.
events.encodea host event’s make / publishThe payload did not encode with the host event’s schema.
service.missingAgent.turnA service the turn needs is not provided by the agent’s plugins or the turn’s services.
config.graphAgent.defineThe plugin graph cannot be built (a missing provider, a cycle, a misplaced turn-dependent plugin).
config.optionsAgent.define, activating a pluginA plugin’s options did not decode with its schema.
plugin.activationactivating a plugin (a session plugin’s, every turn)The plugin’s layer failed to build with an error that is not a HarnessError (one that is keeps its own code: skills.duplicate, skills.root, …).
plugin.defectactivating a plugin (a session plugin’s, every turn)The plugin’s layer died with a defect while it was built.
tools.duplicatebuilding the tool registryTwo capabilities define a tool of the same name.
skills.duplicatebuilding the tool registryTwo capabilities define a skill with the same id (a host’s and a skills directory’s: set the directory’s idPrefix).
skills.toolsbuilding the tool registryA skill names a tool no capability registers.
skills.filesskillsFromFiles; @xandreed/plugin-workspace skills directory (strict)A skills tree has an issue: a SKILL.md without frontmatter or description, an id already used, a reference over the size limit. The message names each file.
skills.root@xandreed/plugin-workspace skills directoryA required skills root does not exist, or a root or a file in it cannot be read.
skills.limitcollectSkillFiles; @xandreed/plugin-workspace skills directoryA skills root holds more files (maxFiles) or text (maxBytes) than allowed, or the roots more skills than maxSkills.
tools.openedopenTurnToolsThe turn’s tools were opened twice.
tools.unavailablethe turn’s toolsThe tools were used before openTurnTools opened them.
loop.modelthe step loopThe turn’s services carry no LanguageModel.
loop.failedthe step loopThe loop failed with an error that is not a HarnessError (the model call’s own error, for example); the message is that error’s text.
request.encodethe turn, before each provider callThe request header did not encode.
request.missingbefore each provider callNo stored request.prepared header for the step.
request.decodebefore each provider callThe step’s stored request.prepared header did not decode.
request.descriptorbefore each provider callThe model’s descriptor (provider, model and settings) did not decode.
request.divergedbefore each provider callThe request about to be sent differs from the one rebuilt from its stored header and memory.
request.model@xandreed/plugin-modelsThe selected model could not be resolved or described.
request.memoryreplayModelRequestThe memory events before a stored request did not decode.
memory.logthe turnStored memory events did not decode, or memory could not be stored.
context.budgeteach step of a run; @xandreed/plugin-memory-windowAt a step, the system prompt and the active tools’ schemas leave nothing of the context budget (budgetTokens); with the window strategy, also when the current turn alone does not fit the budget memory is given: the whole budget at the turn’s start, what they leave at each step.
session.logthe turn writerThe session store failed during the turn.
session.contendedthe turn writer, plugin-sessionsToo many conflicting writes to the session in a row.
session.statethe turn writerThe session’s state, kept in its head, did not decode.
session.encodethe turn writerThe turn’s closing turn.ended did not encode.

Feed and socket frames

@xandreed/plugin-render turns a journal into client frames (RenderFeed); the host’s projection names each record’s event and data, and its cursor is the journal’s seq. Over SSE, each connection starts with the preamble; a record carries its sequence as the event id, so a reconnecting EventSource resumes with Last-Event-ID. One ready frame follows the backlog (the first poll that finds nothing new), then a heartbeat comment while the feed is idle. The feed ends after maxDurationMs, and the client reconnects with its cursor.

FrameSSEWebSocket
preamble"retry: 1000\n\n"none
record"id: 42\nevent: turn.reply\ndata: {\"outcome\":\"completed\",\"reply\":\"Done.\"}\n\n"{"type":"record","sequence":42,"event":"turn.reply","data":{"outcome":"completed","reply":"Done."}}
ready"event: ready\ndata: {}\n\n"{"type":"ready"}
heartbeat": heartbeat\n\n"{"type":"heartbeat"}
resumethe Last-Event-ID request headerthe client’s first message, {"after":41}

SSE response headers (sseHeaders):

{
  "content-type": "text/event-stream; charset=utf-8",
  "cache-control": "no-store",
  "connection": "keep-alive",
  "x-accel-buffering": "no"
}

Feed defaults (renderFeedDefaults, in milliseconds):

{
  "pollMs": 200,
  "maxPollMs": 1000,
  "heartbeatMs": 15000,
  "maxDurationMs": 240000
}

Task records

@xandreed/plugin-tasks records its own kinds (not reserved). A finished task is delivered to the parent’s inbox once, as inbox.queued with the id task:<taskId> and the source { kind: "task", taskId, child, status }.

KindRecorded inWhen
task.startedthe parent sessionTasks.start: the task’s instructions, mode and the starter’s facts; its child session is created with it.
task.resultthe child sessionBefore the child’s turn ends (or outside a turn when the host refused it): the outcome, reply and failure.
task.cancelledthe child, then the parentTasks.cancel, while the task is pending or running; the first of a result and a cancellation ends the task.

task.started

FieldType
taskIdstring
instructionsstring
mode"fork" | "spawn"
metaobject

task.result

FieldType
taskIdstring
outcome"completed" | "partial" | "failed" | "interrupted"
replystring | null
failure{ code, message } | null

task.cancelled

FieldType
taskIdstring

Task failure codes

The code of a task result’s failure, or of a task’s status, besides any HarnessError code the task’s turn failed with.

CodeWhereMeaning
task.turna task’s result failureThe task’s turn failed, or died with an error that is not a HarnessError (a HarnessError keeps its own code).
task.refuseda task’s result failureThe host would not admit the task’s turn.
task.deadlinea task’s result or the parent’s reactionThe task runner’s deadline cut the work.
task.losta task’s statusThe task’s turn stopped without a result.
task.removeda task’s statusThe task’s session was removed with its parent.