ModelAgents module

Invocation

Invocation
Tenanted through its own client_id rather than through a relation, so the Doctrine filter
binds on every query — a row holds the full transcript (every prompt, tool result and decision),
which is customer content, and no read should depend on the caller remembering to scope itself.
The own-column form matters because neither relation is a reliable owner: agentInstall is
nullable with ON DELETE SET NULL, and batchAction reaches a Sited Batch only through an
untenanted hop. client_id is NOT NULL and is autofilled from the AuthSite the batch runs under.

A model in the Agents module, with 29 members for templates, 28 API properties and 31 members for admin templates.

Agents\Models\Frontend\Invocation Agents\Models\Api\Invocation Agents\Models\Backend\Invocation Also in Agents: Escalation Install Recommendation
View
What Smarty and Twig templates receive. Properties in REST API responses and job payloads. What admin screen templates receive when you override them.

Templates view

Properties 29

Take no arguments.

# getDurationSeconds()
?float
Duration of the invocation in seconds, or null while it is still running.
# getDecisionSummaryHtml()
string
Render the LLM-authored decision summary as HTML. The stored summary is markdown produced by the agent, so the admin detail view gets a nicely-formatted block rather than raw markdown source. Parsedown safe mode is on because the content is model-generated and therefore untrusted.
# getTranscriptTurns()
array
Flatten the provider-neutral transcript into display-friendly turns for the admin detail view. The stored transcript is the verbatim messages array (role + neutral parts, each part a text / tool_call / tool_result map). Templates should not reverse-engineer that shape, so this returns one row per visible turn: [ ['index' => int, 'kind' => 'user'|'assistant'|'tool_call'|'tool_result', 'call' => int, 'title' => string, 'body' => string], ... ] Turns are in stored order and index is a 1-based sequence over the displayed rows, so the reader can cite a position unambiguously. call is a 1-based number shared by a tool_call and the tool_result that answers it (paired on the provider's tool-call id), and is 0 for non-tool turns — a result does NOT always follow its call adjacently, because the loop dispatches a whole batch of calls before returning any of their results.
# getTranscriptPrompt()
string
The opening user message the run was given. There is no prompt column — the composed user message survives only as the first text part of the transcript, so a run that failed before reaching the provider (transcript []) has none.
# getTranscriptSteps()
array
The run as an ordered list of steps, one row per tool call. Where getTranscriptTurns() returns the transcript part-by-part, this joins each tool_call to the tool_result answering it — the two are rarely adjacent, because the loop dispatches a whole batch of calls before any result comes back — so the reader sees a call and its outcome as one row. Rows are either a tool call: ['kind' => 'step', 'n' => int, 'tool' => string, 'ok' => bool, 'status' => string, 'ms' => int|null, 'summary' => string, 'resultSize' => string, 'resultLines' => int, 'args' => string, 'result' => string, 'answered' => bool] or the model's narration between two calls: ['kind' => 'reasoning', 'text' => string] The framing parts are omitted: the opening user message and the closing assistant message are rendered as their own pinned cards (see getTranscriptPrompt() and getDecisionSummaryHtml()), so including them here would duplicate them. ms is null and ok is inferred from the result text for turns recorded before the loop stored those facts explicitly. answered is false when no result was ever recorded for the call — the run died mid-turn.
# getTranscriptStats()
array
Headline counts for the transcript view. totalMs is the summed wall-clock time of the tool calls — not the run duration, which is dominated by provider latency — and is null when no step carries a timing (every pre-timing invocation).
# getId()
?int
# getLibraryAgentSlug()
?string
# getPromptContentHash()
?string
# getProvider()
?string
# getModel()
?string
# getRequestedTier()
?string
# getStartedAt()
?DateTime
# getEndedAt()
?DateTime
# getTotalInputTokens()
?int
# getTotalOutputTokens()
?int
# getTotalCostUsd()
?string
# getDecisionSummary()
?string
# getTerminationReason()
?string
# getTranscript()
?array
# getStructuredResult()
?array
# getRecommendationsProduced()
?int
# getSite()
?Site
# getAgentInstall()
?Install
# getBatchAction()
?BatchAction
# getReplayedFrom()
?Invocation
The original invocation this row is a cost-free replay of, or null for a first-class invocation. Set by the re-run replay path. The FK is SET NULL on delete so removing the original never deletes its replays.
# isNewRecord() Not in API & Jobs
bool
Check if the record is a new record
# isHierarchicalModel() Not in API & Jobs
bool
Check whether a model is a hierarchical model, i.e. can have children
# isVersionedModel() Not in API & Jobs
bool
Check whether a model is versioned

API & Jobs view

API & JobsEach property is a key in REST API responses and job payloads. Its name is the template getter without get, is or has. Methods that need arguments are not available here.

Properties 28

Read-only values on the record.

# durationSeconds
?float
Duration of the invocation in seconds, or null while it is still running.
# decisionSummaryHtml
string
Render the LLM-authored decision summary as HTML. The stored summary is markdown produced by the agent, so the admin detail view gets a nicely-formatted block rather than raw markdown source. Parsedown safe mode is on because the content is model-generated and therefore untrusted.
# transcriptTurns
array
Flatten the provider-neutral transcript into display-friendly turns for the admin detail view. The stored transcript is the verbatim messages array (role + neutral parts, each part a text / tool_call / tool_result map). Templates should not reverse-engineer that shape, so this returns one row per visible turn: [ ['index' => int, 'kind' => 'user'|'assistant'|'tool_call'|'tool_result', 'call' => int, 'title' => string, 'body' => string], ... ] Turns are in stored order and index is a 1-based sequence over the displayed rows, so the reader can cite a position unambiguously. call is a 1-based number shared by a tool_call and the tool_result that answers it (paired on the provider's tool-call id), and is 0 for non-tool turns — a result does NOT always follow its call adjacently, because the loop dispatches a whole batch of calls before returning any of their results.
# transcriptPrompt
string
The opening user message the run was given. There is no prompt column — the composed user message survives only as the first text part of the transcript, so a run that failed before reaching the provider (transcript []) has none.
# transcriptSteps
array
The run as an ordered list of steps, one row per tool call. Where getTranscriptTurns() returns the transcript part-by-part, this joins each tool_call to the tool_result answering it — the two are rarely adjacent, because the loop dispatches a whole batch of calls before any result comes back — so the reader sees a call and its outcome as one row. Rows are either a tool call: ['kind' => 'step', 'n' => int, 'tool' => string, 'ok' => bool, 'status' => string, 'ms' => int|null, 'summary' => string, 'resultSize' => string, 'resultLines' => int, 'args' => string, 'result' => string, 'answered' => bool] or the model's narration between two calls: ['kind' => 'reasoning', 'text' => string] The framing parts are omitted: the opening user message and the closing assistant message are rendered as their own pinned cards (see getTranscriptPrompt() and getDecisionSummaryHtml()), so including them here would duplicate them. ms is null and ok is inferred from the result text for turns recorded before the loop stored those facts explicitly. answered is false when no result was ever recorded for the call — the run died mid-turn.
# transcriptStats
array
Headline counts for the transcript view. totalMs is the summed wall-clock time of the tool calls — not the run duration, which is dominated by provider latency — and is null when no step carries a timing (every pre-timing invocation).
# showUrl Not in Templates
?string
Get the url for exporting a pdf for the record.
# id
?int
# libraryAgentSlug
?string
# promptContentHash
?string
# provider
?string
# model
?string
# requestedTier
?string
# startedAt
?DateTime
# endedAt
?DateTime
# totalInputTokens
?int
# totalOutputTokens
?int
# totalCostUsd
?string
# decisionSummary
?string
# terminationReason
?string
# transcript
?array
# structuredResult
?array
# recommendationsProduced
?int
# site
?Site
# agentInstall
?Install
# batchAction
?BatchAction
# replayedFrom
?Invocation
The original invocation this row is a cost-free replay of, or null for a first-class invocation. Set by the re-run replay path. The FK is SET NULL on delete so removing the original never deletes its replays.

Admin view

AdminAvailable in cdna-admin templates. Copy an admin template into your site repository to change how this record is edited.

Properties 31

Take no arguments.

# getDurationSeconds()
?float
Duration of the invocation in seconds, or null while it is still running.
# getDecisionSummaryHtml()
string
Render the LLM-authored decision summary as HTML. The stored summary is markdown produced by the agent, so the admin detail view gets a nicely-formatted block rather than raw markdown source. Parsedown safe mode is on because the content is model-generated and therefore untrusted.
# getTranscriptTurns()
array
Flatten the provider-neutral transcript into display-friendly turns for the admin detail view. The stored transcript is the verbatim messages array (role + neutral parts, each part a text / tool_call / tool_result map). Templates should not reverse-engineer that shape, so this returns one row per visible turn: [ ['index' => int, 'kind' => 'user'|'assistant'|'tool_call'|'tool_result', 'call' => int, 'title' => string, 'body' => string], ... ] Turns are in stored order and index is a 1-based sequence over the displayed rows, so the reader can cite a position unambiguously. call is a 1-based number shared by a tool_call and the tool_result that answers it (paired on the provider's tool-call id), and is 0 for non-tool turns — a result does NOT always follow its call adjacently, because the loop dispatches a whole batch of calls before returning any of their results.
# getTranscriptPrompt()
string
The opening user message the run was given. There is no prompt column — the composed user message survives only as the first text part of the transcript, so a run that failed before reaching the provider (transcript []) has none.
# getTranscriptSteps()
array
The run as an ordered list of steps, one row per tool call. Where getTranscriptTurns() returns the transcript part-by-part, this joins each tool_call to the tool_result answering it — the two are rarely adjacent, because the loop dispatches a whole batch of calls before any result comes back — so the reader sees a call and its outcome as one row. Rows are either a tool call: ['kind' => 'step', 'n' => int, 'tool' => string, 'ok' => bool, 'status' => string, 'ms' => int|null, 'summary' => string, 'resultSize' => string, 'resultLines' => int, 'args' => string, 'result' => string, 'answered' => bool] or the model's narration between two calls: ['kind' => 'reasoning', 'text' => string] The framing parts are omitted: the opening user message and the closing assistant message are rendered as their own pinned cards (see getTranscriptPrompt() and getDecisionSummaryHtml()), so including them here would duplicate them. ms is null and ok is inferred from the result text for turns recorded before the loop stored those facts explicitly. answered is false when no result was ever recorded for the call — the run died mid-turn.
# getTranscriptStats()
array
Headline counts for the transcript view. totalMs is the summed wall-clock time of the tool calls — not the run duration, which is dominated by provider latency — and is null when no step carries a timing (every pre-timing invocation).
# getShowUrl() Not in Templates
?string
Get the url for exporting a pdf for the record.
# getId()
?int
# getLibraryAgentSlug()
?string
# getPromptContentHash()
?string
# getProvider()
?string
# getModel()
?string
# getRequestedTier()
?string
# getStartedAt()
?DateTime
# getEndedAt()
?DateTime
# getTotalInputTokens()
?int
# getTotalOutputTokens()
?int
# getTotalCostUsd()
?string
# getDecisionSummary()
?string
# getTerminationReason()
?string
# getTranscript()
?array
# getStructuredResult()
?array
# getRecommendationsProduced()
?int
# getSite()
?Site
# getAgentInstall()
?Install
# getBatchAction()
?BatchAction
# getReplayedFrom()
?Invocation
The original invocation this row is a cost-free replay of, or null for a first-class invocation. Set by the re-run replay path. The FK is SET NULL on delete so removing the original never deletes its replays.
# isNewRecord() Not in API & Jobs
bool
Check if the record is a new record
# isHierarchicalModel() Not in API & Jobs
bool
Check whether a model is a hierarchical model, i.e. can have children
# isVersionedModel() Not in API & Jobs
bool
Check whether a model is versioned