API Reference
The dialai library provides functions for creating sessions, registering specialists, and managing the decision cycle. For the conceptual foundation, see Concepts.
Quick Reference
| Function | Purpose |
|---|---|
createSession | Create a new session instance |
getSession | Retrieve a session by ID |
getSessions | List all sessions |
registerProposer | Register a proposer specialist |
submitProposal | Submit a state transition proposal |
registerArbiter | Register an arbiter specialist |
submitArbitration | Evaluate consensus and optionally execute |
evaluateConsensus | Check if consensus is reached |
executeTransition | Execute a state transition |
runSession | Run a machine to completion |
tick | Global heartbeat — sweep all active sessions |
getProposers | List proposers for a machine |
getArbiter | Get arbiter for a machine |
enableSpecialist | Re-enable a disabled specialist |
disableSpecialist | Disable a specialist (preserves history) |
getCollapseMetrics | Progressive collapse monitoring |
getProposalsForRound | List proposals in a round |
Session Functions
createSession
Creates a new session instance from a machine definition.
import { createSession } from "dialai";
const session = await createSession(machine);
// Or with metadata:
// const session = await createSession(machine, { puzzleSize: 3 });
// session.sessionId → "a1b2c3d4-..."
// session.currentState → machine.initialState
// session.history → []
Signature:
createSession(machine: MachineDefinition, metaJson?: Record<string, unknown>): Promise<Session>
getSession
Retrieves a session by its ID.
import { getSession } from "dialai";
const session = await getSession("a1b2c3d4-...");
Signature:
getSession(sessionId: string): Promise<Session>
Throws if the session is not found.
getSessions
Returns all stored sessions.
import { getSessions } from "dialai";
const sessions = await getSessions();
Signature:
getSessions(): Promise<Session[]>
Specialist Functions
registerProposer
Registers a proposer specialist for a machine.
import { registerProposer } from "dialai";
const proposer = await registerProposer({
specialistId: "ai-proposer-1",
machineName: "my-task",
strategyFn: async (ctx) => ({
transitionName: Object.keys(ctx.transitions)[0],
toState: Object.values(ctx.transitions)[0],
reasoning: "First available transition",
}),
});
Signature:
registerProposer(opts: {
specialistId: string;
machineName: string;
isHuman?: boolean;
strategyFn?: (ctx: ProposerContext) => Promise<ProposerStrategyResult>;
strategyFnName?: string;
strategyWebhookUrl?: string;
contextFn?: (ctx: ProposerContext) => Promise<string>;
contextWebhookUrl?: string;
modelId?: string;
webhookTokenName?: string;
threshold?: number;
}): Promise<Proposer>
See Registering Specialists for execution mode details.
Human Specialists:
// Register a human specialist
const humanReviewer = await registerProposer({
specialistId: "human-reviewer",
machineName: "my-task",
isHuman: true, // Enables forced arbitration
strategyFnName: "firstAvailable", // Execution mode still required
});
Human specialists are identified by isHuman: true, which grants:
- Human proposals always win consensus
- Ability to force transitions via
submitArbitration
An execution mode is still required at registration. Use strategyFnName as a fallback, or provide a strategyFn encoding human preferences. When submitting proposals directly with transitionName, the strategy is bypassed.
registerArbiter
Registers an arbiter specialist for a machine. Arbiters evaluate consensus among proposals.
import { registerArbiter } from "dialai";
const arbiter = await registerArbiter({
specialistId: "consensus-arbiter",
machineName: "my-task",
strategyFnName: "alignmentMargin",
threshold: 0.5,
});
Signature:
registerArbiter(opts: {
specialistId: string;
machineName: string;
strategyFn?: (ctx: ArbiterContext) => Promise<ArbiterStrategyResult>;
strategyFnName?: string; // "alignmentMargin"
strategyWebhookUrl?: string;
webhookTokenName?: string;
threshold?: number;
}): Promise<Arbiter>
See registerArbiter for full documentation including built-in strategies.
ArbiterContext:
The context passed to a custom arbiter strategy function:
interface ArbiterContext {
sessionId: string;
roundId: string;
currentState: string;
prompt: string;
machineName: string;
proposals: Proposal[];
alignmentScores?: Record<string, number>;
humanGoldExamples?: HumanGoldExample[];
history: TransitionRecord[];
threshold?: number;
metaJson?: Record<string, unknown>;
}
Example custom arbiter using alignment margin logic on proposals:
const arbiter = await registerArbiter({
specialistId: "custom-arbiter",
machineName: "my-task",
strategyFn: async (ctx) => {
// Count proposals per transition
const counts: Record<string, number> = {};
for (const p of ctx.proposals) {
counts[p.transitionName] = (counts[p.transitionName] || 0) + 1;
}
const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
const leader = sorted[0];
const runnerUp = sorted[1]?.[1] ?? 0;
if (leader[1] - runnerUp >= ctx.threshold) {
const winning = ctx.proposals.find(p => p.transitionName === leader[0]);
return {
consensusReached: true,
winningProposalId: winning!.proposalId,
reasoning: `${leader[0]} ahead by ${leader[1] - runnerUp}`,
};
}
return { consensusReached: false, reasoning: "No transition ahead by threshold" };
},
threshold: 2,
});
Decision Functions
submitProposal
Creates and stores a proposal. If transitionName is omitted, invokes the specialist's registered strategy.
import { submitProposal } from "dialai";
// Strategy invocation (AI specialists)
const proposal = await submitProposal({
sessionId: session.sessionId,
specialistId: "ai-proposer-1",
roundId: session.currentRoundId, // omit to use current round
});
// Direct submission with all parameters
const proposal = await submitProposal({
sessionId: session.sessionId,
specialistId: "ai-proposer-1",
roundId: session.currentRoundId,
transitionName: "approve",
reasoning: "Looks good to me",
metaJson: { source: "review" },
costUSD: 0.003,
latencyMsec: 200,
numInputTokens: 150,
numOutputTokens: 50,
});
Signature:
submitProposal(opts: SubmitProposalOptions): Promise<Proposal>
SubmitProposalOptions:
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | Session identifier |
specialistId | string | Yes | Who is submitting |
roundId | string | No | Omit to use current round; provide to target a specific round (enables staleness detection) |
transitionName | string | No | Transition to propose (invokes strategy if omitted) |
reasoning | string | No | Explanation for the proposal |
metaJson | object | No | Arbitrary client metadata |
costUSD | number | No | Cost in USD to generate this proposal |
latencyMsec | number | No | Time in milliseconds to generate |
numInputTokens | number | No | Input tokens used |
numOutputTokens | number | No | Output tokens used |
Cost tracking fields enable measuring the economic cost of AI delegation.
evaluateConsensus
Evaluates whether consensus has been reached for a session.
import { evaluateConsensus } from "dialai";
const result = await evaluateConsensus(session.sessionId);
if (result.consensusReached) {
console.log("Winner:", result.winningProposalId);
} else {
console.log("No consensus:", result.reasoning);
}
Signature:
evaluateConsensus(sessionId: string): Promise<ConsensusResult>
ConsensusResult:
interface ConsensusResult {
consensusReached: boolean;
winningProposalId?: string;
reasoning: string;
}
submitArbitration
Evaluates consensus and optionally executes the winning transition. Combines consensus evaluation with transition execution in a single call. Supports human override for forcing transitions.
import { submitArbitration } from "dialai";
// Check for consensus and auto-execute if found
const result = await submitArbitration({
sessionId: session.sessionId,
});
if (result.executed) {
console.log("Transitioned to:", result.toState);
} else {
console.log("No consensus:", result.guardReason);
}
// Human override with cost tracking
const result = await submitArbitration({
sessionId: session.sessionId,
specialistId: "human-reviewer",
transitionName: "approve",
reasoning: "Manager approved",
metaJson: { approvedBy: "jane" },
costUSD: 0.0,
latencyMsec: 5000,
numInputTokens: 0,
numOutputTokens: 0,
});
Signature:
submitArbitration(opts: SubmitArbitrationOptions): Promise<ArbitrationResult>
SubmitArbitrationOptions:
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | Session identifier |
roundId | string | No | Omit to use current round; if provided, enables staleness detection |
specialistId | string | No | Who is calling (required for override) |
transitionName | string | No | Force this transition (human only) |
reasoning | string | No | Explanation for the decision |
metaJson | object | No | Arbitrary client metadata |
costUSD | number | No | Cost in USD for this arbitration |
latencyMsec | number | No | Time in milliseconds |
numInputTokens | number | No | Input tokens used |
numOutputTokens | number | No | Output tokens used |
See submitArbitration for full documentation.
executeTransition
Executes a state transition, updating the session and recording history.
import { executeTransition } from "dialai";
const updated = await executeTransition(
session.sessionId,
"approve", // transitionName
"approved", // toState
"Consensus reached" // reasoning
);
console.log(updated.currentState); // "approved"
console.log(updated.history); // [..., { transitionName, reasoning, ... }]
Signature:
executeTransition(
sessionId: string,
transitionName: string,
toState: string,
reasoning?: string
): Promise<Session>
Throws if the transition is invalid from the current state.
Engine
runSession
Runs a machine to its goal state. Creates a session, registers a built-in proposer, and loops through the decision cycle until the goal state is reached.
import { runSession } from "dialai";
const session = await runSession(machine);
// session.currentState === machine.goalState
Signature:
runSession(machine: MachineDefinition): Promise<Session>
Behavior:
- Creates a session in the initial state
- Registers machine-level and per-state specialists from the machine definition
- Registers a default proposer (
firstAvailable) if no proposers are specified - Registers a default arbiter (
firstProposal) if no arbiter is specified - Loops
tick()until the session reaches its goal state or needs human intervention - Returns the session (completed or waiting for human)
Store
The store is accessed through exported functions:
import { getStore, setStore, clear } from "dialai";
// Access the current store (default: in-memory)
const store = getStore();
// Reset all state (useful for tests)
await clear();
// Use a custom store (e.g., PostgreSQL)
import { createPostgresStore } from "dialai/store-postgres";
setStore(createPostgresStore(pool));
| Export | Type | Description |
|---|---|---|
getStore | () => Store | Returns the current store instance |
setStore | (store: Store) => void | Replaces the store implementation |
clear | () => Promise<void> | Clears all data in the current store |
Orchestration
tick
Global heartbeat. Sweeps all active sessions, performing one atomic step per session.
import { tick } from "dialai";
const results = await tick();
for (const r of results) {
console.log(`${r.sessionId}: ${r.status} → ${r.currentState}`);
}
Signature:
tick(): Promise<TickResult[]>
Per-session behavior:
- If a proposer hasn't submitted yet → solicit that one proposer (status:
'solicited') - If all proposers submitted and consensus reached → execute transition (status:
'advanced') - If all proposers submitted but no consensus → report (status:
'needs_human') - Terminal sessions are omitted from results
Specialist Management
getProposers
Returns all proposers registered for a machine.
import { getProposers } from "dialai";
const proposers = await getProposers("my-task");
Signature:
getProposers(machineName: string): Promise<Proposer[]>
getArbiter
Returns the arbiter registered for a machine, or undefined if none.
import { getArbiter } from "dialai";
const arbiter = await getArbiter("my-task");
Signature:
getArbiter(machineName: string): Promise<Arbiter | undefined>
enableSpecialist
Re-enables a previously disabled specialist.
import { enableSpecialist } from "dialai";
await enableSpecialist("ai-proposer-1");
Signature:
enableSpecialist(specialistId: string): Promise<void>
disableSpecialist
Disables a specialist. The specialist stops receiving solicitations but its registration and alignment history are preserved.
import { disableSpecialist } from "dialai";
await disableSpecialist("ai-proposer-1");
Signature:
disableSpecialist(specialistId: string): Promise<void>
Monitoring
getCollapseMetrics
Returns progressive collapse metrics for a machine, optionally filtered by state.
import { getCollapseMetrics } from "dialai";
const metrics = await getCollapseMetrics("my-task");
console.log(`Collapse ratio: ${metrics.collapseRatio}`);
Signature:
getCollapseMetrics(machineName: string, state?: string): Promise<CollapseMetrics>
getProposalsForRound
Returns all proposals submitted in a specific round.
import { getProposalsForRound } from "dialai";
const proposals = await getProposalsForRound(session.sessionId, session.currentRoundId);
Signature:
getProposalsForRound(sessionId: string, roundId: string): Promise<Proposal[]>
Additional References
- Types Reference - Complete type definitions
- CLI Reference - Command-line interface
- Registering Specialists - Execution modes
- Implementing Strategies - Strategy functions