Skip to content

Incident Response Agent

Build a BDI (Belief-Desire-Intention) cognitive agent for cybersecurity incident response with real-time WebSocket events, episodic memory, human-in-the-loop approval, and inter-agent coordination.

What you will learn:

  • Creating and configuring cognitive agents
  • Managing beliefs, goals, and drives as psi-terms
  • Running BDI cognitive cycles and interpreting outcomes
  • Subscribing to real-time agent events via WebSocket
  • Human-in-the-loop approval workflows
  • Episodic memory for learning from past incidents
  • Inter-agent messaging and coordination

Prerequisites:

  • Completion of the Loan Approval Engine tutorial (or familiarity with sorts, inference rules, and backward chaining)
  • A running Reasoning Layer backend at https://platform.ovh.reasoninglayer.ai
  • Node.js 18+ with @kortexya/reasoninglayer installed

1. Why a Cognitive Agent?

Traditional incident response relies on static playbooks and human analysts. A BDI cognitive agent brings three advantages:

  1. Beliefs — the agent maintains a typed knowledge base of threat intelligence, correlated with the sort hierarchy. An IDS alert belief can unify with inference rules, not just pattern-match strings.
  2. Goals — response objectives (contain_threat, identify_scope) are psi-terms in the sort lattice. The engine can compute GLB of two goals to find a more specific combined objective.
  3. Intentions — committed plans are tracked with commitment strength. When new information arrives, the agent can reconsider its commitments using its reasoning engine.

The key insight: an agent’s beliefs ARE psi-terms, its rules ARE inference rules, and its goals ARE backward chaining goals. The cognitive layer builds on the inference layer from Tutorial 2.


2. Set Up the Security Domain

import {
ReasoningLayerClient,
SortBuilder,
psi,
constrained,
guard,
} from '@kortexya/reasoninglayer';
import type { SortDto } from '@kortexya/reasoninglayer';
import type { Cognitive } from '@kortexya/reasoninglayer';
type CreateAgentResponse = Cognitive.CreateAgentResponse;
type AgentStateDto = Cognitive.AgentStateDto;
type ExtendedAgentStateDto = Cognitive.ExtendedAgentStateDto;
type AddBeliefResponse = Cognitive.AddBeliefResponse;
type AddGoalResponse = Cognitive.AddGoalResponse;
type RunCycleResponse = Cognitive.RunCycleResponse;
type CycleOutcomeDto = Cognitive.CycleOutcomeDto;
type AgentEvent = Cognitive.AgentEvent;
type AgentEventHandlers = Cognitive.AgentEventHandlers;
type AgentSubscription = Cognitive.AgentSubscription;
type EpisodeStatsResponse = Cognitive.EpisodeStatsResponse;
const tenantId = '550e8400-e29b-41d4-a716-446655440000';
const client = new ReasoningLayerClient({
baseUrl: 'https://platform.ovh.reasoninglayer.ai',
tenantId,
auth: { mode: 'cookie' },
});

2.1 Create the Security Ontology

// Base sorts
const securityEntity = await client.sorts.createSort(
SortBuilder.create('security_entity')
.description('Base sort for all security domain concepts')
.build()
);
const networkEntity = await client.sorts.createSort(
SortBuilder.create('network_entity')
.description('Base sort for network infrastructure concepts')
.build()
);
// Alert hierarchy
const alert = await client.sorts.createSort(
SortBuilder.create('alert')
.parent(securityEntity.id)
.feature({ name: 'source', required: true })
.feature({ name: 'severity', required: true, constraint: { type: 'IntegerRange', value: { min: 1, max: 10 } } })
.feature({ name: 'timestamp', required: true })
.feature({ name: 'description', required: true })
.feature({ name: 'host', required: false })
.build()
);
// Multiple inheritance: IDS alert is both a security entity and a network entity
const idsAlert = await client.sorts.createSort(
SortBuilder.create('ids_alert')
.parents([alert.id, networkEntity.id])
.feature({ name: 'signature_id', required: true })
.feature({ name: 'src_ip', required: true })
.feature({ name: 'dst_ip', required: true })
.feature({ name: 'protocol', required: false })
.description('Inherits from both alert and network_entity')
.build()
);
const siemAlert = await client.sorts.createSort(
SortBuilder.create('siem_alert')
.parent(alert.id)
.feature({ name: 'correlation_id', required: false })
.feature({ name: 'log_sources', required: false })
.build()
);
// Indicator of Compromise (IoC)
const indicator = await client.sorts.createSort(
SortBuilder.create('indicator')
.parent(securityEntity.id)
.feature({ name: 'ioc_type', required: true }) // ip, domain, hash, url
.feature({ name: 'ioc_value', required: true })
.feature({ name: 'confidence', required: true })
.feature({ name: 'source_feed', required: false })
.build()
);
// Host inventory
const host = await client.sorts.createSort(
SortBuilder.create('host')
.parent(networkEntity.id)
.feature({ name: 'hostname', required: true })
.feature({ name: 'ip_address', required: true })
.feature({ name: 'os', required: false })
.feature({ name: 'criticality', required: true, constraint: { type: 'IntegerRange', value: { min: 1, max: 5 } } })
.feature({ name: 'zone', required: true })
.build()
);
// Response actions — subsorts carry different features
const responseAction = await client.sorts.createSort(
SortBuilder.create('response_action')
.parent(securityEntity.id)
.feature({ name: 'target_host', required: true })
.feature({ name: 'priority', required: true })
.feature({ name: 'requires_approval', required: true })
.build()
);
const isolate = await client.sorts.createSort(
SortBuilder.create('isolate')
.parent(responseAction.id)
.feature({ name: 'isolation_method', required: true })
.feature({ name: 'duration_hours', required: false })
.build()
);
const blockIp = await client.sorts.createSort(
SortBuilder.create('block_ip')
.parent(responseAction.id)
.feature({ name: 'blocked_ip', required: true })
.feature({ name: 'block_scope', required: false })
.build()
);

3. Create the Cognitive Agent

A cognitive agent maintains its own belief base, goal stack, and intention commitments. Configuration parameters control how aggressively it pursues goals and when it requests human approval.

const agentResponse: CreateAgentResponse = await client.cognitive.createAgent({
name: 'incident-responder-alpha',
config: {
beliefConfidenceThreshold: 0.6, // Discard beliefs below 60% confidence
enableLearning: true, // Learn from feedback
maxBeliefHistory: 500, // Retain last 500 belief updates
maxGoalsPerCycle: 3, // Pursue up to 3 goals per cycle
},
});
const agentId = agentResponse.agentId;
console.log(`Created agent: ${agentResponse.name} (${agentId})`);

3.1 Register Cognitive Sorts and Rules

Cognitive sorts and rules are registered in the agent’s cognitive registry, separate from the main sort hierarchy. They define how the agent reasons about its beliefs and goals.

// Register security-relevant sorts in the cognitive registry
await client.cognitive.addSort({
sortName: 'threat_detected',
parentSortNames: ['security_entity'],
});
await client.cognitive.addSort({
sortName: 'containment_action',
parentSortNames: ['response_action'],
});
// Cognitive rule: high-severity alert → create containment goal
const rule1 = await client.cognitive.addRule({
head: {
sort: 'containment_action',
features: {
target_host: { name: '?Host' },
priority: { name: '?Priority' },
},
},
antecedents: [
{
sort: 'ids_alert',
features: {
host: { name: '?Host' },
severity: { name: '?Severity' },
},
},
],
certainty: 0.85,
});
console.log(`Cognitive rule: ${rule1.ruleId}`);
// Rule: IoC match → block IP action
const rule2 = await client.cognitive.addRule({
head: {
sort: 'block_ip',
features: {
blocked_ip: { name: '?SrcIP' },
target_host: { name: '?DstHost' },
},
},
antecedents: [
{
sort: 'indicator',
features: {
ioc_type: 'ip',
ioc_value: { name: '?SrcIP' },
},
},
{
sort: 'ids_alert',
features: {
src_ip: { name: '?SrcIP' },
host: { name: '?DstHost' },
},
},
],
certainty: 0.9,
});
console.log(`IoC correlation rule: ${rule2.ruleId}`);

4. Populate Beliefs

Beliefs represent what the agent knows. Each belief carries a confidence score and a source attribution. Beliefs are psi-terms — they participate in the agent’s inference.

// Alert beliefs — incoming from detection systems
const alertBelief: AddBeliefResponse = await client.cognitive.addBelief({
agentId: agentId,
belief: {
sort: 'ids_alert',
features: {
source: 'suricata',
severity: 8,
timestamp: '2024-04-01T14:23:00Z',
description: 'ET MALWARE Win32/Emotet C2 Activity Detected',
host: 'web-prod-03',
signature_id: 'SID-2028765',
src_ip: '198.51.100.42',
dst_ip: '10.0.1.15',
protocol: 'tcp',
},
},
confidence: 0.92,
source: 'ids_pipeline',
});
console.log(`Alert belief: ${alertBelief.beliefId}`);
// Threat intelligence beliefs
await client.cognitive.addBelief({
agentId: agentId,
belief: {
sort: 'indicator',
features: {
ioc_type: 'ip',
ioc_value: '198.51.100.42',
confidence: 0.95,
source_feed: 'abuse_ch',
},
},
confidence: 0.95,
source: 'threat_intel_feed',
});
// Host inventory beliefs
await client.cognitive.addBelief({
agentId: agentId,
belief: {
sort: 'host',
features: {
hostname: 'web-prod-03',
ip_address: '10.0.1.15',
os: 'Ubuntu 22.04',
criticality: 4, // High criticality: production web server
zone: 'dmz',
},
},
confidence: 1.0,
source: 'cmdb',
});
// A second alert on a different host
await client.cognitive.addBelief({
agentId: agentId,
belief: {
sort: 'siem_alert',
features: {
source: 'splunk',
severity: 5,
timestamp: '2024-04-01T14:25:00Z',
description: 'Unusual outbound DNS traffic volume',
host: 'db-staging-01',
correlation_id: 'CORR-2024-0401-A',
},
},
confidence: 0.7,
source: 'siem_pipeline',
});

5. Set Goals and Run a BDI Cycle

Goals represent what the agent wants to achieve. The agent prioritizes goals and selects plans to pursue them.

// Primary goal: contain the threat on web-prod-03
const goal1: AddGoalResponse = await client.cognitive.addGoal({
agentId: agentId,
goal: {
sort: 'containment_action',
features: {
target_host: 'web-prod-03',
},
},
priority: 10, // Highest priority
status: 'pending',
});
console.log(`Containment goal: ${goal1.goalId}`);
// Secondary goal: identify the blast radius
const goal2: AddGoalResponse = await client.cognitive.addGoal({
agentId: agentId,
goal: {
sort: 'threat_detected',
features: {
target_host: 'web-prod-03',
},
},
priority: 7,
status: 'pending',
});
console.log(`Scope identification goal: ${goal2.goalId}`);
// Run a BDI cognitive cycle
const cycle: RunCycleResponse = await client.cognitive.runCycle({
agentId: agentId,
});
console.log(`\n=== BDI Cycle Result ===`);
console.log(`Message: ${cycle.message}`);
const outcome: CycleOutcomeDto = cycle.outcome;
console.log(`Goals pursued: ${outcome.goalsPursued.length}`);
console.log(`Goals achieved: ${outcome.goalsAchieved.length}`);
console.log(`Goals failed: ${outcome.goalsFailed.length}`);
console.log(`Actions executed: ${outcome.actionsExecuted.length}`);
console.log(`New beliefs: ${outcome.newBeliefs.length}`);
console.log(`Updated beliefs: ${outcome.updatedBeliefs.length}`);
console.log(`Success rate: ${outcome.successRate}`);

6. Real-Time WebSocket Events

Subscribe to the agent’s event stream for real-time notification of cognitive activity. Events are a discriminated union — switch on event.type for type-safe handling.

const handlers: AgentEventHandlers = {
onEvent: (event: AgentEvent) => {
switch (event.type) {
case 'connected':
console.log(`\n[WS] Connected to agent ${event.agentId}`);
break;
case 'kb_change':
console.log(`[WS] KB change: ${event.changeType} on ${event.sort} (term: ${event.termId})`);
// Fires when beliefs are added, updated, or removed
break;
case 'hitl_request':
console.log(`\n[WS] === HUMAN APPROVAL REQUIRED ===`);
console.log(` Review ID: ${event.reviewId}`);
console.log(` Action: ${event.actionSort}`);
console.log(` Confidence: ${event.confidence}`);
if (event.deadline) {
console.log(` Deadline: ${event.deadline}`);
}
// In production, route this to a human analyst via your ticketing system
break;
case 'cycle_complete':
console.log(`[WS] Cycle complete for agent ${event.agentId}`);
console.log(` Actions: ${event.actionsExecuted}, Beliefs updated: ${event.beliefsUpdated}`);
if (event.pendingReviews.length > 0) {
console.log(` Pending reviews: ${event.pendingReviews.join(', ')}`);
}
break;
case 'attention_shift':
console.log(`[WS] Attention shifted to goal: ${event.to} (priority: ${event.priority})`);
if (event.from) {
console.log(` From: ${event.from}`);
}
// Fires when the agent switches which goal it is pursuing
break;
case 'goal_completed':
console.log(`[WS] Goal ${event.goalId}: ${event.success ? 'ACHIEVED' : 'FAILED'}`);
if (event.result) {
console.log(` Result: ${event.result}`);
}
break;
case 'impasse_detected':
console.log(`[WS] IMPASSE on goal ${event.blockedGoal}`);
console.log(` Type: ${event.impasseType}`);
console.log(` Suggestions: ${event.recoverySuggestions.join(', ')}`);
// The agent cannot make progress — may need human intervention
break;
case 'heartbeat':
// Periodic health check — log sparingly in production
break;
case 'error':
console.error(`[WS] Agent error [${event.code}]: ${event.message}`);
break;
}
},
onReconnect: () => {
console.log('[WS] Reconnected after connection loss');
},
onError: (error: Error) => {
console.error('[WS] Connection error:', error.message);
},
onClose: () => {
console.log('[WS] Connection closed');
},
};
const subscription: AgentSubscription = client.cognitive.subscribeToEvents(
agentId,
handlers,
);
console.log(`WebSocket connected: ${subscription.connected}`);

6.1 A Real-Time Incident Scenario

With the WebSocket connected, trigger a sequence of events:

// Simulate a new high-severity alert arriving
const newAlert: AddBeliefResponse = await client.cognitive.addBelief({
agentId: agentId,
belief: {
sort: 'ids_alert',
features: {
source: 'suricata',
severity: 9,
timestamp: '2024-04-01T14:30:00Z',
description: 'ET TROJAN Possible Emotet Lateral Movement',
host: 'app-prod-01',
signature_id: 'SID-2028770',
src_ip: '10.0.1.15', // web-prod-03 is now the source!
dst_ip: '10.0.2.22',
protocol: 'smb',
},
},
confidence: 0.88,
source: 'ids_pipeline',
});
// → WebSocket fires: kb_change event
// Run another BDI cycle — the agent should escalate
const cycle2: RunCycleResponse = await client.cognitive.runCycle({
agentId: agentId,
});
// → WebSocket fires: attention_shift (lateral movement is higher priority)
// → WebSocket fires: hitl_request (isolating a production server requires approval)
// → WebSocket fires: cycle_complete
console.log(`Cycle 2: ${cycle2.message}`);
console.log(`Actions executed: ${cycle2.outcome.actionsExecuted.length}`);

7. Human-in-the-Loop Approval

When the agent proposes a high-impact action (like isolating a production server), it emits a hitl_request event and waits for human approval. This is the bridge between autonomous reasoning and human oversight.

// Provide feedback/approval for the containment action
const feedback = await client.cognitive.provideFeedback({
agentId: agentId,
resultId: goal1.goalId,
rating: 1, // Approve the action
correction: 'Approved: isolate web-prod-03 from DMZ, maintain logging access',
preference: 'Prefer network isolation over full shutdown for production hosts',
});
console.log(`\n=== HITL Feedback ===`);
console.log(`Feedback ID: ${feedback.feedbackId}`);
if (feedback.learnedRuleId) {
console.log(`Agent learned a new rule: ${feedback.learnedRuleId}`);
// The agent learned: "for production DMZ hosts, prefer network isolation"
}
console.log(`Updated rules: ${feedback.updatedRules.length}`);
for (const ruleId of feedback.updatedRules) {
console.log(` Modified rule: ${ruleId}`);
}

8. Episodic Memory: Learning from Past Incidents

Episodic memory records past BDI cycles as episodes. The agent can recall similar past incidents to inform its current response.

// Get overall episode statistics
const stats: EpisodeStatsResponse = await client.cognitive.getEpisodeStats({ agentId });
console.log(`\n=== Episodic Memory ===`);
console.log(`Total episodes: ${stats.totalEpisodes}`);
console.log(`Successes: ${stats.successes} (${stats.successRate})`);
console.log(`Failures: ${stats.failures}`);
console.log(`Average reward: ${stats.averageReward}`);
// Recall episodes similar to the current incident
const recalled = await client.cognitive.recallEpisodes({
agentId: agentId,
goalId: goal1.goalId,
context: ['emotet', 'lateral_movement', 'dmz'],
maxResults: 5,
});
console.log(`Recalled ${recalled.count} similar episodes:`);
for (const ep of recalled.episodes) {
console.log(` Episode ${ep.episodeId}: ${ep.outcome} (reward: ${ep.reward})`);
}

9. Inspect Agent State and Motivation

The agent’s extended state reveals its full BDI and motivation structure.

// Basic state: beliefs and goals
const basicState: AgentStateDto = await client.cognitive.getAgent(agentId);
console.log(`\n=== Agent State: ${basicState.name} ===`);
console.log(`Has pending goals: ${basicState.hasPendingGoals}`);
console.log(`\nBeliefs (${basicState.beliefs.length}):`);
for (const b of basicState.beliefs) {
console.log(` [${b.sortName}] confidence: ${b.confidence}, source: ${b.source}`);
}
console.log(`\nGoals (${basicState.goals.length}):`);
for (const g of basicState.goals) {
console.log(` [${g.goalSort ?? 'unknown'}] priority: ${g.priority}, status: ${g.status}`);
}
// Extended state: intentions, drives, and curiosity targets
const extState: ExtendedAgentStateDto = await client.cognitive.getExtendedAgentState({ agentId });
console.log(`\nIntentions (${extState.intentions.length}):`);
for (const intention of extState.intentions) {
console.log(` Goal: ${intention.goalId}`);
console.log(` Progress: step ${intention.currentStep}/${intention.plan.length}`);
console.log(` Commitment strength: ${intention.commitmentStrength}`);
}
// Motivation: drives that influence goal selection
if (extState.motivation) {
console.log(`\nMotivation:`);
console.log(` Dominant drive: ${extState.motivation.dominantDrive ?? 'none'}`);
for (const drive of extState.motivation.drives) {
console.log(` Drive: ${drive.driveType} (activation: ${drive.activation})`);
}
for (const deficit of extState.motivation.deficits) {
console.log(` Deficit: ${deficit.driveType} (urgency: ${deficit.urgency})`);
}
// Curiosity: novel threats get higher attention
for (const target of extState.motivation.curiosityTargets) {
console.log(` Curiosity target: ${target.targetId} (novelty: ${target.novelty})`);
}
}
// Activations
console.log(`\nActivations (${extState.activations.length}):`);
for (const a of extState.activations) {
console.log(` ${a.label ?? a.termId}: total activation = ${a.total}`);
}

10. Inter-Agent Coordination

Create a second agent and demonstrate communication between agents.

// Create a forensics agent
const forensicsAgent = await client.cognitive.createAgent({
name: 'forensics-investigator',
config: {
beliefConfidenceThreshold: 0.5,
enableLearning: true,
maxBeliefHistory: 1000,
maxGoalsPerCycle: 2,
},
});
const forensicsId = forensicsAgent.agentId;
// Send a direct message between agents
const msg = await client.cognitive.sendMessage({
fromAgentId: agentId,
toAgentId: forensicsId,
content: {
action: 'request_forensic_analysis',
host: 'web-prod-03',
indicators: ['198.51.100.42', 'SID-2028765'],
priority: 'critical',
},
priority: 9,
});
console.log(`\nMessage sent: ${msg.messageId}`);
// Broadcast an alert to all agents in the tenant
const broadcast = await client.cognitive.broadcastMessage({
fromAgentId: agentId,
content: {
announcement: 'active_incident',
affected_zone: 'dmz',
threat_type: 'emotet_lateral_movement',
containment_status: 'in_progress',
},
});
console.log(`Broadcast to ${broadcast.recipientsCount} agents`);
// Mark messages as read
await client.cognitive.markMessagesRead({
agentId: forensicsId,
messageIds: [msg.messageId],
});

11. Full Incident Timeline

Here is how all the pieces compose into a complete incident response workflow:

async function runIncidentResponse() {
console.log('\n=== PHASE 1: Detection ===');
// Beliefs arrive from IDS, SIEM, threat intel feeds
// → kb_change events fire on WebSocket
// → Agent correlates IoC with alert via cognitive rules
console.log('\n=== PHASE 2: Triage ===');
// Run BDI cycle
const triage = await client.cognitive.runCycle({
agentId: agentId,
});
console.log(`Triage: ${triage.outcome.goalsPursued.length} goals pursued`);
// → attention_shift events show goal prioritization
// → Agent recalls similar past incidents from episodic memory
console.log('\n=== PHASE 3: Containment ===');
// Agent proposes isolation of compromised host
// → hitl_request event: human analyst must approve
// Human approves with feedback
await client.cognitive.provideFeedback({
agentId: agentId,
resultId: goal1.goalId,
rating: 1,
correction: 'Approved with network isolation',
});
// → Agent learns preference for network isolation on DMZ hosts
// → goal_completed event fires
console.log('\n=== PHASE 4: Eradication ===');
// Agent sends forensics request to forensics agent
await client.cognitive.sendMessage({
fromAgentId: agentId,
toAgentId: forensicsId,
content: { action: 'eradicate', host: 'web-prod-03' },
priority: 8,
});
// Forensics agent handles eradication
console.log('\n=== PHASE 5: Recovery ===');
// Run final BDI cycle to verify all goals achieved
const recovery = await client.cognitive.runCycle({
agentId: agentId,
});
console.log(`Recovery: success rate ${recovery.outcome.successRate}`);
// Final state inspection
const finalState = await client.cognitive.getExtendedAgentState({ agentId });
console.log(`Final intentions: ${finalState.intentions.length}`);
console.log(`Active beliefs: ${finalState.beliefs.length}`);
}
await runIncidentResponse();

12. Cleanup

// Close WebSocket subscription
subscription.close();
console.log('WebSocket closed');
// Delete agents
await client.cognitive.deleteAgent(agentId);
await client.cognitive.deleteAgent(forensicsId);
console.log('Agents deleted');

What You Learned

ConceptWhat It MeansTraditional Alternative
BDI cognitive agentsBeliefs + Desires + Intentions architecture for autonomous reasoningStatic playbooks, rule-based systems without state
Beliefs as psi-termsAgent beliefs carry sort membership and participate in sort hierarchy unificationProlog atoms, flat key-value stores
Goals as typed psi-termsGoals are sorts in the hierarchy — GLB of two goals gives a more specific combined objectiveString-labeled goals with no lattice operations
Cognitive rulesRules that connect beliefs to actions, modifiable at runtime via feedbackStatic configuration files
WebSocket eventsReal-time discriminated union events: kb_change, hitl_request, cycle_complete, attention_shift, goal_completed, impasse_detectedPolling-based APIs
HITL approvalHuman-in-the-loop workflow for high-impact actions with feedback-driven learningAd-hoc ticketing systems
Episodic memoryRecall similar past incidents to inform current responseManual incident databases
Drives and motivationCuriosity and security drives model analyst attention allocation and alert fatigueFixed priority queues
Inter-agent messagingDirect and broadcast communication between agents in the same tenantMessage queues (Kafka, RabbitMQ)

What Comes Next

You have now covered the three core layers of the Reasoning Layer SDK:

  1. Knowledge modeling — sorts, psi-terms, fuzzy values, lattice operations (Tutorial 1)
  2. Inference — rules, backward/forward chaining, NAF, homoiconic introspection (Tutorial 2)
  3. Cognitive agents — BDI reasoning, real-time events, learning, coordination (Tutorial 3)

For additional SDK capabilities, see the guides:

  • Fuzzy Logic — Fuzzy unification, subsumption, and top-K similarity search
  • Causal Reasoning — Pearl’s causal hierarchy, interventions, and counterfactuals
  • Optimization — Linear programming and constraint solving