Decisions
A decision evaluates named, typed questions against caller-supplied context.
ThreadState.decide() returns the answers without advancing the conversation.
For the rest of the ThreadState surface, see Threads.
1. Thread Interface
interface DecideOptions<Q extends DecisionQuestions = DecisionQuestions>
extends DecisionRequest<Q> {
provider: DecisionProviderFactory;
}
interface ThreadState {
decide<const Q extends DecisionQuestions>(
options: DecideOptions<Q>
): Promise<DecisionResult<Q>>;
}
The provider property identifies the factory used for the invocation. A
decision does not require a conversational model definition.
decide() MUST be available both during active execution and when
state.execution is null.
The decide(state, options) helper exported by @standardagents/spec is
equivalent to state.decide(options).
2. Request Types
type DecisionJson =
| string
| number
| boolean
| null
| DecisionJson[]
| { [key: string]: DecisionJson };
type DecisionContext =
| string
| DecisionJson[]
| { [key: string]: DecisionJson };
interface DecisionRequest<Q extends DecisionQuestions = DecisionQuestions> {
model: string;
context: DecisionContext;
questions: Q;
signal?: AbortSignal;
}
| Property | Semantics |
|---|---|
model | Provider model identifier for this invocation. |
context | Explicit context shared by the questions. Structured values MUST retain their JSON structure. |
questions | Map of question identifiers to question definitions. Each identifier associates a question with its answer. |
signal | Optional cancellation signal for the request. |
Question definitions are invocation inputs. They are not provider connection configuration.
3. Question Types
type DecisionQuestion =
| {
type: 'choice';
instructions: DecisionContext;
options: Record<string, DecisionContext | null>;
}
| {
type: 'score';
instructions: DecisionContext;
levels: DecisionContext[];
}
| {
type: 'boolean';
instructions: DecisionContext;
criteria?: { true?: DecisionContext; false?: DecisionContext };
};
type DecisionQuestions = Record<string, DecisionQuestion>;
instructions defines the evaluation to perform. Structured instructions,
option descriptions, levels, and criteria MUST retain their JSON structure.
| Type | Semantics |
|---|---|
choice | Select an outcome from options. Each key identifies an outcome; its value describes that outcome. A null value supplies no additional description. |
score | Evaluate against the ordered levels rubric. Level indices start at zero; array order defines the scale. |
boolean | Estimate the probability that the condition in instructions is true. Optional criteria.true and criteria.false describe the two outcomes. |
Providers MUST preserve these semantics when translating requests. A provider MUST reject inputs it cannot represent rather than silently omit or reinterpret them. Question-count and context-size limits are provider-defined.
4. Result Types
interface DecisionChoiceAnswer {
type: 'choice';
choice: string;
probabilities: Record<string, number>;
confidence?: number;
}
interface DecisionScoreAnswer {
type: 'score';
score: number;
probabilities: Record<string, number>;
legend: Record<string, DecisionContext>;
confidence?: number;
}
interface DecisionBooleanAnswer {
type: 'boolean';
probability: number;
}
type DecisionAnswer =
| DecisionChoiceAnswer
| DecisionScoreAnswer
| DecisionBooleanAnswer;
type DecisionAnswers<Q extends DecisionQuestions> = {
[K in keyof Q]: Q[K] extends { type: 'choice' } ? DecisionChoiceAnswer
: Q[K] extends { type: 'score' } ? DecisionScoreAnswer
: Q[K] extends { type: 'boolean' } ? DecisionBooleanAnswer
: DecisionAnswer;
};
interface DecisionResult<Q extends DecisionQuestions = DecisionQuestions> {
model: string;
answers: DecisionAnswers<Q>;
usage: ProviderUsage;
metadata?: Record<string, unknown>;
}
| Property | Semantics |
|---|---|
model | The model that performed the evaluation, including the resolved version when the request uses an alias. |
answers | Answers keyed by request question identifier, with an answer type corresponding to each question type. |
usage | Standard ProviderUsage: token counts and optional cost and pricing fields. |
metadata | Optional provider-specific result information. |
4.1 Answer Semantics
choiceidentifies the selected option. Itsprobabilitiesmap associates option identifiers with their probabilities.scoreis the probability-weighted value of the zero-based level indices. It MAY be fractional. Itsprobabilitiesmap associates level indices with probabilities, andlegendassociates those indices with rubric descriptions.probabilityon a boolean answer is P(true).- Probabilities and
confidencevalues are numbers in [0, 1]. Confidence is provider-reported and is distinct from an individual outcome probability.
Providers MUST preserve complete probability distributions, score legends,
and available confidence values. Provider rounding information MAY be
included in metadata. Rounded probabilities MUST NOT be silently
renormalized.
Providers and runtimes MUST NOT coerce a boolean probability to a boolean or apply an application threshold. Thresholds and subsequent actions are caller-defined.
5. Provider Interface
interface DecisionProviderInterface {
readonly name: string;
readonly specificationVersion: '1';
readonly kind: 'decision';
decide<const Q extends DecisionQuestions>(
request: DecisionRequest<Q>
): Promise<DecisionResult<Q>>;
getModels?(filter?: string): Promise<ProviderModelInfo[]>;
getIcon?(modelId?: string): string | undefined;
}
interface DecisionProviderFactory {
(config: ProviderFactoryConfig): DecisionProviderInterface;
configSlots?: ProviderConfigSlots;
}
A decision provider MUST implement decide() and declare
kind: 'decision'. Conversational generate() and stream() methods are
not required by this interface.
Factories receive standard ProviderFactoryConfig. Transport overrides and
credential isolation follow the Provider specification.
The runtime resolves thread-scoped credentials and provider connection
configuration. User- or thread-scoped destination overrides MUST NOT
redirect inherited credentials.
6. Conversation Semantics
Invoking decide() MUST NOT implicitly:
- read conversation messages to assemble context;
- add, modify, or remove conversation messages;
- execute tools;
- start a conversational turn.
Adding an answer to conversation history requires a separate call to a message API.