@elsium-ai/agents
Agent orchestration, memory, guardrails, and multi-agent patterns for ElsiumAI.
Install
npm install @elsium-ai/agents @elsium-ai/core
What’s Inside
| Category | Exports | Description |
|---|---|---|
| Agent | defineAgent, Agent, AgentDependencies |
Define and run a single agent with tools, memory, and guardrails |
| Types | AgentConfig, AgentResult, AgentRunOptions, AgentHooks, GuardrailConfig, StateDefinition, StateHistoryEntry, StateMachineResult |
Core type definitions used across the package |
| Memory | createMemory, Memory, MemoryConfig, MemoryStrategy |
Conversation history management with configurable retention strategies |
| Multi-Agent | runSequential, runParallel, runSupervisor, MultiAgentConfig |
Orchestrate multiple agents in sequence, parallel, or supervisor patterns |
| Semantic Guardrails | createSemanticValidator, SemanticGuardrailConfig, SemanticCheck, SemanticCheckResult, SemanticValidationResult, SemanticValidator |
Hallucination detection, relevance checking, and grounding validation |
| Security | createAgentSecurity, AgentSecurityConfig, AgentSecurityResult |
Prompt injection detection, jailbreak prevention, and secret redaction |
| Confidence | createConfidenceScorer, ConfidenceConfig, ConfidenceResult |
Score agent output confidence with heuristic and semantic signals |
| State Machine | executeStateMachine |
Run an agent through a finite state machine of states and transitions |
| Approval Gates | createApprovalGate, shouldRequireApproval, ApprovalRequest, ApprovalDecision, ApprovalCallback, ApprovalGateConfig, ApprovalGate |
Human-in-the-loop approval for high-stakes operations |
Agent
defineAgent
Create a new agent instance from a configuration object and a set of dependencies.
function defineAgent(config: AgentConfig, deps: AgentDependencies): Agent
| Parameter | Type | Description |
|---|---|---|
config | AgentConfig | Full agent configuration (name, system prompt, tools, guardrails, etc.) |
deps | AgentDependencies | External dependencies the agent requires (currently an LLM completion function) |
Returns: Agent
import { defineAgent } from '@elsium-ai/agents'
const agent = defineAgent(
{
name: 'assistant',
system: 'You are a helpful assistant.',
model: 'claude-sonnet-4-6',
},
{ complete: (req) => llm.complete(req) },
)
const result = await agent.run('Explain circuit breakers in distributed systems.')
console.log(result.message.content)
Reproducibility (seed)
Set AgentConfig.seed to forward a seed to every CompletionRequest the agent issues (both the tool loop and streaming). Override it per call with AgentRunOptions.seed; the per-run value falls back to the agent-level one when omitted.
const agent = defineAgent({ name: 'extractor', system: '...', seed: 42 })
const a = await agent.run('Extract the fields')
const b = await agent.run('Extract the fields', { seed: 7 }) // per-run override
Caveat: seed is only effective if the provider honors it (forwarded where supported, e.g. OpenAI and Google; absent on Anthropic). It does not, on its own, make a hosted model deterministic. Use @elsium-ai/testing’s assertDeterministic to measure run-to-run variance (it measures variance, it does not enforce it). See examples/reproducible-run.
Agent
The object returned by defineAgent. Provides methods for running the agent and managing memory.
interface Agent {
readonly name: string
readonly config: AgentConfig
run(input: string, options?: AgentRunOptions): Promise<AgentResult>
chat(messages: Message[], options?: AgentRunOptions): Promise<AgentResult>
resetMemory(): void
}
| Method | Description |
|---|---|
run(input, options?) | Run the agent with a single string input. If states and initialState are configured, the agent runs in state-machine mode. |
chat(messages, options?) | Run the agent with an array of Message objects for multi-turn conversations. |
resetMemory() | Clear the agent’s conversation memory. |
AgentDependencies
Dependencies injected into an agent at creation time.
interface AgentDependencies {
complete: (request: CompletionRequest) => Promise<LLMResponse>
}
Types
AgentConfig
Top-level configuration for creating an agent.
interface AgentConfig {
name: string
model?: string
system: string
tools?: Tool[]
memory?: MemoryConfig
guardrails?: GuardrailConfig
hooks?: AgentHooks
confidence?: boolean | ConfidenceConfig
states?: Record<string, StateDefinition>
initialState?: string
seed?: number
}
AgentResult
The result returned after an agent run completes.
interface AgentResult {
message: Message
usage: {
totalInputTokens: number
totalOutputTokens: number
totalTokens: number
totalCost: number
iterations: number
}
toolCalls: Array<{
name: string
arguments: Record<string, unknown>
result: ToolExecutionResult
}>
traceId: string
confidence?: ConfidenceResult
}
AgentRunOptions
Optional parameters passed to agent.run() or agent.chat().
interface AgentRunOptions {
signal?: AbortSignal
traceId?: string
metadata?: Record<string, unknown>
seed?: number
}
AgentHooks
Lifecycle hooks that fire at various points during the agent loop.
interface AgentHooks {
onMessage?: (message: Message) => void | Promise<void>
onToolCall?: (call: { name: string; arguments: Record<string, unknown> }) => void | Promise<void>
onToolResult?: (result: ToolExecutionResult) => void | Promise<void>
onError?: (error: Error) => void | Promise<void>
onComplete?: (result: AgentResult) => void | Promise<void>
onApprovalRequired?: ApprovalCallback
}
GuardrailConfig
Configuration for input/output validation, semantic checks, security, and approval gates.
interface GuardrailConfig {
maxIterations?: number
maxTokenBudget?: number
inputValidator?: (input: string) => boolean | string
outputValidator?: (output: string) => boolean | string
semantic?: SemanticGuardrailConfig
security?: AgentSecurityConfig
approval?: ApprovalGateConfig
}
StateDefinition
Defines a single state within a state-machine agent.
interface StateDefinition {
system?: string
tools?: Tool[]
guardrails?: GuardrailConfig
transition: (result: AgentResult) => string
terminal?: boolean
}
StateHistoryEntry
A record of a single state execution and its transition.
interface StateHistoryEntry {
state: string
result: AgentResult
transitionedTo: string | null
}
StateMachineResult
Extends AgentResult with state-machine-specific data.
interface StateMachineResult extends AgentResult {
stateHistory: StateHistoryEntry[]
finalState: string
}
Memory
createMemory
Create a memory instance that manages conversation history with a configurable retention strategy.
function createMemory(config: MemoryConfig): Memory
| Parameter | Type | Description |
|---|---|---|
config | MemoryConfig | Strategy and limits for memory retention |
Returns: Memory
import { createMemory } from '@elsium-ai/agents'
const memory = createMemory({
strategy: 'sliding-window',
maxMessages: 50,
})
memory.add({ role: 'user', content: 'Hello' })
memory.add({ role: 'assistant', content: 'Hi there!' })
console.log(memory.getMessages()) // [{ role: 'user', ... }, { role: 'assistant', ... }]
console.log(memory.getTokenEstimate()) // approximate token count
Memory
The memory interface returned by createMemory.
interface Memory {
readonly strategy: MemoryStrategy
add(message: Message): void
getMessages(): Message[]
clear(): void
getTokenEstimate(): number
}
| Method | Description |
|---|---|
add(message) | Append a message. Automatically trims based on the configured strategy. |
getMessages() | Return a copy of all stored messages. |
clear() | Remove all messages from memory. |
getTokenEstimate() | Return an approximate token count across all stored messages. |
MemoryConfig
Configuration for a memory instance.
interface MemoryConfig {
strategy: MemoryStrategy
maxTokens?: number
maxMessages?: number
}
| Field | Type | Default | Description |
|---|---|---|---|
strategy | MemoryStrategy | — | Retention strategy to use |
maxTokens | number | 128000 | Maximum estimated tokens (used by token-limited) |
maxMessages | number | 100 | Maximum number of messages (used by sliding-window) |
MemoryStrategy
The available memory retention strategies.
type MemoryStrategy = 'sliding-window' | 'token-limited' | 'unlimited'
| Strategy | Behavior |
|---|---|
sliding-window | Keeps the most recent maxMessages messages, dropping the oldest first |
token-limited | Keeps messages within maxTokens, dropping the oldest first when the budget is exceeded |
unlimited | Retains all messages without any trimming |
Multi-Agent
runSequential
Run multiple agents in sequence, piping the text output of each agent as the input to the next.
function runSequential(
agents: Agent[],
input: string,
options?: AgentRunOptions,
): Promise<AgentResult[]>
| Parameter | Type | Description |
|---|---|---|
agents | Agent[] | Ordered list of agents to run |
input | string | Initial input for the first agent |
options | AgentRunOptions | Shared run options for all agents |
Returns: Promise<AgentResult[]> — one result per agent, in order.
import { defineAgent, runSequential } from '@elsium-ai/agents'
const researcher = defineAgent(
{ name: 'researcher', system: 'Research the topic thoroughly.' },
{ complete: (req) => llm.complete(req) },
)
const writer = defineAgent(
{ name: 'writer', system: 'Write a polished article from the research.' },
{ complete: (req) => llm.complete(req) },
)
const results = await runSequential([researcher, writer], 'Quantum computing basics')
const finalArticle = results[1].message.content
runParallel
Run multiple agents concurrently on the same input. Uses Promise.allSettled internally so that partial results are returned even if some agents fail.
function runParallel(
agents: Agent[],
input: string,
options?: AgentRunOptions,
): Promise<AgentResult[]>
| Parameter | Type | Description |
|---|---|---|
agents | Agent[] | Agents to run in parallel |
input | string | Input provided to every agent |
options | AgentRunOptions | Shared run options |
Returns: Promise<AgentResult[]> — results from all agents that completed successfully. Throws the first error only if all agents fail.
import { defineAgent, runParallel } from '@elsium-ai/agents'
const optimist = defineAgent(
{ name: 'optimist', system: 'Present the optimistic perspective.' },
{ complete: (req) => llm.complete(req) },
)
const critic = defineAgent(
{ name: 'critic', system: 'Present a critical analysis.' },
{ complete: (req) => llm.complete(req) },
)
const results = await runParallel([optimist, critic], 'Should we adopt microservices?')
runSupervisor
Run a supervisor agent that coordinates a set of worker agents. The supervisor receives descriptions of all workers and the user request, and decides how to delegate.
function runSupervisor(
supervisor: Agent,
workers: Agent[],
input: string,
options?: AgentRunOptions,
): Promise<AgentResult>
| Parameter | Type | Description |
|---|---|---|
supervisor | Agent | The coordinating agent |
workers | Agent[] | Available worker agents (their names and system prompts are provided to the supervisor) |
input | string | User request to delegate |
options | AgentRunOptions | Shared run options |
Returns: Promise<AgentResult> — the supervisor’s synthesized response.
import { defineAgent, runSupervisor } from '@elsium-ai/agents'
const supervisor = defineAgent(
{ name: 'supervisor', system: 'Coordinate workers to answer the user.' },
{ complete: (req) => llm.complete(req) },
)
const coder = defineAgent(
{ name: 'coder', system: 'Write code solutions.' },
{ complete: (req) => llm.complete(req) },
)
const reviewer = defineAgent(
{ name: 'reviewer', system: 'Review code for bugs and improvements.' },
{ complete: (req) => llm.complete(req) },
)
const result = await runSupervisor(supervisor, [coder, reviewer], 'Build a rate limiter in TypeScript')
MultiAgentConfig
Configuration type for multi-agent orchestration (useful when building higher-level abstractions).
interface MultiAgentConfig {
agents: Agent[]
strategy: 'sequential' | 'parallel' | 'supervisor'
supervisor?: Agent
}
Streaming
agent.stream
agent.stream(input: string, options?: AgentRunOptions): AgentStream
Streams agent execution in real-time. Returns an AgentStream that yields AgentStreamEvent objects and provides a .result() method for the final AgentResult.
Requires a stream function in agent dependencies.
AgentStreamEvent types:
| Type | Fields | Description |
|---|---|---|
text_delta | text: string | Incremental text from the LLM |
tool_call_start | toolCall: { id, name } | Tool call initiated |
tool_call_delta | toolCallId, arguments | Partial tool arguments |
tool_call_end | toolCallId | Tool arguments complete |
tool_result | toolCallId, name, result | Tool execution result |
iteration_start | iteration: number | New loop iteration |
iteration_end | iteration: number | Loop iteration complete |
agent_end | result: AgentResult | Agent finished |
error | error: Error | Execution error |
const agent = defineAgent(
{ name: 'assistant', system: 'You are helpful.' },
{ complete: (req) => llm.complete(req), stream: (req) => llm.stream(req) },
)
const stream = agent.stream('Hello')
for await (const event of stream) {
if (event.type === 'text_delta') process.stdout.write(event.text)
}
const result = await stream.result()
Threads
createThread
createThread(config: ThreadConfig): Thread
Creates a conversation thread that manages message history across multiple send() calls.
Config:
| Field | Type | Description |
|---|---|---|
id | string? | Custom thread ID (auto-generated if omitted) |
agent | Agent | The agent to use for this thread |
metadata | Record? | Custom metadata |
store | ThreadStore? | Persistence adapter |
Thread methods:
| Method | Returns | Description |
|---|---|---|
send(input) | Promise<AgentResult> | Send a message and get a response |
stream(input) | AgentStream | Stream a response |
getMessages() | Message[] | Get full conversation history |
addMessage(msg) | void | Manually add a message |
fork(opts?) | Thread | Fork thread with full history |
clear() | void | Clear all messages |
save() | Promise<void> | Manually persist to store |
loadThread
loadThread(threadId: string, config: { agent, store }): Promise<Thread | null>
Loads a thread from a store. Returns null if not found.
createInMemoryThreadStore
createInMemoryThreadStore(): ThreadStore
In-memory thread persistence. Data lost on process exit.
Async Agents
createAsyncAgent
createAsyncAgent(config: AsyncAgentConfig): AsyncAgent
Wraps an agent for background task execution with progress tracking and cancellation.
Config:
| Field | Type | Description |
|---|---|---|
agent | Agent | The agent to run tasks with |
onProgress | (task, event) => void | Progress callback |
onComplete | (task) => void | Completion callback |
onError | (task, error) => void | Error callback |
AsyncAgent methods:
| Method | Returns | Description |
|---|---|---|
submit(input, opts?) | AgentTask | Submit a task for execution |
getTask(id) | AgentTask | null | Get a task by ID |
listTasks(filter?) | AgentTask[] | List tasks, optionally filtered by status |
cancelAll() | void | Cancel all pending/running tasks |
AgentTask:
| Property/Method | Description |
|---|---|
id | Task identifier |
status | 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' |
result | AgentResult | null |
error | Error | null |
wait() | Promise<AgentResult> — resolves when task completes |
cancel() | Cancel the task |
const asyncAgent = createAsyncAgent({ agent, onComplete: (t) => notify(t.id) })
const task = asyncAgent.submit('Research quantum computing')
const result = await task.wait()
Task Stores
Pass a TaskStore as AsyncAgentConfig.taskStore to persist every task transition, so submitted tasks survive process restarts. The package ships two reference adapters; for production durability implement TaskStore against your own backend.
createInMemoryTaskStore(): TaskStore
createJsonFileTaskStore(config: JsonFileTaskStoreConfig): TaskStore
createInMemoryTaskStore keeps PersistedTask records in a Map (lost on exit). createJsonFileTaskStore writes one <taskId>.json file per task under config.dir using atomic temp-file renames and per-id write serialization.
JsonFileTaskStoreConfig:
| Field | Type | Description |
|---|---|---|
dir | string | Directory where task JSON files are written (created if missing) |
TaskStore methods:
| Method | Returns | Description |
|---|---|---|
save(task) | Promise<void> | Persist (upsert) a PersistedTask |
load(taskId) | Promise<PersistedTask | null> | Load a task by ID |
list(filter?) | Promise<PersistedTask[]> | List tasks, optionally filtered by status |
delete(taskId) | Promise<void> | Remove a task record |
import { createAsyncAgent, createJsonFileTaskStore } from '@elsium-ai/agents'
const taskStore = createJsonFileTaskStore({ dir: './data/tasks' })
const asyncAgent = createAsyncAgent({ agent, taskStore })
ReAct Agent
defineReActAgent
defineReActAgent(config: ReActConfig): ReActAgent
Creates an agent that follows the explicit ReAct (Reason + Act) loop — emitting Thought → Action → Action Input → Observation cycles until it produces a Final Answer. Unlike defineAgent, the result carries the full step-by-step reasoning trace, and the agent is wired directly to a gateway/provider rather than taking AgentDependencies.
ReActConfig:
| Field | Type | Description |
|---|---|---|
name | string | Agent identifier |
tools | Tool[] | Tools available to the agent |
model | string? | LLM model to use |
system | string? | User system prompt, prepended to the built-in ReAct prompt |
maxIterations | number? | Maximum reasoning iterations (default: 10) |
maxTokenBudget | number? | Token budget across the run (default: 500000) |
hooks | ReActConfig['hooks']? | AgentHooks plus onThought, onAction, onObservation callbacks |
provider | string | LLMProvider | ProviderMesh? | Provider name, or an object exposing complete |
apiKey | string? | API key (required when provider is a name string) |
baseUrl | string? | Optional provider base URL |
Provide either an LLMProvider/ProviderMesh object as provider, or a provider name string together with apiKey; otherwise construction throws.
ReActAgent exposes name and run(input, options?): Promise<ReActResult>.
ReActResult extends AgentResult with reasoning: ReActStep[], where each ReActStep is { iteration, thought, action?: { tool, input }, observation? }.
import { defineReActAgent } from '@elsium-ai/agents'
const agent = defineReActAgent({
name: 'researcher',
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4o',
tools: [searchTool, calculatorTool],
hooks: {
onThought: (thought, i) => console.log(`[${i}] ${thought}`),
},
})
const result = await agent.run('What is the population of France times 2?')
for (const step of result.reasoning) {
console.log(step.thought, step.action, step.observation)
}
Semantic Guardrails
createSemanticValidator
Create a semantic validator that checks agent output for hallucinations, relevance, and grounding. When an llmComplete function is provided, checks use LLM-based evaluation; otherwise they fall back to word-overlap heuristics.
function createSemanticValidator(
config: SemanticGuardrailConfig,
llmComplete?: LLMComplete,
): SemanticValidator
| Parameter | Type | Description |
|---|---|---|
config | SemanticGuardrailConfig | Which checks to enable and their thresholds |
llmComplete | (request: CompletionRequest) => Promise<LLMResponse> | Optional LLM function for production-grade checks |
Returns: SemanticValidator
import { createSemanticValidator } from '@elsium-ai/agents'
const validator = createSemanticValidator(
{
hallucination: { enabled: true, ragContext: ['The capital of France is Paris.'], threshold: 0.7 },
relevance: { enabled: true, threshold: 0.5 },
},
(req) => llm.complete(req),
)
const result = await validator.validate(
'What is the capital of France?',
'The capital of France is Paris.',
)
console.log(result.valid) // true
console.log(result.checks) // [{ name: 'hallucination', passed: true, ... }, ...]
SemanticValidator
The validator object returned by createSemanticValidator.
interface SemanticValidator {
validate(input: string, output: string): Promise<SemanticValidationResult>
checkHallucination(output: string, context: string[]): Promise<SemanticCheckResult>
checkRelevance(input: string, output: string): Promise<SemanticCheckResult>
checkGrounding(output: string, sources: string[]): Promise<SemanticCheckResult>
}
| Method | Description |
|---|---|
validate(input, output) | Run all enabled checks and return an aggregate result |
checkHallucination(output, context) | Check whether the output contains claims unsupported by the given context |
checkRelevance(input, output) | Check whether the output is relevant to the input |
checkGrounding(output, sources) | Check whether the output’s claims are grounded in the provided sources |
SemanticGuardrailConfig
Configuration for which semantic checks to enable and how they behave.
interface SemanticGuardrailConfig {
hallucination?: {
enabled: boolean
ragContext?: string[]
threshold?: number
}
relevance?: {
enabled: boolean
threshold?: number
}
grounding?: {
enabled: boolean
sources?: string[]
}
customChecks?: SemanticCheck[]
autoRetry?: {
enabled: boolean
maxRetries?: number
}
}
SemanticCheck
A custom semantic check function.
interface SemanticCheck {
name: string
check: (input: string, output: string) => Promise<SemanticCheckResult>
}
SemanticCheckResult
The result of a single semantic check.
interface SemanticCheckResult {
passed: boolean
score: number
reason: string
}
SemanticValidationResult
The aggregate result of all semantic checks run by validate().
interface SemanticValidationResult {
valid: boolean
checks: Array<{
name: string
passed: boolean
score: number
reason: string
}>
}
Security
createAgentSecurity
Create a security module that validates input for prompt injection and jailbreak attempts, and sanitizes output by redacting detected secrets.
function createAgentSecurity(config: AgentSecurityConfig): {
validateInput(input: string): AgentSecurityResult
sanitizeInput(input: string): AgentSecurityResult
sanitizeOutput(output: string): AgentSecurityResult
}
| Parameter | Type | Description |
|---|---|---|
config | AgentSecurityConfig | Which security checks to enable |
Returns: An object with validateInput, sanitizeInput, and sanitizeOutput methods.
import { createAgentSecurity } from '@elsium-ai/agents'
const security = createAgentSecurity({
detectPromptInjection: true,
detectJailbreak: true,
redactSecrets: true,
})
const inputCheck = security.validateInput('ignore all previous instructions and ...')
console.log(inputCheck.safe) // false
console.log(inputCheck.violations) // [{ type: 'prompt_injection', ... }]
const outputCheck = security.sanitizeOutput('Your API key is sk-abc123def456ghi789jkl012mno')
console.log(outputCheck.redactedOutput) // 'Your API key is [REDACTED_API_KEY]'
AgentSecurityConfig
Configuration for the security module.
interface AgentSecurityConfig {
detectPromptInjection?: boolean
detectJailbreak?: boolean
redactSecrets?: boolean
blockedPatterns?: RegExp[]
redactInputSecrets?: boolean
redactInputPii?: Array<'email' | 'phone' | 'address' | 'passport' | 'all'>
injectionClassifier?: InputGuardrail
redactToolArgSecrets?: boolean
}
| Field | Type | Default | Description |
|---|---|---|---|
detectPromptInjection | boolean | true | Detect prompt injection patterns in input |
detectJailbreak | boolean | false | Detect jailbreak attempt patterns in input |
redactSecrets | boolean | true | Redact secrets (API keys, passwords, SSNs, cards, bearer tokens) in output |
blockedPatterns | RegExp[] | [] | Additional custom regex patterns to block in input |
redactInputSecrets | boolean | false | Redact secrets from user input before it reaches the model |
redactInputPii | Array<'email' | 'phone' | 'address' | 'passport' | 'all'> | [] | PII categories to redact from input; setting any category also redacts input secrets |
injectionClassifier | InputGuardrail | — | Optional async classifier run on the raw input; return true to reject it (applied on run/chat/generate, not stream) |
redactToolArgSecrets | boolean | false | Redact secrets from tool-call arguments before execution and trace (PII is left intact) |
Input guardrail pipeline (applied on run / chat / generate): detection (throws on violation) → async injectionClassifier (throws if flagged) → redaction (transforms the text sent to the model). stream applies only the synchronous steps (detection + redaction); the async classifier is skipped.
AgentSecurityResult
The result from an input validation or output sanitization check.
interface AgentSecurityResult {
safe: boolean
violations: Array<{
type: string
detail: string
severity: 'low' | 'medium' | 'high'
}>
redactedOutput?: string
}
createLLMGuardrail
createLLMGuardrail(options: LLMGuardrailOptions): InputGuardrail
Returns a built-in InputGuardrail ((input: string) => boolean | Promise<boolean>, true = reject) backed by the gateway you already use — no extra install. Plug it into AgentSecurityConfig.injectionClassifier as a higher-precision alternative to the heuristic regex detector.
InputGuardrail is the extension port: pass createLLMGuardrail, or your own function wrapping an external tool (Lakera, NeMo Guardrails, Presidio, etc.). External integration is the caller’s choice, never a dependency.
LLMGuardrailOptions:
| Field | Type | Default | Description |
|---|---|---|---|
complete | LLMComplete | — | Completion function — typically gateway.complete |
model | string | gateway default | Model override |
instructions | string | built-in prompt | Override the classification system prompt |
onError | 'allow' | 'block' | 'allow' | 'allow' fails open (do not block on classifier failure); 'block' fails closed |
import { defineAgent, createLLMGuardrail } from '@elsium-ai/agents'
const agent = defineAgent({
name: 'assistant',
system: 'You are helpful.',
guardrails: {
security: {
injectionClassifier: createLLMGuardrail({ complete: (req) => llm.complete(req) }),
},
},
})
Confidence
createConfidenceScorer
Create a confidence scorer that evaluates how reliable an agent’s output is. When semantic validation results are available they are used directly; otherwise heuristic word-overlap analysis is applied.
function createConfidenceScorer(config: ConfidenceConfig): {
score(
input: string,
output: string,
semanticResult?: SemanticValidationResult,
): Promise<ConfidenceResult>
}
| Parameter | Type | Description |
|---|---|---|
config | ConfidenceConfig | Which confidence signals to compute |
Returns: An object with a score method.
import { createConfidenceScorer } from '@elsium-ai/agents'
const scorer = createConfidenceScorer({
hallucinationRisk: true,
relevanceScore: true,
citationCoverage: false,
})
const confidence = await scorer.score(
'What is TypeScript?',
'TypeScript is a typed superset of JavaScript.',
)
console.log(confidence.overall) // 0.0 - 1.0
console.log(confidence.hallucinationRisk) // 0.0 - 1.0 (lower is better)
console.log(confidence.relevanceScore) // 0.0 - 1.0 (higher is better)
ConfidenceConfig
Configuration for the confidence scorer.
interface ConfidenceConfig {
hallucinationRisk?: boolean
relevanceScore?: boolean
citationCoverage?: boolean
customChecks?: Array<{
name: string
check: (input: string, output: string) => Promise<{ score: number; reason: string }>
}>
}
| Field | Type | Default | Description |
|---|---|---|---|
hallucinationRisk | boolean | true | Compute hallucination risk (higher score = higher risk) |
relevanceScore | boolean | true | Compute relevance to input (higher score = more relevant) |
citationCoverage | boolean | false | Compute citation coverage against RAG context |
customChecks | Array | [] | Additional custom scoring functions |
ConfidenceResult
The scored confidence output.
interface ConfidenceResult {
overall: number
hallucinationRisk: number
citationCoverage: number
relevanceScore: number
checks: Array<{ name: string; score: number; reason: string }>
}
State Machine
executeStateMachine
Execute an agent through a finite state machine. Each state can override the system prompt, tools, and guardrails. The agent transitions between states based on the transition function defined on each StateDefinition until a terminal state is reached.
function executeStateMachine(
baseConfig: AgentConfig,
stateConfig: { states: Record<string, StateDefinition>; initialState: string },
deps: AgentDependencies,
input: string,
options?: AgentRunOptions,
): Promise<StateMachineResult>
| Parameter | Type | Description |
|---|---|---|
baseConfig | AgentConfig | Base agent configuration (model, default system prompt, guardrails) |
stateConfig | { states, initialState } | State definitions and the name of the initial state |
deps | AgentDependencies | LLM completion function |
input | string | User input |
options | AgentRunOptions | Optional run options |
Returns: Promise<StateMachineResult> — the final agent result plus the full state history.
import { defineAgent } from '@elsium-ai/agents'
const agent = defineAgent(
{
name: 'order-processor',
system: 'You process customer orders.',
initialState: 'intake',
states: {
intake: {
system: 'Gather order details from the user.',
transition: (result) => 'validate',
},
validate: {
system: 'Validate the order details are complete.',
transition: (result) => 'confirm',
},
confirm: {
system: 'Confirm the order with the user.',
terminal: true,
transition: () => 'confirm',
},
},
},
{ complete: (req) => llm.complete(req) },
)
const result = await agent.run('I want to order 3 widgets')
console.log(result.finalState) // 'confirm'
console.log(result.stateHistory) // [{ state: 'intake', ... }, { state: 'validate', ... }, ...]
Note: You do not need to call executeStateMachine directly. When states and initialState are set on the AgentConfig, calling agent.run() or agent.chat() automatically delegates to the state machine.
Approval Gates
createApprovalGate
Create an approval gate that intercepts operations and requests human (or programmatic) approval before proceeding.
function createApprovalGate(config: ApprovalGateConfig): ApprovalGate
| Parameter | Type | Description |
|---|---|---|
config | ApprovalGateConfig | Callback, filters, timeout, and timeout behavior |
Returns: ApprovalGate
import { createApprovalGate } from '@elsium-ai/agents'
const gate = createApprovalGate({
callback: async (request) => {
console.log(`Approval needed: ${request.description}`)
// In production, prompt a human or call an external approval service
return {
requestId: request.id,
approved: true,
reason: 'Auto-approved in development',
decidedAt: Date.now(),
}
},
requireApprovalFor: {
tools: ['delete_record', 'send_email'],
costThreshold: 1.0,
},
timeoutMs: 60_000,
onTimeout: 'deny',
})
const decision = await gate.requestApproval(
'tool_call',
'Execute tool: delete_record',
{ toolName: 'delete_record', arguments: { id: '123' } },
)
console.log(decision.approved) // true
shouldRequireApproval
Determine whether a given operation should be gated behind approval based on the configured rules.
function shouldRequireApproval(
config: ApprovalGateConfig['requireApprovalFor'],
context: { toolName?: string; model?: string; cost?: number },
): boolean
| Parameter | Type | Description |
|---|---|---|
config | ApprovalGateConfig['requireApprovalFor'] | The approval rules (which tools, models, or cost thresholds require approval) |
context | { toolName?, model?, cost? } | The current operation context to check |
Returns: boolean — true if the operation requires approval.
import { shouldRequireApproval } from '@elsium-ai/agents'
const needsApproval = shouldRequireApproval(
{ tools: ['delete_record'], costThreshold: 5.0 },
{ toolName: 'delete_record' },
)
console.log(needsApproval) // true
ApprovalRequest
Describes a pending approval request.
interface ApprovalRequest {
id: string
type: 'tool_call' | 'model_access' | 'budget_exceed' | 'custom'
description: string
context: Record<string, unknown>
requestedAt: number
}
ApprovalDecision
The decision returned by the approval callback.
interface ApprovalDecision {
requestId: string
approved: boolean
reason?: string
decidedBy?: string
decidedAt: number
}
ApprovalCallback
The function type called when approval is required.
type ApprovalCallback = (request: ApprovalRequest) => Promise<ApprovalDecision>
ApprovalGateConfig
Configuration for the approval gate.
interface ApprovalGateConfig {
callback: ApprovalCallback
requireApprovalFor?: {
tools?: string[] | boolean
models?: string[]
costThreshold?: number
}
timeoutMs?: number
onTimeout?: 'deny' | 'allow'
}
| Field | Type | Default | Description |
|---|---|---|---|
callback | ApprovalCallback | — | Function invoked when approval is needed |
requireApprovalFor | object | — | Rules defining which operations require approval |
requireApprovalFor.tools | string[] | boolean | — | Tool names that require approval, or true for all tools |
requireApprovalFor.models | string[] | — | Model identifiers that require approval |
requireApprovalFor.costThreshold | number | — | Cost threshold above which approval is required |
timeoutMs | number | 300000 | Milliseconds to wait for approval before timing out |
onTimeout | 'deny' | 'allow' | 'deny' | Behavior when the approval request times out |
ApprovalGate
The approval gate object returned by createApprovalGate.
interface ApprovalGate {
requestApproval(
type: ApprovalRequest['type'],
description: string,
context: Record<string, unknown>,
): Promise<ApprovalDecision>
readonly pendingCount: number
}
createApprovalChain / createInMemoryApprovalStore
createApprovalChain(config: ApprovalChainConfig): ApprovalChain
createInMemoryApprovalStore(): ApprovalStore
Multi-stage human-in-the-loop approval. A request flows through ordered ApprovalStages; each stage decides whether it applies (enter), who approves it (approver), and what happens on timeout. callback approvers are auto-invoked; role/user approvers pause the chain until resolved externally via store.resolveStage(...). The package ships only the in-memory store; implement ApprovalStore against your own backend for durability.
ApprovalChainConfig:
| Field | Type | Description |
|---|---|---|
stages | readonly ApprovalStage[] | Ordered stages (at least one; names must be unique) |
store | ApprovalStore | Persistence adapter for approval state |
notifier | ApprovalNotifier? | Optional hook called when a stage is entered (Slack/email/PagerDuty) |
ApprovalStage:
| Field | Type | Description |
|---|---|---|
name | string | Unique stage name |
enter | (req: ApprovalRequest) => boolean | Returns true if the stage applies; false skips it |
approver | ApproverSpec | { type: 'role' | 'user', target: string } or { type: 'callback', target: ApprovalCallback } |
timeoutMs | number? | Stage timeout (default: 300000) |
onTimeout | 'deny' | 'escalate' | 'allow'? | Action when the timeout fires (default: 'deny') |
ApprovalChain methods:
| Method | Returns | Description |
|---|---|---|
request(req) | Promise<ApprovalState> | Start a chain (req omits id/requestedAt); advances through auto-resolvable stages |
resume(requestId) | Promise<ApprovalState> | Re-advance a pending chain (e.g. after an external resolveStage) |
cancel(requestId, reason) | Promise<ApprovalState> | Deny all pending stages and mark the chain denied |
store | ApprovalStore | The backing store |
ApprovalState reports status ('pending' | 'approved' | 'denied' | 'expired'), currentStage, and per-stage StageState[].
import { createApprovalChain, createInMemoryApprovalStore } from '@elsium-ai/agents'
const chain = createApprovalChain({
store: createInMemoryApprovalStore(),
stages: [
{
name: 'manager',
enter: (req) => (req.context?.amount as number) > 1000,
approver: { type: 'role', target: 'manager' },
timeoutMs: 60_000,
onTimeout: 'escalate',
},
{
name: 'auto-policy',
enter: () => true,
approver: { type: 'callback', target: (req) => ({
requestId: req.id, approved: true, decidedAt: Date.now(),
}) },
},
],
})
let state = await chain.request({ type: 'tool_call', description: 'Wire funds', context: { amount: 5000 } })
// A role/user stage waits — the approving UI resolves it, then resume:
await chain.store.resolveStage(state.request.id, 'manager', {
requestId: state.request.id, approved: true, decidedBy: 'alice', decidedAt: Date.now(),
})
state = await chain.resume(state.request.id)
askHuman / resolveAskHuman / createInMemoryAskHumanStore
askHuman<TOption>(options: AskHumanOptions<TOption> & { timeoutMs?: string | number }): Promise<AskHumanDecision<TOption>>
resolveAskHuman(store: AskHumanStore, requestId: string, decision: Omit<AskHumanDecision, 'decidedAt'>): Promise<void>
createInMemoryAskHumanStore(config?: InMemoryAskHumanStoreConfig): AskHumanStore
Durable, option-based human-in-the-loop primitive. askHuman poses a question with a fixed set of options and resolves either through a responder callback or by polling a store until a human resolves it via resolveAskHuman. The same capability is also available pre-bound on every agent as agent.askHuman(options).
AskHumanOptions:
| Field | Type | Description |
|---|---|---|
question | string | The question to ask (required, non-empty) |
options | readonly TOption[] | Allowed answer options (at least one) |
context | Record? | Arbitrary context attached to the request |
timeoutMs | string | number? | Timeout; accepts ms or a duration string like '2h', '1d' (default: 24h) |
onTimeout | 'reject' | 'timeout'? | Status used when the timeout fires (default: 'timeout') |
store | AskHumanStore? | Store to persist/poll the request |
responder | AskHumanResponder<TOption>? | Callback that resolves the request directly |
requestId | string? | Custom request ID |
Either a responder or a store must be supplied. AskHumanDecision is { status: 'approved' | 'rejected' | 'timeout' | 'custom', option?, reason?, decidedBy?, decidedAt }.
import { askHuman, createInMemoryAskHumanStore, resolveAskHuman } from '@elsium-ai/agents'
const store = createInMemoryAskHumanStore()
// Worker A: pauses until a human resolves the request (or 2h elapse)
const pending = askHuman({
question: 'Approve the refund?',
options: ['approve', 'deny'] as const,
timeoutMs: '2h',
store,
requestId: 'refund-42',
})
// Worker B (the UI / webhook): resolve it
await resolveAskHuman(store, 'refund-42', {
status: 'custom', option: 'approve', decidedBy: 'ops',
})
const decision = await pending // { status: 'custom', option: 'approve', ... }
Verification (VAG)
Verification-Augmented Generation: generate, validate against one or more Validators, and automatically re-generate with a repair prompt until the output passes or the repair budget is exhausted.
runWithVerification
runWithVerification<T>(generate: GenerateFn<T>, config: VerificationConfig<T>): Promise<VerificationOutcome<T>>
Runs the generate → validate → repair loop. generate(repair?) produces a candidate (receiving a RepairContext with a ready-made repairPrompt on retries); validators run in 'all' mode (every validator must pass). Throws if config.validators is empty.
VerificationConfig:
| Field | Type | Description |
|---|---|---|
validators | Validator<T>[] | Validators to apply (all must pass) |
maxRepairs | number? | Max repair attempts after the first try (default from package) |
formatRepairPrompt | (failures, previousValue) => string? | Custom repair-prompt builder |
onAttempt | (attempt: VerificationAttempt<T>) => void? | Called after each attempt |
onAbort | (abort: VerificationAbort<T>) => void? | Called when the loop gives up |
VerificationOutcome is either { status: 'ok' | 'repaired', value, attempts, history } or { status: 'aborted', lastValue, attempts, history, reason }.
A Validator<T> is { name, validate(value, context): ValidationOutcome | Promise<ValidationOutcome> }, where ValidationOutcome is { valid, failures: ValidationFailure[] }. Built-in validator factories are exported for common cases: zodValidator / schemaValidator, regexValidator, judgeValidator, semanticAdapter, externalValidator, and composeValidators.
import { runWithVerification, zodValidator } from '@elsium-ai/agents'
import { z } from 'zod'
const schema = z.object({ title: z.string().max(60), tags: z.array(z.string()).min(1) })
const outcome = await runWithVerification(
async (repair) => llm.completeJson(repair?.repairPrompt ?? 'Summarize the article'),
{ validators: [zodValidator(schema)], maxRepairs: 2 },
)
if (outcome.status !== 'aborted') {
console.log(outcome.value, `passed in ${outcome.attempts} attempt(s)`)
}
withVerifiers
withVerifiers(base: Agent, verifiers: Validator<AgentResult>[], policy?: AgentRetryPolicy): Agent
Wraps an Agent so that run and generate automatically retry through runWithVerification against the supplied AgentResult verifiers. Failed verification throws an ElsiumError after exhausting the retry budget. The fluent equivalents agent.withVerifier(validator) and agent.withRetryPolicy(policy) are available on every agent returned by defineAgent.
AgentRetryPolicy:
| Field | Type | Description |
|---|---|---|
maxAttempts | number? | Total attempts including the first (default: 3) |
semantic | boolean? | Enable semantic repair behavior (default: true) |
import { defineAgent, judgeValidator } from '@elsium-ai/agents'
const agent = defineAgent({ name: 'writer', system: 'Write concise summaries.' })
const verified = agent
.withVerifier(judgeValidator({
rubric: 'The summary must be under 3 sentences and factually grounded.',
judge: (rubric, value) => myLLMJudge(rubric, value), // { passed, score }
}))
.withRetryPolicy({ maxAttempts: 4 })
const result = await verified.run('Summarize the quarterly report')
Confidence (CAG)
Confidence-Augmented Generation voters aggregate multiple samples of the same generation into a single winner plus a confidence score. They plug into selfConsistency({ voter }) (and are usable standalone).
createMajorityVoter / createSimilarityVoter
createMajorityVoter<T>(): Voter<T>
createSimilarityVoter<T>(options: SimilarityVoterOptions<T>): Voter<T>
createMajorityVoter picks the most frequent sample using a canonicalized deep-equality key (order-insensitive for object keys); confidence is the winner’s share of the votes. createSimilarityVoter clusters samples with a caller-supplied similarity function and returns the largest cluster’s representative; confidence is that cluster’s share.
SimilarityVoterOptions:
| Field | Type | Description |
|---|---|---|
similarity | (a: T, b: T) => number | Promise<number> | Pairwise similarity in [0, 1] |
threshold | number? | Minimum similarity to join a cluster (default: 0.85) |
A Voter<T> is { name, vote(samples): VoteResult<T> | Promise<VoteResult<T>> }, where VoteResult is { winner, confidence, details? }.
import { selfConsistency, createMajorityVoter, createSimilarityVoter } from '@elsium-ai/agents'
// Exact-match majority across 5 samples
const exact = selfConsistency({ samples: 5, voter: createMajorityVoter() })
const r1 = await exact.score(async () => ({ value: await classify(input) }))
// Cluster free-text answers by embedding similarity
const fuzzy = selfConsistency({
voter: createSimilarityVoter({
similarity: (a, b) => cosineSim(embed(a), embed(b)),
threshold: 0.9,
}),
})
const r2 = await fuzzy.score(async () => ({ value: await answer(input) }))
console.log(r2.value, r2.confidence)
Structured Output
agent.generate
agent.generate<T>(input: string, schema: z.ZodType<T>, options?: AgentRunOptions): Promise<AgentGenerateResult<T>>
Runs the full agent loop (tools, guardrails, memory) and parses the final response into a typed object validated against a Zod schema. Throws ElsiumError if the response is not valid JSON or doesn’t match the schema.
AgentGenerateResult<T>
| Field | Type | Description |
|---|---|---|
data | T | Parsed and validated data |
result | AgentResult | Full agent execution result |
import { z } from 'zod'
const schema = z.object({
answer: z.string(),
confidence: z.number(),
sources: z.array(z.string()),
})
const { data } = await agent.generate('What causes rain?', schema)
// data is fully typed: { answer: string, confidence: number, sources: string[] }
Channels
createWebhookChannel
createWebhookChannel(config: WebhookChannelConfig): ChannelAdapter & { receive(msg): void }
Creates a webhook-based channel adapter. Call receive() to inject incoming messages (e.g., from an HTTP webhook handler).
Config:
| Field | Type | Description |
|---|---|---|
name | string | Channel identifier |
onSend | (userId, message) => void | Callback when agent sends a response |
ChannelAdapter
interface ChannelAdapter {
readonly name: string
start(): Promise<void>
stop(): Promise<void>
send(userId: string, message: OutgoingMessage): Promise<void>
onMessage(handler: (message: IncomingMessage) => void): void
}
Implement this interface to create custom adapters for messaging platforms (WhatsApp, Telegram, Discord, Slack, etc.).
IncomingMessage:
| Field | Type | Description |
|---|---|---|
channelName | string | Which channel the message came from |
userId | string | User identifier on the platform |
text | string | Message text |
attachments | ChannelAttachment[]? | Optional attachments |
metadata | Record? | Platform-specific metadata |
raw | unknown? | Raw platform message |
createChannelGateway
createChannelGateway(config: ChannelGatewayConfig): ChannelGateway
Connects channel adapters to agents via a session router. Incoming messages are routed to the correct session, processed by the agent, and responses are sent back through the originating channel.
Config:
| Field | Type | Description |
|---|---|---|
adapters | ChannelAdapter[] | Channel adapters to connect |
router | SessionRouter | Session router for thread management |
agent | Agent | Default agent for all channels |
resolveAgent | (msg) => Agent? | Dynamic agent selection per message |
onError | (error, msg) => void | Error callback |
import {
createWebhookChannel, createChannelGateway,
createSessionRouter, defineAgent,
} from '@elsium-ai/agents'
const webhook = createWebhookChannel({
name: 'api',
onSend: (userId, msg) => sendPushNotification(userId, msg.text),
})
const agent = defineAgent({ name: 'assistant', system: 'You are helpful.' })
const router = createSessionRouter({ defaultAgent: agent })
const gateway = createChannelGateway({
adapters: [webhook],
router,
agent,
})
await gateway.start()
// In your HTTP handler:
webhook.receive({ userId: 'user-123', text: 'Hello!' })
Session Router
createSessionRouter
createSessionRouter(config: SessionRouterConfig): SessionRouter
Maps (channel, userId) pairs to persistent conversation threads with concurrency control.
Config:
| Field | Type | Description |
|---|---|---|
defaultAgent | Agent | Agent used when none specified |
store | ThreadStore? | Optional persistence for threads |
concurrency | 'serial' | 'parallel' | Concurrency mode (default: 'serial') |
sessionTimeout | number? | Auto-expire sessions after ms of inactivity |
onSessionCreated | (session) => void | Callback on new session |
onSessionExpired | (session) => void | Callback on session expiry |
SessionRouter methods:
| Method | Returns | Description |
|---|---|---|
resolve(opts) | Promise<Thread> | Get or create a thread for channel+user |
getSession(channel, userId) | SessionInfo | null | Get session info |
listSessions() | SessionInfo[] | List all active sessions |
endSession(channel, userId) | boolean | End a specific session |
endAllSessions() | void | End all sessions and cleanup |
import { createSessionRouter, defineAgent, createInMemoryThreadStore } from '@elsium-ai/agents'
const agent = defineAgent({ name: 'support', system: 'You help users.' })
const store = createInMemoryThreadStore()
const router = createSessionRouter({
defaultAgent: agent,
store,
concurrency: 'serial',
sessionTimeout: 30 * 60 * 1000, // 30 minutes
onSessionCreated: (s) => console.log(`New session: ${s.sessionId}`),
onSessionExpired: (s) => console.log(`Expired: ${s.sessionId}`),
})
const thread = await router.resolve({ channelName: 'slack', userId: 'U12345' })
const result = await thread.send('Help me reset my password')
Scheduler
createScheduler
createScheduler(config: SchedulerConfig): Scheduler
Cron-based task scheduler for recurring agent tasks.
Config:
| Field | Type | Description |
|---|---|---|
agent | Agent | Default agent for scheduled tasks |
resolveAgent | (task) => Agent? | Dynamic agent selection per task |
onComplete | (task, result) => void | Callback on successful run |
onError | (task, error) => void | Callback on failed run |
tickIntervalMs | number | Check interval in ms (default: 60000) |
Scheduler methods:
| Method | Returns | Description |
|---|---|---|
schedule(cron, input, opts?) | ScheduledTask | Schedule a recurring task |
unschedule(taskId) | boolean | Remove a scheduled task |
getTask(taskId) | ScheduledTask | null | Get task by ID |
listTasks() | ScheduledTask[] | List all tasks |
pause(taskId) | boolean | Pause a task |
resume(taskId) | boolean | Resume a paused task |
start() | void | Start the scheduler tick loop |
stop() | void | Stop the scheduler |
parseCronExpression / cronMatchesDate / getNextCronDate
parseCronExpression(expression: string): CronFields | null
cronMatchesDate(fields: CronFields, date: Date): boolean
getNextCronDate(fields: CronFields, after: Date): Date
Cron utilities. Standard 5-field cron syntax: minute hour dayOfMonth month dayOfWeek. Supports *, ranges (1-5), steps (*/15), and comma-separated values (0,30).
import { createScheduler, defineAgent } from '@elsium-ai/agents'
const agent = defineAgent({ name: 'reporter', system: 'Generate a daily summary.' })
const scheduler = createScheduler({
agent,
onComplete: (task, result) => sendSlackMessage(result.message.content),
onError: (task, error) => alertOps(error),
})
// Every day at 9am
scheduler.schedule('0 9 * * *', 'Generate the daily metrics report')
// Every 30 minutes on weekdays
scheduler.schedule('*/30 * * * 1-5', 'Check for critical alerts', {
name: 'alert-check',
})
scheduler.start()
Agent Identity
Cryptographic agent identity with HMAC-SHA256 signing, replay protection, and cross-agent verification.
createAgentIdentity
createAgentIdentity(config: AgentIdentityConfig): Promise<AgentIdentity>
Creates a cryptographic identity for an agent. Each identity has a unique keypair, can sign payloads with HMAC-SHA256, and verify signatures with timing-safe comparison and replay protection.
Config:
| Field | Type | Description |
|---|---|---|
agentId | string | Unique agent identifier |
secret | string? | HMAC secret (auto-generated if omitted) |
replayWindowMs | number? | Replay protection window (default: 5 min) |
import { createAgentIdentity } from '@elsium-ai/agents'
const identity = await createAgentIdentity({ agentId: 'researcher' })
const signed = await identity.sign({ action: 'tool_call', tool: 'search' })
const result = await identity.verify(signed)
// { valid: true }
createIdentityRegistry
createIdentityRegistry(): IdentityRegistry
Central registry for managing and verifying agent identities across a multi-agent system.
import { createAgentIdentity, createIdentityRegistry } from '@elsium-ai/agents'
const registry = createIdentityRegistry()
const researcher = await createAgentIdentity({ agentId: 'researcher' })
const reviewer = await createAgentIdentity({ agentId: 'reviewer' })
registry.register(researcher)
registry.register(reviewer)
const signed = await researcher.sign({ data: 'findings' })
const verification = await registry.verifySignedPayload(signed)
// { valid: true }
Runtime Policy Enforcement
Enforce policies at agent runtime — before each tool call, not just at the HTTP layer.
createRuntimePolicyEnforcer
createRuntimePolicyEnforcer(config: RuntimePolicyConfig): RuntimePolicyEnforcer
Creates a policy enforcer that checks RBAC, tool access, and custom policies before each tool execution inside the agent loop.
Config:
| Field | Type | Description |
|---|---|---|
policies | PolicySet | Policy set from @elsium-ai/core |
actor | string? | Actor identity for policy context |
role | string? | Role for policy evaluation |
allowedTools | string[]? | Whitelist of allowed tool names |
deniedTools | string[]? | Blacklist of denied tool names |
import { createPolicySet, tokenLimitPolicy } from '@elsium-ai/core'
import { defineAgent, createRuntimePolicyEnforcer, toolAccessPolicy } from '@elsium-ai/agents'
const policies = createPolicySet([
tokenLimitPolicy(10_000),
toolAccessPolicy(['search', 'read_file']),
])
const agent = defineAgent({
name: 'restricted-agent',
system: 'You are a read-only assistant.',
guardrails: {
runtimePolicy: {
policies,
role: 'viewer',
allowedTools: ['search', 'read_file'],
deniedTools: ['delete_file', 'write_file'],
},
maxDurationMs: 30_000,
},
})
Built-in Policy Factories
| Factory | Description |
|---|---|
toolAccessPolicy(tools: string[]) | Restricts tool execution to a whitelist |
iterationLimitPolicy(max: number) | Limits agent iteration count via policy |
Memory Integrity
SHA-256 hash-chained memory stores that detect tampering.
createSecureMemoryStore
createSecureMemoryStore(inner: MemoryStore): SecureMemoryStore
Wraps any MemoryStore with a SHA-256 hash chain. Every message gets a hash binding it to the previous message, forming a tamper-evident chain.
import { createInMemoryMemoryStore, createSecureMemoryStore } from '@elsium-ai/agents'
const inner = createInMemoryMemoryStore()
const secure = createSecureMemoryStore(inner)
await secure.save('agent-1', messages)
const integrity = await secure.verifyIntegrity('agent-1')
// { valid: true, totalMessages: 5, chainComplete: true }
Utility Functions
| Function | Description |
|---|---|
computeMessageHash(msg, index, previousHash) | Compute SHA-256 hash for a single message in the chain |
verifyMessageChain(messages, hashes) | Verify integrity of a complete message chain |
Part of ElsiumAI
This package is the agent orchestration layer of the ElsiumAI framework. See the Getting Started guide or the Fundamentals for comprehensive coverage of every feature.