Static ReadonlyCarryTool families whose step results are eligible for prior-turn carry-forward —
derived from CarryForwardToolFamily (the single source the stamp sites use).
Read-tool families only: memory writes, pipelines, and client tools also record
StepType='Tool' steps but must never be replayed as reusable results.
Static ReadonlySummarizeDisplay name of the seeded system prompt behind summarizeRange's recursive sub-call (see metadata/prompts/.summarize-range-prompt.json). Resolved with a trimmed, case-insensitive compare — never an exact-case inline literal.
ProtectedAgentProtectedAgent name hierarchy from root to current (e.g. ['Sage', 'Skip', 'Researcher']).
Access the current run for the agent
Accessor for the agent's type instance
ProtectedAgentOverridable accessor for the current agent instance's agent-type state
ProtectedDefaultEngine-default wall-clock timeout applied to any agent run whose
ExecuteAgentParams.maxExecutionTimeMs is not set. Sub-classes can
override to globally change the default. Intentionally generous
(2 hours) — tighten per-run for interactive scenarios.
ProtectedDepthProtectedDepth of this agent in the execution hierarchy (0 = root).
ProtectedFileProtectedAccumulated file outputs (PDF, Excel, Word, etc.) produced this run.
Mirrors the existing MediaOutputs accessor pattern but is scoped to
driver sub-classes since it's a more internal collection.
ProtectedmaxProtectedCharacter budget (~4 chars/token) for a SINGLE standalone artifact-tool result injected into
the conversation. A get_full on a large artifact can otherwise dump the whole thing into
context and overflow the model's window — the exact failure pipelines exist to avoid. Override
in a subclass to tune. Pipelines are unaffected: their intermediate results never flow through
here, and the executor already caps a pipeline's final output.
Gets the currently accumulated media outputs for this agent run.
Array of promoted media outputs
ProtectedParentProtectedParent step counts used to build the 2.1.3 hierarchical step label.
ProtectedProviderReturns the active metadata provider for this agent run. Subclasses MUST
use this getter (rather than new Metadata() or Metadata.Provider) so
that per-request provider isolation is preserved on the server.
ProtectedResolvedThe resolved FileStorageAccount ID for this agent run. Set during Execute() via the hierarchical resolution chain (Runtime → Agent → Category → Type → fallback). Included in the ExecuteAgentResult so AgentRunner can route file artifact uploads.
Subclasses can read this to customize storage behavior based on the resolved account.
Gets the current validation retry count for the agent run. This count tracks how many times the agent has retried validation during the FinalPayloadValidation step.
ProtectedapplyResult type for applyActionChanges method. Contains both the modified actions and any dynamic execution limits.
ProtectedapplyApplies payload changes if any are requested in the next step
ProtectedapplyProtectedApplies auto-alignment rules to includeResponseTypeDefinition based on other flags.
When a documentation flag (e.g., includeForEachDocs) is explicitly set to false, the corresponding response type section (e.g., forEach) should also be excluded unless explicitly set otherwise.
Auto-alignment mappings:
The merged params object to modify in place
OptionalexplicitResponseType: Record<string, unknown>The explicitly set response type config from agent/runtime (not schema defaults)
ProtectedapplyProtectedApplies runtime SubAgentChanges to a base sub-agent set — the sub-agent counterpart of applyActionChanges. Returns a NEW array (never mutates the cached base set).
ProtectedattemptProtectedAttempts to recover from a context length exceeded error using multiple strategies. Uses escalating strategies: remove old results → compact old results → compact all → trim user message. This approach preserves the user's original request while removing stale tool results.
Agent execution parameters (conversationMessages will be modified)
Current payload to carry forward
The original error message from the failed prompt
OptionalmodelSelectionInfo: AIModelSelectionInfoModel selection information containing the model and vendor used
A Retry step with reduced context or Failed if recovery unsuccessful
ProtectedbuildProtectedBuilds the agent-invariant AgentBaseCatalog — the resolved sub-agents + actions and their formatted markdown, plus the base agent-type prompt params. Computed once per agent and cached on AIEngine (see gatherPromptTemplateData); does NOT apply any runtime overrides.
ProtectedbuildProtectedBuilds merged agent type prompt params from schema defaults, agent config, and runtime overrides.
Merge precedence (lowest to highest):
The agent type entity with schema definition
The agent entity with configured values
OptionalruntimeOverrides: Record<string, unknown>Optional runtime overrides from ExecuteAgentParams.data
Merged prompt params object
ProtectedbuildProtectedBuilds the summarizeRange recursive-sub-call host: resolves the seeded 'Summarize Conversation Range' prompt (priority-ordered cheap models — the RLM "strong root model, cheap sub-call model" split) and runs it via the standard prompt runner so the AIPromptRun records itself.
ProtectedbuildProtectedBuilds hierarchical step string from parent and current step counts.
Examples:
Exposed as protected so driver sub-classes can emit step labels that
match the framework's 2.1.3 nesting convention.
Current agent's step number (1-based)
Array of parent step counts from root to immediate parent
Formatted hierarchical step string, or undefined if currentStep is undefined/null
ProtectedbuildProtectedBuilds a per-run PipelineToolRegistry that unifies the three pipeline-able substrates behind one namespace: built-in transforms, the agent's effective Actions, and the run's artifact tools. Transforms register first so their reserved names win; a source whose name collides with a transform is skipped for pipeline use (still callable normally) and logged, rather than aborting the whole pipeline.
ProtectedbuildProtectedBuilds the editable plan-approval AgentResponseForm: the Markdown-rendered plan (with an
Edit toggle so the human can amend it before approving), an optional feedback field that
travels back to the agent with the decision (most useful on Reject — it steers the re-plan),
and an Approve/Reject button group. Override to change the card's layout (e.g. split the
plan into per-step checkboxes instead of one field).
Approval is a HIGHER-ORDER signal, not just a form reply: conversation hosts detect
decision === 'approve' on this form and switch the conversation out of Plan Mode
(see ng-conversations' plan-decision handling), so the follow-up run executes the approved
plan instead of planning again. Rejection keeps Plan Mode on — the agent re-plans with the
feedback in context.
ProtectedbuildBuilds the fully-populated RealtimeSessionRunnerDeps from this agent's collaborators.
Each dependency is a thin closure over BaseAgent state so the runner stays decoupled from
metadata/DB. The closures cover: target delegation (via ExecuteSubAgent), non-target
tool execution, transcript persistence (as ConversationDetail), and usage checkpointing
(onto the long-lived prompt run).
The execution parameters.
The agent configuration.
The resolved realtime model + identifiers.
The long-lived prompt run for usage checkpoints (may be null).
The assembled deps object.
ProtectedbuildProtectedBuilds the message appended to conversationMessages when skill(s) activate — this is what
actually puts each skill's Instructions into effect for the rest of the run. Override to
change formatting (e.g. a more compact representation for a high skill-activation-count agent).
ProtectedbuildProtectedBuilds the AgentSkillInvocation observability record for a skill activation — capturing WHO pulled the trigger and the provenance-of-authority gate values in effect at activation time, so auditors can see exactly which configuration admitted the skill even if that configuration later changes.
Optionalreason: stringProtectedcapProtectedBound a standalone tool result to maxStandaloneToolResultChars: return a head slice
plus a redirect that teaches the agent to page (get_rows) or reduce (pipeline) instead of
reading a whole large artifact. Mirrors how read/search tools cap output at the tool boundary.
ProtectedcheckProtectedChecks execution guardrails and modifies next step if limits are exceeded. This method is called after validation but before execution of non-terminal steps.
Execution parameters
The validated next step
Current payload
Current agent run
Current execution step
Modified next step if guardrails exceeded, or original next step
ProtectedcheckChecks if all minimum execution requirements are met for actions and sub-agents.
NOTE: This method intentionally uses only database-configured AgentActions, not _effectiveActions.
MinExecutionsPerRun is a database-only concept - dynamically added actions via actionChanges
do not have minimum execution requirements. If minimum requirements for dynamic actions are
needed in the future, consider adding a minActionLimits property to the ActionChange interface.
The agent to check
The current agent run
Array of violation messages (empty if all requirements are met)
ProtectedcheckProtectedPre-turn fallback: compacts synchronously when the assembled window is already over the trigger budget BEFORE the first prompt of this run, then splices the fresh summary into the live message array. Only runs with an EXPLICIT configured budget (agent or type ContextWindowMaxTokens) — before the first prompt the model is unknown, and compacting against the conservative default would over-trigger on large-context models. The post-turn hook (real model known) covers those.
ProtectedcloneProtectedBest-effort deep clone for sub-agent payloads. We need this so two parallel sub-agents can each receive their own working copy — without it, mutations by one in-flight sub-agent would race the others' reads.
Uses structuredClone (Node 17+) where available; falls back to a JSON
round-trip for environments without it. Returns the original value on
non-cloneable inputs.
JSON fallback caveats — the round-trip is not shape-preserving:
Date → ISO stringMap, Set, RegExp, typed arrays → {}undefined values and function-valued properties → droppedBigInt → throws (caught by the outer try/catch, returns original)Exposed as protected so driver sub-classes performing their own parallel
dispatch get the same payload-isolation guarantee.
ProtectedcompactProtectedCompacts a message using configured compaction mode.
The message to compact
Compaction configuration
Agent execution parameters for context
Compacted content string
ProtectedcontentProtectedConverts ChatMessageContent to a string representation. Handles both simple strings and content block arrays.
The message content to convert
String representation of the content
ProtectedconvertConverts UI markup (@{...} syntax) in user messages to plain text. This prevents agents from being confused by UI-specific JSON syntax and reduces token usage.
Modifies the messages in-place, converting:
The conversation messages array to convert (modified in-place)
ProtectedcreateProtectedCreates an AIAgentRunStep for message compaction operations. Records the compaction attempt with context about the message being compacted.
The AI prompt used for compaction
The message being compacted
Agent execution parameters
The created run step entity
ProtectedcreateProtectedCreates a step entity for tracking.
Exposed as protected so driver sub-classes (e.g. Skip) can author custom
AIAgentRunStep records with the same setup correctness — StepNumber,
hierarchy breadcrumb, UUID validation, payload serialization, and the
queueStepSave coupling that keeps INSERT-then-UPDATE ordering safe.
Step creation parameters
Optionalcompleted?: {For steps whose outcome is fully known BEFORE the row exists (e.g. recording an
already-finished compaction pass): applies finalizeAgentRunStep in memory
so the single queued INSERT carries the terminal state, and no follow-up
finalizeStepEntity UPDATE is needed — one DB write instead of two. Do NOT use
for steps with real work between start and finish; those stay two-phase so a
crash mid-work leaves a visible Running row.
OptionalinputData?: anyOptionalparentId?: stringOptionalpayloadAtEnd?: anyOptionalpayloadAtStart?: anyOptionalskills?: AgentSkillInvocation[]Skill-invocation records to persist on AIAgentRunStep.Skills for this step. When
omitted, Prompt steps default to the full set of skills currently in effect
(_skillInvocations) so prompt injection is always visible; all other step
types default to no skill linkage. Pass explicitly for Skill steps (the activation
performed) and Actions/Sub-Agent steps (the skill(s) that granted the tool).
OptionaltargetId?: stringOptionaltargetLogId?: stringProtectedcreateProtectedCreates a chat message containing sub-agent execution results.
The sub-agent that was executed
The result from sub-agent execution
A formatted message with sub-agent results
ProtecteddeduplicateDeduplicates arrays in AgentSpec to fix common LLM retry issues Modifies spec in place and returns whether duplicates were found
ProtecteddetermineBase class method that determines the next step by contacting the agent type class for the specified agent type and delegating that decision. Sub-classes can override this method to implement custom next step logic if needed.
ProtecteddoesProtectedDetermines if an action change scope applies to the current agent.
The scope of the action change
The ID of the current agent
Whether this is the root agent
OptionalagentIds: string[]Array of specific agent IDs (for 'specific' scope)
True if the change applies to this agent
ProtectedemitProtectedEmits message lifecycle event if callback is registered.
ProtectedenableProtectedEnables a skill's bundled Actions and sub-agents by pushing specific-scoped add entries
(targeted at exactly the activating agent's ID) onto params.actionChanges /
params.subAgentChanges. specific/[agent.ID] is the correct scope for "apply to THIS
agent, at whatever depth it runs, and never leak to its sub-agents":
root-scoped change would NOT do, since root means
"the depth-0 agent," not "the current agent").specific as-is, and each downstream agent checks includes(itsOwnID) → false, so the
grant never cascades to sub-agents the activating agent later delegates to.
Because params is the same object reference used for the rest of this run, every subsequent
turn's gatherPromptTemplateData() call picks up the change automatically — no extra plumbing.Override to change propagation scope (e.g. a subclass that wants skill-granted capabilities
to cascade to sub-agents could push scope: 'all-subagents' instead).
ProtectedestimateProtectedEstimates total token count across all conversation messages.
Message array to estimate
Total estimated tokens
ProtectedestimateOptionalmodelName: stringProtectedexecuteProtectedExecutes the agent's internal logic.
This method contains the core execution logic that drives agent behavior. By default, it implements a sequential execution loop, but subclasses can override this to implement different execution patterns such as:
The type of the return value from agent execution
The execution parameters with wrapped callbacks (includes wrapped onProgress and onStreaming)
The loaded agent configuration
The execution result with typed final payload and step count
ProtectedexecuteProtectedExecutes a batch of artifact tool calls, recording each as its own
Tool AIAgentRunStep (a sibling of the Prompt step that requested them)
with full inputs/outputs captured in InputData/OutputData. Returns the
stored results so the caller can render them into a single recall-friendly
message for the next prompt turn.
Step naming convention: Artifact Tool: {toolName} for log/UI clarity.
ProtectedexecuteProtectedExecutes conversation-history retrieval tool calls, wrapping each invocation in its own AIAgentRunStep (StepType='Tool', "Conversation Tool: {tool}") — the same per-call observability shape as artifact tools. Reads are served from the ConversationEngine cache; per-call failures are contained in the result.
At most MAX_CONVERSATION_TOOL_CALLS_PER_TURN calls execute per response; the excess come back as skipped failure-shaped results (no run steps recorded) telling the model to re-request them next turn.
ProtectedexecuteProtectedExpands a previously compacted message to its original content.
The expand message request
Agent execution parameters
Current turn number
null when the expansion succeeded; otherwise a model-facing reason the expansion is impossible. Callers must surface a non-null reason into the next prompt's context — a silent no-op leaves the loop state identical and the model re-requests the same expansion indefinitely.
ProtectedexecuteProtectedExecutes a batch of in-flight memory writes, recording each as its own
Tool AIAgentRunStep (a sibling of the Prompt step that requested them)
with full inputs/outcomes captured in InputData/OutputData.
Writes run SEQUENTIALLY (not Promise.all like artifact tools) by design: each persisted note is embedded and synced into the in-memory vector service on Save, so write N must be visible to write N+1's near-duplicate check (this is also what makes same-run supersede-own work). The per-run cap bounds the cost of the serialization.
Step naming convention: Memory Write for log/UI clarity.
ProtectedexecuteProtectedExecutes the next step based on the current state.
This method can be overridden by subclasses to customize step execution behavior. It handles the execution of different step types (prompt, actions, sub-agent, chat) based on the previous decision.
Original execution parameters
Agent configuration
Previous step decision
OptionalstepCount: numberProtectedexecuteProtectedRuns a tool pipeline as a single Tool step in the run tree (sibling of the prompt step that
requested it, matching artifact-tool steps). ALL pipeline observability lives in this step's
OutputData — the per-stage breakdown, totals, bytes saved, and the tool chain — so there are
no dedicated pipeline entities and no extra SQL I/O; the run tree alone carries everything a
debug UI needs.
ProtectedexecuteProtectedExecutes a 'Plan' next step (Plan Mode): records a Plan run-step, raises the standard
MJ: AI Agent Requests HITL request with an editable plan-approval AgentResponseForm, and
terminates this run awaiting the human's response — reusing the exact same pause/resume
infrastructure executeChatStep uses (createFeedbackRequest + the existing
MJAIAgentRequestEntityServer.Save() auto-resume-on-status-change hook). A rejected or
edited-and-resubmitted plan resumes as a new linked run via the normal run-chain mechanism;
resolvePlanModeGate re-checks approval on that new run so a rejection sends the agent back
to present a revised plan rather than through to execution.
Important: the RETURNED BaseAgentNextStep.step is 'Chat', not 'Plan' — 'Plan' is
only ever an intermediate classification (used for the AIAgentRunStep.StepType audit
record, which the UI reads to render a plan-approval card instead of a generic chat bubble).
The step returned to the framework must stay within AIAgentRun.FinalStep's DB-CHECK-
constrained, terminal-only vocabulary — 'Plan' is deliberately not part of it (see the
BaseAgentNextStep.step doc comment) — so a plan-approval pause is represented as the same
terminal shape executeChatStep already uses.
ProtectedexecuteProtectedExecutes the configured prompt. Always uses the attemptJSONRepair option to try to fix LLM JSON syntax issues if they arise.
The prompt parameters
The prompt execution result
ProtectedexecuteThis method executes one action using the MemberJunction Actions framework. The full ActionResult objects are returned, allowing the caller to access result codes, output parameters, and other execution details.
Parameters from agent execution for context passing
Action to execute
OptionalcontextUser: UserInfoOptional user context for permissions
ActionResult object from the action execution
ProtectedexecuteProtectedExecutes a 'Skill' next step: activates one or more skills the LLM requested by name.
Activating a skill (1) appends its full Instructions to the conversation so they take
effect for the remainder of the run, and (2) enables its bundled Actions/sub-agents by
pushing root-scoped add entries onto params.actionChanges/params.subAgentChanges —
the same runtime tool-surface-extension mechanism ExecuteAgentParams already exposes to
external callers. This is NOT a nested agent run; it never terminates the loop itself.
Already-activated skills (tracked in _activatedSkillIDs) are skipped — re-requesting an
active skill is a harmless no-op rather than re-appending duplicate instructions.
Decomposed into resolveSkillActivations, buildSkillActivationMessage, enableSkillCapabilities, and recordSkillActivationStep — override any of those for fine-grained control (e.g. custom instruction formatting, additional side effects on activation) without re-implementing the whole step.
OptionalstepCount: numberProtectedExecuteExecutes a sub-agent synchronously.
This method creates a new instance of AgentRunner to execute a sub-agent. The sub-agent receives the provided message/context and runs to completion. If terminateAfter is true, the parent agent will not continue after the sub-agent completes.
Sub-agent execution details
Optionalpayload: SROptionalcontextMessage: ChatMessageOptionalstepCount: numberResult from the sub-agent execution
ProtectedextractProtectedfilterProtectedFilters and transforms action changes for propagation to a sub-agent.
This method applies the following propagation rules:
The action changes to filter
Filtered action changes for sub-agent, or undefined if empty
ProtectedfilterProtectedFilters/transforms sub-agent changes for propagation to a sub-agent — the sub-agent counterpart of filterActionChangesForSubAgent (same propagation rules).
ProtectedfinalizeProtectedFinalizes a step entity with completion status. Pairs with createStepEntity
— drivers that create custom steps should also finalize them through this
method so Status/CompletedAt/Success/ErrorMessage/OutputData are
populated consistently and the UPDATE is sequenced behind the INSERT via
queueStepSave.
OptionalerrorMessage: stringOptionaloutputData: anyProtectedformatProtectedFormats a message with agent hierarchy for streaming/progress updates.
Exposed as protected so driver sub-classes emit progress events whose
breadcrumbs line up with the framework's own — keeps the Explorer tree
view consistent across custom and built-in dispatch.
The base message to format
ProtectedformatFormats validation errors into a clear, actionable message
ProtectedgetGets the count of how many times a specific action has been executed in this agent run.
The agent run ID (not used anymore, kept for signature compatibility)
The action ID to count
The number of times the action has been executed
ProtectedgetUtility method to get agent prompt parameters for a given agent. This gets the highest priority prompt for the agent, and then gets the parameters for that prompt.
ProtectedgetGets the available configuration presets for an agent. Returns semantic presets like "Fast", "Balanced", "High Quality" that users can choose from. These are actual presets stored in the database with specific AIConfiguration references.
The ID of the agent to get presets for
Array of configuration presets sorted by Priority, or empty array if none configured
const agent = new ResearchAgent();
const presets = agent.GetConfigurationPresets('agent-uuid-here');
// Returns presets defined in database: [
// { Name: 'Fast', DisplayName: 'Quick Draft', AIConfigurationID: 'fast-config-uuid', IsDefault: true },
// { Name: 'HighQuality', DisplayName: 'Maximum Detail', AIConfigurationID: 'frontier-uuid', IsDefault: false }
// ]
// Note: If no presets configured, returns empty array - agent will use default behavior
Gets the default configuration preset for an agent.
The ID of the agent to get the default preset for
The default preset, or undefined if none configured
ProtectedgetProtectedGets the effective actions for validation, using runtime changes if available. Falls back to database-configured actions if _effectiveActions is empty.
The ID of the agent to get actions for
Array of effective actions available to the agent
ProtectedgetProtectedGets the effective sub-agents for validation, using runtime subAgentChanges if available. Falls back to the database-configured relationship set if _effectiveSubAgents is empty. Mirrors getEffectiveActionsForValidation.
The ID of the agent to get sub-agents for
Array of effective sub-agents available to the agent
ProtectedgetProtectedGets the execution count for an item (action or sub-agent).
The item ID to get count for
The execution count (0 if never executed)
ProtectedgetProtectedGets the context limit for the current model. Uses model selection info from the prompt result to determine the actual model's MaxInputTokens.
OptionalmodelSelectionInfo: AIModelSelectionInfoModel selection information from the prompt execution
The maximum input tokens for the model
ProtectedgetReturns the list of sub-agent requests on a next-step decision, normalizing
the singular (subAgent) and plural (subAgents) forms. Plural takes
precedence when both are present (parallel fan-out); otherwise the singular
form is wrapped into a single-element array. Empty if neither is set.
Use this anywhere code needs to enumerate the sub-agents an LLM requested
— keeps validation and execution paths consistent and avoids the regression
where one path read .subAgent?.name and missed parallel requests.
ProtectedgetProtectedResolves which activated skill(s), if any, granted the given action to this agent — the
attribution recorded on the Actions step's Skills column. Returns undefined (no
linkage) when the action is one of the agent's NATIVE grants (an Active MJ: AI Agent Actions row), even if an activated skill also bundles it: native authority takes
precedence, and Skills = NULL is the contract for "the agent had this tool anyway".
ProtectedgetProtectedResolves which activated skill(s), if any, granted the given sub-agent to this agent — the
attribution recorded on the Sub-Agent step's Skills column. Returns undefined when the
sub-agent is a NATIVE relationship (a ParentID child of this agent, or an Active
MJ: AI Agent Relationships referenced-sub-agent row), even if an activated skill also
bundles it — same native-precedence contract as getSkillAttributionForAction.
ProtectedgetResolves the file storage account ID for this agent's file artifacts.
Resolution chain (first non-null wins):
params.override?.storageAccountId)params.agent.DefaultStorageAccountID)ParentIDagentType.DefaultStorageAccountID)This method is protected so subclasses can override the resolution logic
for custom storage routing (e.g., routing by file type or tenant).
The current agent execution parameters
The resolved FileStorageAccount ID, or null if none configured
ProtectedgetGets the count of how many times a specific sub-agent has been executed in this agent run.
The agent run ID (not used anymore, kept for signature compatibility)
The sub-agent ID to count
The number of times the sub-agent has been executed
ProtectedgetProtectedReturns the system default prompt ID for message compaction. Looks up the "Compact Agent Message" prompt by name.
ProtectedgetHelper to get value from nested object path - extracts from LoopAgentType
ProtectedhandleHandles final payload validation failures based on the configured mode.
Execution parameters
The original success next step
The current payload
The validation mode (Retry, Fail, Warn)
The validation error messages
Modified next step based on validation mode
ProtectedhandleProtectedHandles validation of the starting payload if configured.
This method validates the input payload against the agent's StartingPayloadValidation schema before execution begins. It respects the agent's PayloadScope if configured.
The execution parameters
Error result if validation fails and mode is 'Fail', null otherwise
ProtectedhasProtectedChecks if any agent run guardrails have been exceeded. Override this method to implement custom guardrail logic.
Execution parameters
Current agent run
Object indicating if guardrails exceeded and details
ProtectedhashSimple hash function for string deduplication
ProtectedincrementProtectedIncrements the execution count for an item (action or sub-agent).
Exposed as protected so driver sub-classes performing custom dispatch
bump the same per-item counter the framework checks against execution
guardrails — without this, custom dispatch silently bypasses limits.
The item ID to increment (action ID or sub-agent ID)
ProtectedinitializeThis overridable method is responsible for setting up any necessary one-time initalization of the agent type. The base class sets up the AgentTypeInstance and also lets that agent type initialize its state.
ProtectedinitializeProtectedInitializes the AI and Action engines. Subclasses can override this to add any additional engine/metadata loading initialization they want to do and this method will be called at the right time in the agent execution process.
OptionalcontextUser: UserInfoOptional user context
ProtectedinitializeSub-classes can override this method to perform any specialized initialization
ProtectedinjectProtectedPushes a single user-role message containing rendered artifact-tool
results into the conversation. This mirrors the action-result
"inject once, then expire" pattern — the LLM sees the results on its
next turn, and older messages are pruned/compacted by
pruneAndCompactExpiredMessages instead of being re-rendered into
every system-prompt turn.
ProtectedInjectInject notes and examples into agent context memory. Called automatically before agent execution if injection is enabled on the agent. Injects memory context directly into conversation messages array.
The user input text for semantic search
The agent configuration entity
OptionaluserId: stringOptional user ID for scoping
OptionalcompanyId: stringOptional company ID for scoping
OptionalcontextUser: UserInfoUser context
OptionalconversationMessages: ChatMessage[]The conversation messages array to inject into
OptionalprimaryScopeEntityId: stringOptional primary scope entity ID for multi-tenant filtering
OptionalprimaryScopeRecordId: stringOptional primary scope record ID for multi-tenant filtering
OptionalsecondaryScopes: Record<string, SecondaryScopeValue>Optional secondary scope dimensions for multi-tenant filtering
OptionalsecondaryScopeConfig: SecondaryScopeConfigOptional agent-level scope config for per-dimension inheritance
Object containing injected notes and examples
ProtectedinjectProtectedPushes a single user-role message containing rendered conversation-tool results into the conversation — the same inject-once-then-expire lifecycle as artifact tool results.
ProtectedinjectProtectedPushes a single user-role message containing memory-write outcomes into
the conversation, mirroring injectArtifactToolResultsMessage's
inject-once-then-expire pattern. Closing the loop here is what stops the
LLM from re-emitting the same memory on subsequent turns.
ProtectedinjectProtectedPushes the pipeline's final output (or its failure message) into the conversation for the LLM's next turn, mirroring the artifact-tool "inject once, then expire" pattern. Only the final output is surfaced — intermediate step outputs never enter the context window.
ProtectedInjectInject pre-execution RAG context for this agent using scoped search.
Runs in parallel with InjectContextMemory during Phase 2 of Execute(). Loads the
agent's AIAgentSearchScope rows (Phase IN 'PreExecution'|'Both'), renders any per-scope
query templates, calls SearchEngine.Search() per scope, cross-scope RRF fuses the
results when multiple scopes contributed, and unshifts a <retrieved_context> system
message onto conversationMessages.
When the agent has no active pre-execution scopes (or all scopes returned zero results),
this method is a no-op — no system message is injected and _injectedRAG stays null.
See plans/search-scopes-rag-plus.md §4 (Agent Integration — Pre-Execution RAG).
The most recent user message text.
The agent being executed.
Calling user (threaded to SearchEngine + Metadata).
The mutated message array that flows to the LLM (system msg is unshifted here).
The unmodified messages array (for template recentMessages).
OptionalprimaryScopeEntityId: stringMulti-tenant primary scope entity ID.
OptionalprimaryScopeRecordId: stringMulti-tenant primary scope record ID.
OptionalsecondaryScopes: Record<string, SecondaryScopeValue>Multi-tenant secondary scope dimensions.
Optionalpayload: unknownThe agent's current payload (for template rendering).
The structured RAG result, or null if no scopes produced results.
ProtectedinjectProtectedCarries the PREVIOUS turn's tool results forward into this run's context.
Inline tool results (artifact + conversation tools) are injected into the run's in-memory messages only — the next turn rebuilds messages from the conversation window, so a result paged in on turn N is gone on turn N+1 and the agent must re-call the tool. The results already persist in each Tool step's OutputData; this re-injects the immediately previous completed run's successful results as one transient message. One-turn memory by construction: each run carries only its direct predecessor's results, so context never compounds.
Gated on conversationId + root depth — programmatic runs and sub-agents skip it.
ProtectedInjectInject this agent's scoped prompt parts into the conversation, role-faithfully.
Parallels InjectContextMemory: resolves MJ: Scoped Prompt Parts for the agent's
primary prompt under the run's polymorphic scope (the SAME PrimaryScope/SecondaryScopes the
runtime threads for memory), and unshifts the assembled role-tagged messages onto
conversationMessages. In-memory + synchronous (parts are cached on AIEngine). No-op when
the agent has no active prompt or no parts resolve for the scope.
OptionalprimaryScopeEntityId: stringOptionalprimaryScopeRecordId: stringOptionalsecondaryScopes: Record<string, SecondaryScopeValue>ProtectedisProtectedDetermines if an error is a configuration error that cannot be resolved by retrying. Configuration errors indicate issues with agent setup that need manual intervention.
This method provides detailed diagnostic information to help identify what configuration is missing or incorrect.
The error message string
Optionalconfig: AgentConfigurationOptional agent configuration to check for missing pieces
Object with determination and detailed diagnostic message
ProtectedisProtectedDetermines if a prompt execution error is fatal and should stop agent execution. Fatal errors are those that won't be resolved by retrying, such as context length exceeded (when no larger model is available), authentication failures, or invalid request format.
The result from prompt execution
true if the error is fatal and agent should terminate, false otherwise
ProtectedisType guard for whether the resolved agent-type instance is session-driven.
Detects the Realtime agent type without importing it (and without instanceof, which is
brittle under bundler class-duplication) by duck-typing the IsSessionDriven getter that
RealtimeAgentType adds. BaseAgentType (and Loop/Flow) do not expose this member, so the
guard returns false for them and the iterative loop runs unchanged.
The resolved agent-type instance for this run.
true only when the type explicitly marks itself session-driven.
ProtectedIsReturns true if the message is a tool result (action or client tool). Used by recovery strategies to identify compactable result messages.
ProtectedIsProtectedReturns true if the message is a turn-generated result (action, tool, client tool, sub-agent, or loop). These are the volatile, expirable messages that accumulate over turns. Everything else — system/RAG context, injected memory, the original user request — anchors the cache-stable prompt prefix and is protected from routine pruning.
ProtectedloadProtectedLoads all required configuration for agent execution.
The agent to load configuration for
Configuration object with loaded entities
ProtectedlogHelper method for enhanced error logging with metadata
Optionaloptions: {ProtectedlogHelper method for status logging with verbose control
The message to log
OptionalverboseOnly: booleanWhether this is a verbose-only message
Optionalparams: ExecuteAgentParamsOptional agent execution parameters for custom verbose check
ProtectedmapProtectedMaps an array through an async worker with bounded concurrency. Preserves input order in the output. Used to cap parallel sub-agent and preload dispatches so a misbehaving LLM (or a runaway data source) can't exhaust the model API or DB pool.
Exposed as protected so driver sub-classes performing custom parallel
work get the same bounded-fan-out + ordered-results contract for free.
ProtectednotifyProtectedHandles user-requested skill IDs that failed the activation guard (agent-accepted ∩ user-permitted). A silent drop leaves both the user AND the agent blind to the refusal — the agent then improvises around the missing capability instead of explaining it. This emits a server-side warning log and injects a system note into the conversation so the agent tells the user why the skill isn't available rather than working around it.
ProtectedpreProtectedPre-activates skills the caller explicitly requested via ExecuteAgentParams.requestedSkillIDs
(typically an end user's /skill-name composer mentions), at run start — so their Instructions
and bundled Actions/sub-agents take effect from the first turn rather than waiting for the model
to discover and activate them through the catalog.
Root-agent only (skills never cascade to sub-agents), and each requested skill activates
only if it survives the guard: it must be in the set AIEngine.GetSkillsForAgent
allows for this agent (the AcceptsSkills gate) AND the acting user must have Run permission on it
— both enforced by passing params.contextUser to GetSkillsForAgent. Requested IDs that fail
either check are silently dropped, so a client can never force-activate a skill the user or agent
isn't entitled to. Reuses the same recordSkillActivationStep / enableSkillCapabilities
/ buildSkillActivationMessage machinery as the model-initiated Skill step, so activation
is recorded and takes effect identically. Plan Mode is unaffected — pre-activation widens the tool
surface, but the plan-approval gate still blocks executing those tools until the plan is approved.
ProtectedprepareProtectedPrepares prompt parameters for hierarchical execution.
Original execution parameters
Configured prompt parameters
ProtectedprepareProtectedPrepares conversation messages for sub-agent execution based on database-configured message mode.
Message passing is controlled by MessageMode and MaxMessages fields stored in either:
Message Modes:
'None': Fresh start - only context message and task message (default)'All': Pass complete parent conversation history'Latest': Pass most recent N messages (where N = MaxMessages)'Bookend': Pass first 2 messages + indicator + most recent (N-2) messagesPriority: AIAgentRelationship.MessageMode takes precedence over AIAgent.MessageMode to allow different parent agents to pass messages differently to the same sub-agent.
Subclasses can override this method to implement custom message preparation logic specific to their domain (e.g., Skip agents adding special context).
Execution parameters with conversation history
Sub-agent request details
The sub-agent entity
OptionalcontextMessage: ChatMessageOptional context from SubAgentContextPaths
Prepared message array for sub-agent execution
ProtectedprocessProtectedTurn-loop entry point for in-flight memory writes, gated on the agent's AllowMemoryWrite flag. When disabled but the LLM emitted writes anyway (prompt drift / injection attempt), records ONE summary skip step — observable without per-write noise — and tells the agent the memories were NOT saved so it stops re-emitting. When enabled, executes the writes as run steps and injects the results message.
ProtectedprocessProtectedProcesses the next step based on agent type determination.
Original execution parameters
The agent type
The prompt execution result
The execution result
Promotes media outputs to the agent's final outputs. Call this method to add generated images, audio, or video to the agent's outputs. These will be saved to AIAgentRunMedia and flow to ConversationDetailAttachment.
Array of media outputs to promote
// Promote images from action result
this.promoteMediaOutputs([{
modality: 'Image',
mimeType: 'image/png',
data: base64Data,
label: 'Generated Product Image'
}]);
// Promote from prompt run media reference
this.promoteMediaOutputs([{
promptRunMediaId: 'prompt-run-media-uuid',
modality: 'Image',
mimeType: 'image/png',
label: 'AI Generated Visualization'
}]);
ProtectedpruneProtectedPrunes and compacts expired messages in the conversation based on configured expiration rules. Processes messages in three phases: identification, compaction, and removal.
Agent execution parameters containing conversation messages
Current turn number in the agent execution
ProtectedqueueProtectedQueues a fire-and-forget UPDATE of a step entity whose fields the caller has ALREADY mutated.
Delegates to AgentRunStepSaveQueue.QueueUpdate — the agent flow never awaits this; the UPDATE
chains after the step's INSERT and force-persists (IgnoreDirtyState). Kept protected so driver
subclasses that finalize their own steps get the same non-blocking behavior.
OptionalapplyMutation: (stepEntity: MJAIAgentRunStepEntityExtended) => voidProtectedrecordProtectedCreates and immediately finalizes the AIAgentRunStep (StepType='Skill') that records this
skill activation for observability/audit. The step's Skills column carries the single
AgentSkillInvocation performed (activation type, provenance of authority, and the
agent-stated reason when self-activated). Activation is not itself a failure mode today — it
always finalizes as successful — but subclasses can override to add richer InputData/OutputData
or to make activation conditionally fail (e.g. a licensing check).
Optionalinvocation: AgentSkillInvocationProtectedrecoveryProtectedRecovery Strategy 3: Aggressively compact ALL tool-result messages. Used when gentler strategies haven't freed enough space.
Agent execution parameters
Target number of tokens to free
Result with tokens saved and strategy description
ProtectedrecoveryProtectedRecovery Strategy 2: Compact old tool-result messages. Uses smart trimming to reduce size while preserving some content.
Agent execution parameters
Target number of tokens to free
Current turn/step number
OptionalminAge: numberMinimum age in turns for compaction (default: 3)
Result with tokens saved and strategy description
ProtectedrecoveryProtectedRecovery Strategy 1: Remove oldest tool-result messages. Targets messages older than minAge turns for removal.
Agent execution parameters
Target number of tokens to free
Current turn/step number
OptionalminAge: numberMinimum age in turns for removal (default: 5)
Result with tokens saved and strategy description
ProtectedrecoveryProtectedRecovery Strategy 4: Preserve beginning of last user message (fallback). Keeps the first 200-300 tokens of the user's request (which usually contains the core ask) and adds a clear marker that content was trimmed due to context limits.
Agent execution parameters
Target number of tokens to free
Result with tokens saved and strategy description
ProtectedresolveProtectedResolves the effective context budget for cross-turn compaction, validated against the most recent prompt's model when available. Logs the clamp warning once when a configured budget exceeded the model's MaxInputTokens.
ProtectedresolveResolves multiple {{expression}} placeholders embedded within a literal string.
Unlike the single-variable path (which can return objects/numbers), this always returns a string because the resolved values are interpolated back into surrounding text.
ProtectedresolveProtectedResolves whether Plan Mode is active for this run, and whether its approval gate is already
satisfied. Called once from initializeAgentRun, after _depth is set.
active: this is a root agent (_depth === 0) AND either agent.RequirePlanMode
(mandatory HITL — forces plan mode on every root run regardless of the per-request flag;
SupportsPlanMode is irrelevant when set) OR agent.SupportsPlanMode (capability,
default ON/opt-out) AND params.planMode (per-request, default OFF). Sub-agents never
gate on Plan Mode — only the top-level agent the user/caller invoked does.approved: only meaningful when active. True when params.lastRunId points to a prior
run whose Plan step's MJ: AI Agent Requests row resolved to Approved or Responded
(a Rejected plan — or no matching request at all — leaves the gate unsatisfied, sending
the agent back to present a revised plan).Override to change Plan Mode eligibility rules (e.g. gate on a specific agent category).
ProtectedresolveResolves the agent's EFFECTIVE realtime configuration — the agent TYPE's
DefaultConfiguration (base layer) deep-merged with the agent's TypeConfiguration
(per-agent layer; the server-bridged path has no runtime-override layer). Tolerant:
malformed layers contribute nothing and an unloaded type cache yields no type defaults.
See realtime/realtime-coagent-config.ts for the merge contract.
The session-driven (Realtime) agent.
The normalized effective configuration (possibly empty, never null).
ProtectedresolveResolves the realtime model + vendor driver + API key for a session-driven run.
Overridable seam. This is the single injection point that test subclasses override to return a mock BaseRealtimeModel, so executeRealtimeSession can be exercised without provider SDKs or DB metadata.
Production resolution: pick the highest-power active model of AIModelType Realtime; then
pick its highest-priority active vendor whose DriverClass has a resolvable API key; then
instantiate the driver via the ClassFactory. Returns null (never throws) if any step
can't be satisfied — the caller turns that into a clean FAILED result. (Per-agent realtime
model preference can later be wired through the agent's prompt-model config, the same path
loop agents use for ModelSelectionMode; the AI Agent entity has no direct model FK.)
The execution parameters (for the agent + context user).
OptionaloverrideModelID: stringThe resolved model instance plus its model/vendor identifiers, or null.
ProtectedresolveProtectedResolves the LLM's requested skill names to MJ: AI Skills entities, restricted to what
AIEngine.GetSkillsForAgent allows for this agent (the AcceptsSkills gate + Status
chain). Names that don't resolve are silently dropped here — validateSkillNextStep
is responsible for rejecting unknown/disallowed names before execution ever reaches this
point, so by the time executeSkillStep runs, every requested name is expected to match.
Override to change resolution semantics (e.g. resolve by ID instead of Name).
OptionalcontextUser: UserInfoProtectedresolveProtectedFinds a sub-agent by name, checking child agents (ParentID) first, then
related agents (AgentRelationships). Returns undefined when the name
doesn't resolve to an active agent reachable from params.agent.
Used by both the single and parallel sub-agent dispatch paths so name resolution is consistent and there's one place to fix lookup bugs.
Exposed as protected so driver sub-classes with custom routing logic
still resolve names through the same case-insensitive child-then-related
lookup the framework uses internally.
ProtectedresolveGeneric template resolver for loop iterations - extracts from LoopAgentType to make available to all agent types Resolves templates like {{item.field}}, {{index}}, etc. in action parameters and sub-agent payloads
ProtectedresolveResolves a value from context using variable references - extracts from LoopAgentType
This method handles both scalar and complex object iterations in ForEach/While loops.
Examples:
Scalar array iteration: candidateEntities = ["Users", "Companies", "Invoices"] itemVariable = "entityName" Template: "{{entityName}}" → resolves to "Users", then "Companies", then "Invoices"
Object array iteration: users = [{name: "Alice", age: 30}, {name: "Bob", age: 25}] itemVariable = "user" Template: "{{user.name}}" → resolves to "Alice", then "Bob" Template: "{{user}}" → resolves to entire object {name: "Alice", age: 30}
ProtectedrunProtectedRuns one compaction pass and records it as a StepType='Compaction' run step —
TargetID = the summary prompt, TargetLogID = the summary AIPromptRun (the same ID
written to ConversationDetail.SummaryPromptRunID, closing the lineage chain).
Quiet no-ops (window under trigger) record no step; fired passes and failures do.
ProtectedserializeProtectedSerializes payload for logging to PayloadAtEnd Override in subclasses to customize logging behavior (e.g., summarize large payloads)
The payload to serialize
Serialized string or null to skip logging
ProtectedserializeProtectedSerializes payload for logging to PayloadAtStart Override in subclasses to customize logging behavior (e.g., summarize large payloads)
The payload to serialize
Serialized string or null to skip logging
ProtectedsmartProtectedSmart trimming of message content based on detected format. Attempts to preserve structure while reducing size.
The message content to trim (must be string)
OptionalmaxLength: numberMaximum length for the trimmed content
Object with trimmed content and strategy used
Opens a raw IRealtimeSession for this agent — the duplex model connection a Realtime
Bridge hands to AIBridgeEngine.StartBridgeSession so the agent can talk + hear over a media
transport (a LiveKit room, a Zoom/Teams meeting, a phone call). The bridge engine owns turn-taking
and the transport seam, so this deliberately returns the session itself, NOT a
RealtimeSessionRunner (which is the client-direct topology's own orchestration loop).
It reuses the EXACT same resolution + assembly as executeRealtimeSession — model selection
(resolveRealtimeModel), agent configuration (loadAgentConfiguration), effective-config
persona/voice (resolveRealtimeEffectiveConfig), and the system-prompt + memory context
(buildRealtimeSessionParams) — then opens the session via
BaseRealtimeModel.StartSession. Tools are intentionally NOT pre-populated: the
invoke-target-agent + interactive-surface tools are a runner concern; a bridge that needs them
registers them on the returned session itself.
The execution parameters (agent + context user + the request-scoped provider). A fresh
bridge session typically passes an empty conversationMessages array.
The live realtime session.
ProtectedstartProtectedPost-turn hook (the primary path), called from finalizeAgentRun after the run row is saved. Fire-and-forget by design: the caller's completion event never waits on the summary LLM call. Fires for settled root runs with a conversation — settledRunStatuses: 'Completed' AND 'AwaitingFeedback', because a Chat final step (→ AwaitingFeedback) is the NORMAL ending of a conversational turn; gating on 'Completed' alone silently disabled post-turn compaction for exactly the long-chat scenario this feature targets.
ProtectedtryProtectedExtracts the vendor-specific MaxInputTokens from model selection info, returning null when it genuinely cannot be determined. Callers that need a hard number use getModelContextLimit (which falls back to a conservative default); callers for whom a guessed default would be WRONG — e.g. cross-turn compaction budget clamping, where a bogus 8000 would clamp a configured 200k budget — use this and handle null explicitly.
ProtectedupdateProtectedUpdates the compaction step with execution results and token usage.
The run step to update
The prompt execution result
The message that was compacted
Agent execution parameters
ProtectedvalidateProtectedvalidateValidates that action IDs exist in the database
ProtectedvalidateValidates that the actions next step is valid and can be executed by the current agent. Subclasses can override this method to implement custom validation logic if needed.
ProtectedvalidateProtectedValidates that the agent is active and ready for execution.
The agent to validate
Error result if validation fails, null if valid
ProtectedvalidateValidates an AgentSpec object Returns errors array and potentially a corrected spec
ProtectedvalidateValidates that the Chat next step is valid and can be executed by the current agent. Implements ChatHandlingOption remapping logic - if the agent has ChatHandlingOption set, the Chat step is remapped to the specified value (Success, Fail, or Retry). Subclasses can override this method to implement custom validation logic if needed.
ProtectedvalidateValidates that the Failed next step is valid and can be executed by the current agent. Subclasses can override this method to implement custom validation logic if needed.
ProtectedvalidateValidates if the next step is valid, or not. If the next step is invalid, it returns a retry step with an error message that can be processed by the agent via a retry prompt to attempt to correct the issue. Alternatively, subclasses can handle this scenario differently as desired.
The BaseAgent class implements checking for sub-agents and actions to ensure that the next step is valid in the context of the current agent. If the next step is a sub-agent, it checks if the sub-agent is active and available for execution. If the next step is actions, it checks if the actions are valid and available for execution.
ProtectedvalidateProtectedValidates that a 'Plan' next step (Plan Mode) has plan text to present. Subclasses can override to add additional plan-quality checks (e.g. minimum length, required sections).
ProtectedvalidateValidates that the Retry next step is valid and can be executed by the current agent. Subclasses can override this method to implement custom validation logic if needed. The retry step is typically used to handle cases where the agent needs to re-attempt a step due to an error or invalid state.
ProtectedvalidateProtectedValidates that the requested skill(s) are known and allowed for this agent (resolved via AIEngine.GetSkillsForAgent, which enforces the agent's AcceptsSkills gate + the catalog/grant Status chain). Subclasses can override to implement custom validation logic.
Mirrors validateActionsNextStep's fuzzy-name-matching UX: an exact case-insensitive match is tried first, falling back to a CONTAINS match when exactly one candidate matches.
ProtectedvalidateProtectedvalidateProtectedvalidateValidates SubAgent structure recursively
ProtectedvalidateValidates that the Success next step is valid and can be executed by the current agent. Subclasses can override this method to implement custom validation logic if needed.
StaticBuildRenders prior-turn tool-result steps into the carried-forward message body.
Pure and static for testability: keeps only steps whose OutputData satisfies the
structured contract stamped by the tool executors — a carry-forward-eligible
toolFamily (see CarryForwardToolFamilies) AND a non-empty tool name.
Tolerant of missing/invalid OutputData JSON, keeps only successful results,
caps each result and the total under maxChars (adding an explicit truncation
note when results are dropped). Returns null when nothing usable remains.
Architect Agent - Transforms technical design into validated AgentSpec JSON
This agent creates AgentSpec objects from technical designs and validates them before passing to the Builder Agent for persistence. It overrides validateSuccessNextStep to ensure the generated AgentSpec is valid before proceeding.
Key responsibilities: