Protected_Protected_Protected_ReadonlyAttemptsReadonlyRetryProtectedDefaultThe category logError records when the caller passes none. Override it so a runner's uncategorized errors are attributed to that runner rather than to the base.
ProtectedDefaultEngine-level default model-call timeout, in milliseconds, applied when the caller supplies no
AIPromptParams.timeoutMS. undefined (the default) means NO implicit bound — a prompt run
with neither a timeout nor a cancellation token stays unbounded, exactly as before, so this
change is behavior-preserving for existing callers.
Subclasses (or a host application's runner subclass) can override this to impose a global safety ceiling on every prompt call.
Optional metadata provider override. Callers should set
instance.Provider = providerToUse before invoking run methods
in multi-provider contexts. Falls back to the global default provider when unset.
Decisions run only on Decision-type models.
ProtectedApplyOptionalsuggestedDelaySeconds: numberProtectedApplyRecords one call's usage on its run row, for runners whose calls are not chat calls: tokens;
continuous units (seconds, characters, images) with the MJ: AI Usage Types row that names
their measure; and a cost only when the driver gave one. With no usage nothing is recorded, so
the cost stays empty and the row's save prices it from the model's cost rows, or declines to.
Call it from the FinalizeRunRecord callback.
ProtectedAssertAsserts that the prompt's configured model type matches the runner's required model type.
If prompt.AIModelTypeID is null or undefined, does not throw (the runner's required type applies).
If prompt.AIModelTypeID is set and does not match RequiredModelTypeID, throws a descriptive
error naming the prompt, the prompt's configured type (resolved to name if possible), and the runner's
required type.
ProtectedBuildThe error for a model call the caller cancelled. It is classified Cancelled — Fatal and not
eligible for failover — so ErrorAnalyzer, the failover loop and the validation retries all stop
on it rather than treating it as an unknown model error.
Optionalmessage: stringThe message to keep, when a driver already reported the cancellation.
ProtectedBuildBuilds a unified, ordered list of model-vendor candidates based on all selection criteria. Uses a 3-phase approach to properly handle SelectionStrategy='Specific' with AIPromptModel priorities.
Phase 1: Handle explicit model ID (highest priority) Phase 2: Check if SelectionStrategy='Specific' with AIPromptModel entries - use ONLY those with AIPromptModel priorities Phase 3: Use general selection strategy (fallback) - blended priorities from legacy behavior
The AI prompt with selection criteria
OptionalexplicitModelId: stringExplicitly specified model ID (highest priority)
OptionalconfigurationId: stringConfiguration ID for filtering
OptionalpreferredVendorId: stringPreferred vendor ID
Optionalverbose: booleanOrdered array of model-vendor candidates (highest priority first)
ProtectedcalculateCalculates the delay before the next failover attempt.
The current attempt number (1-based)
The base delay in seconds from configuration
Delay in milliseconds before the next attempt
The candidates the runner builds for prompt, highest priority first.
Optionalparams: AIDecisionParamsProtectedcreateComposes the caller-supplied cancellation token with the resolved model-call timeout into a single AbortSignal that bounds one model call. NEITHER bound is discarded:
Signal is undefined and the call runs unbounded (legacy behavior)Implemented with an AbortController + relay listener rather than AbortSignal.any() so it works
on Node 18 (where AbortSignal.any does not exist — it landed in Node 20.3).
OptionalcancellationToken: AbortSignalProtectedcreateThe result returned when every failover candidate has failed.
ProtectedCreateCreates the MJ: AI Prompt Runs record for one model call, fills the fields every runner shares,
lets the subclass add its own request fields, then queues the INSERT (fire-and-forget).
For prompt-based runners: the record is always tied to an AIPrompt and its AIPromptParams.
OptionalvendorId: stringOptionalmodelSelectionInfo: AIModelSelectionInfoOptionalapplyRequestFields: (promptRun: MJAIPromptRunEntityExtended) => voidSets the runner's own request columns. It runs once, after the shared
fields are set and before the INSERT is queued, so everything it sets is on the inserted row.
It must be synchronous. An async callback type-checks against this signature, but any
field it sets after its first await races the INSERT and can miss the row.
ProtectedDriverWhether a driver needs its own API key. LLMDecision does not: it runs an MJ prompt whose chat
models resolve their own credentials, as LLMReranker does.
Answers the questions in params.Questions about the state. Never throws: every failure is a
result with success: false and an errorMessage.
ProtectedExecuteRuns one model call across the failover candidates, in priority order: skips candidates with no credentials, lets processFailoverError decide retry / next candidate / stop, and records failover success or failure on the prompt run. The model call itself and the final error result are supplied by the subclass, so the loop works for any result type that extends BaseResult.
For prompt-based runners: it takes the AIPrompt and AIPromptParams the call is for, and
candidates that carry prompt-model fields (effort level, the AIPromptModel configuration).
Makes the call on one candidate. It must use the candidate's own model, vendor, driver and prompt-model fields, not those of the first candidate.
Builds the result returned when every candidate has failed.
OptionalpromptRun: MJAIPromptRunEntityExtendedOptionalcredentialAvailability: Map<string, boolean>ProtectedFinalizeFinalizes a prompt-run record in memory immediately AND queues a fire-and-forget UPDATE on
_promptRunQueue that re-applies the finalized state after any in-flight 'Running' INSERT
and its finalizeSave reload complete.
Applying the completion fields synchronously in memory ensures callers that inspect
result.promptRun right after ExecutePrompt (such as BaseAgent.calculateTokenStats() reading
TokensPromptRollup, TokensCompletionRollup, TotalCost, and Status) see the finalized values
immediately without blocking on the database round-trip, while re-applying the captured snapshot
inside _promptRunQueue.Update(promptRun, ...) guarantees an in-flight INSERT reload can never
revert the persisted row back to 'Running'.
In order, it sets: the completion timing (CompletedAt, ExecutionTimeMS); the outcome (Success,
and Status = 'Completed' or 'Failed' from it); any pending failover fields; the subclass's
result fields (applyResultFields, invoked once); and finally the token rollups and TotalCost.
Whether the call succeeded, as the runner judges it (the chat runner also requires its output to pass validation).
Sets the runner's own result columns. It can read the timing and outcome
already set, and may refine them (for example ErrorDetails or Status = 'Cancelled').
It must be synchronous.
Awaits all in-flight MJAIPromptRunEntityExtended INSERT and UPDATE operations queued on this
runner's internal BaseEntitySaveQueue (_promptRunQueue) and clears the in-flight parent
lookup table.
Normal intra-step prompt execution is non-blocking (fire-and-forget); callers that own the outer
lifecycle (such as BaseAgent at run completion, failure, cancellation, or compaction) call
FlushPendingSaves() to guarantee every AIPromptRun row is durably settled before returning.
ProtectedgetResolves the per-model-call timeout: the caller's AIPromptParams.timeoutMS, else the runner's
DefaultPromptTimeoutMS. Non-positive / non-numeric values mean "no timeout".
The bound is applied PER MODEL CALL (not per prompt execution), which mirrors the parallel
path's existing taskTimeoutMS semantics: each failover candidate / validation retry gets a
fresh budget rather than sharing one wall-clock window.
NOTE (issue #3064): there is deliberately NO prompt-entity source here yet — the AIPrompt
table has no TimeoutMS column today. Once a migration adds one and CodeGen regenerates the
entity, this becomes prompt.TimeoutMS ?? params.timeoutMS ?? this.DefaultPromptTimeoutMS
and every bound below starts honoring the per-prompt configuration with no other change.
ProtectedgetRetrieves failover configuration from the prompt entity.
The AI prompt entity containing failover settings
FailoverConfiguration object with strategy and settings
Whether selection would find a credential for candidate.
Optionalparams: AIDecisionParamsProtectedHasTreats a driver that needs no credentials as always available.
Optionalparams: AIPromptParamsProtectedIsWhether a failure was caused by the caller cancelling the request: its classification is
Cancelled, or the older shape drivers built before that type existed.
ProtectedIsChecks if a model vendor is configured as an inference provider. Delegates to the memoized AIEngine.IsInferenceProvider helper so the "Inference Provider" vendor-type lookup happens once per engine load rather than on every candidate in every selection pass.
The model vendor to check
true if the vendor is an inference provider
ProtectedlogHelper method for enhanced error logging with metadata
Optionaloptions: {ProtectedlogLogs a failover attempt for tracking and debugging.
The ID of the prompt being executed
The failover attempt details
Whether another attempt will be made
ProtectedlogInternal logging helper that wraps LogStatusEx with verbose control
The message to log
OptionalverboseOnly: booleanWhether this is a verbose-only message
Optionalparams: AIPromptParamsOptional prompt parameters for custom verbose check
ProtectedRaceWaits for a model call, or rejects as soon as its bound aborts: with the bound's AIPromptTimeoutError when the timeout fired, else with a cancellation error. The call the runner stops waiting for is torn down only if its driver was handed the bound's signal.
For any runner whose driver call is not already bounded by the driver itself.
ProtectedRequiredResolves the runner's RequiredModelType name against AIEngine.Instance.ModelTypes
(case-insensitive, trimmed) and returns its UUID. Callers compare models' AIModelTypeID with it
using UUIDsEqual.
Throws a descriptive error if the model type is not found in the engine metadata.
ProtectedResolveResolves credentials for AI model execution using a hierarchical resolution system.
Resolution priority (highest to lowest):
IMPORTANT: When ANY credential ID is found (priorities 1-4), the system uses the Credentials path and ignores legacy methods (priorities 5-6).
Each tier is consulted only if CredentialScopeAllows the run's
AIPromptParams.CredentialScope to use its source. Under 'RuntimeOnly' that leaves
priority 1 and the apiKeys entry for this driver class; bindings, the vendor default and the
environment are skipped.
The driver class name (e.g., 'OpenAILLM')
The prompt ID for looking up AIPromptModel credentials
The model ID for looking up AIPromptModel and AIModelVendor credentials
The vendor ID for looking up AIModelVendor and AIVendor credentials
The prompt execution parameters containing contextUser and optional credentialId
The API key/configuration string to pass to the LLM constructor
ProtectedResolveThe usage to record for one call: the driver's when it reports a quantity (tokens, or units in a measure), otherwise the runner's own count, which keeps any cost the driver gave. With neither, the driver's usage (possibly undefined) is returned, and nothing is invented.
A runner counts what it can see without the driver: the images returned, or the characters it sent. The driver's figure wins because it is the vendor's own measure.
The usage the driver returned, if any.
The runner's own count as ModelUsage.ForMedia, or undefined when it has none.
ProtectedselectSelects candidate models for failover based on the strategy and current failure.
The model that just failed
The vendor ID that just failed
The failover strategy to use
The model selection preference
All available model-vendor candidates
History of previous failover attempts
Array of candidates sorted by priority (highest first)
This method implements different strategies for selecting failover candidates:
Override this method to implement custom candidate selection logic.
ProtectedshouldDetermines whether a failover attempt should be made based on the error and configuration.
The error that occurred during execution
The failover configuration
The current attempt number (1-based)
True if failover should be attempted, false otherwise
ProtectedupdateUpdates prompt run with successful failover tracking data
Awaits all in-flight prompt-run saves queued by this runner instance. The normal execution path does NOT call this — prompt-run persistence is intentionally fire-and-forget. Exposed for tests and for callers that need the AIPromptRun rows durably written before proceeding.
The real
AIDecisionRunner, with three of its protected seams opened for the checks:MemoryGateProbeexposes the Memory Manager's internals;