Skip to main content
Comis uses a TypedEventBus for cross-module communication. Instead of packages importing each other directly, they emit and subscribe to typed events. The bus provides compile-time safety — TypeScript enforces that you emit events with the correct payload shape and subscribe to events that actually exist.

How It Works

The TypedEventBus wraps Node.js EventEmitter with a type-safe generic interface. All typed events are defined in the EventMap interface, which is composed from 5 domain-specific sub-interfaces (MessagingEvents, AgentEvents, ChannelEvents, InfraEvents, TerminalEvents). API methods include emit(), on(), off(), once(), removeAllListeners(), listenerCount(), and setMaxListeners(). All methods are constrained by EventMap — you cannot emit an event name that does not exist in the map, and every payload is type-checked at compile time.
import type { TypedEventBus, EventMap } from "@comis/core";

// Subscribe to an event
eventBus.on("message:received", (payload) => {
  // TypeScript knows payload has: message, sessionKey
  console.log(`Message received in session ${payload.sessionKey}`);
});

// Emit an event (payload type-checked at compile time)
eventBus.emit("tool:executed", {
  toolName: "web_search",
  durationMs: 1500,
  success: true,
  timestamp: Date.now(),
});

Event Naming Convention

Events follow a subsystem:action naming pattern (for example, message:received, tool:executed, graph:started). Events are organized into 5 subsystems:
SubsystemEvent CountScope
MessagingEvents38Message lifecycle, sessions, subagent lifecycle, compaction, context, execution control, announcements
AgentEvents43Skills, tools, audit, observability, model selection, security, graphs, cache, SEP
ChannelEvents43Channel lifecycle, queuing, streaming, typing, auto-reply, policies, delivery, steer
InfraEvents55Approvals, config, plugins, hooks, auth, diagnostics, media, scheduler, system, MCP, notifications, background tasks
TerminalEvents4Terminal session lifecycle, keystrokes, session eviction

Most Commonly Used Events

A quick reference for the events developers are most likely to need when building plugins or adapters:
EventSubsystemWhen It FiresTypical Use
message:receivedMessagingIncoming message from any channelLogging, analytics
message:sentMessagingAgent sends a responseDelivery tracking
tool:executedAgentAfter a tool completesAudit logging, metrics
session:createdMessagingNew conversation session beginsSession setup hooks
session:expiredMessagingConversation session endsCleanup, analytics
channel:registeredChannelChannel adapter registeredHealth monitoring
queue:enqueuedChannelMessage enters command queueQueue depth monitoring
graph:startedAgentExecution graph beginsPipeline monitoring
graph:completedAgentExecution graph finishesPipeline results
config:patchedInfraRuntime config modifiedConfig-reactive behavior
plugin:registeredInfraPlugin loadedPlugin lifecycle tracking
diagnostic:message_processedInfraFull message lifecycle completeEnd-to-end metrics

Complete Event Reference

Message lifecycle, session management, subagent lifecycle, compaction, context engine lifecycle, context engine metrics, response filtering, execution control, and dead-letter queue events.
Event NameKey Payload Fields
announcement:dead_letteredrunId, channelType, reason, timestamp
announcement:dead_letter_deliveredrunId, channelType, attemptCount, timestamp
compaction:startedagentId, sessionKey, timestamp
compaction:recommendedagentId, sessionKey, contextPercent, contextTokens, contextWindow, timestamp
compaction:flushsessionKey, memoriesWritten, trigger, success, timestamp
context:maskedagentId, sessionKey, maskedCount, totalChars, persistedToDisk, timestamp
context:compactedagentId, sessionKey, fallbackLevel, attempts, originalMessages, keptMessages, timestamp
context:rehydratedagentId, sessionKey, sectionsInjected, filesInjected, skillsInjected, overflowStripped, timestamp
context:overflowagentId, sessionKey, contextTokens, budgetTokens, recoveryAction, timestamp
context:evictedagentId, sessionKey, evictedCount, evictedChars, categories, timestamp
context:rereadagentId, sessionKey, rereadCount, rereadTools, timestamp
context:pipelineagentId, sessionKey, tokensLoaded, tokensEvicted, tokensMasked, tokensCompacted, thinkingBlocksRemoved, budgetUtilization, evictionCategories, rereadCount, rereadTools, sessionDepth, sessionToolResults, cacheHitTokens, cacheWriteTokens, cacheMissTokens, cacheFenceIndex, durationMs, layerCount, layers, timestamp
context:pipeline:cacheagentId, sessionKey, cacheHitTokens, cacheWriteTokens, cacheMissTokens, timestamp
context:dag_compactedconversationId, agentId, sessionKey, leafSummariesCreated, condensedSummariesCreated, maxDepthReached, totalSummariesCreated, durationMs, timestamp
context:integrityconversationId, agentId, sessionKey, issueCount, repairsApplied, errorsLogged, issueTypes, durationMs, timestamp
execution:abortedsessionKey, reason, agentId, timestamp
execution:budget_warningagentId, sessionKey, totalTokens, llmCallCount, projectedCallsLeft, timestamp
execution:output_escalatedagentId, sessionKey, originalMaxTokens, escalatedMaxTokens, timestamp
execution:prompt_timeoutagentId, sessionKey, timeoutMs, timestamp
execution:signed_replay_recoveredagentId, sessionKey, blocksRemoved, thoughtSignaturesStripped, succeeded, timestamp
message:receivedmessage, sessionKey
message:sentchannelId, messageId, content
message:streamingchannelId, messageId, delta, accumulated
response:filteredchannelId, suppressedBy, timestamp
session:createdsessionKey, timestamp
session:expiredsessionKey, reason
session:cross_sendfromSessionKey, toSessionKey, mode, timestamp
session:ping_pong_turnfromSessionKey, toSessionKey, turnNumber, totalTurns, tokensUsed, timestamp
session:sub_agent_spawnedrunId, parentSessionKey, agentId, task, timestamp
session:sub_agent_completedrunId, agentId, success, runtimeMs, tokensUsed, cost, timestamp, cacheReadTokens, cacheWriteTokens
session:sub_agent_archivedrunId, sessionKey, ageMs, timestamp
session:sub_agent_spawn_preparedrunId, parentSessionKey, agentId, task, depth, maxDepth, artifactCount, timestamp
session:sub_agent_spawn_queuedrunId, parentSessionKey, agentId, task, queuePosition, activeChildren, maxChildren, timestamp
session:sub_agent_spawn_rejectedparentSessionKey, agentId, task, reason, currentDepth, maxDepth, currentChildren, maxChildren, timestamp
session:sub_agent_spawn_startedrunId, parentSessionKey, agentId, task, depth, timestamp
session:sub_agent_result_condensedrunId, agentId, level, originalTokens, condensedTokens, compressionRatio, taskComplete, diskPath, timestamp
session:sub_agent_lifecycle_endedrunId, agentId, parentSessionKey, endReason, durationMs, tokensUsed, cost, condensationLevel, timestamp
The context:* events provide per-turn observability into context engine decisions:
  • context:evicted fires when context history is dropped to fit the token budget — in pipeline mode when the dead content evictor removes superseded tool results, and in DAG mode when the LCD assembler evicts older history under budget (categories.lcd_history carries the dropped count). Counts and ids only; never message content.
  • context:reread fires when the re-read detector identifies duplicate tool calls in the session (pipeline mode).
  • context:pipeline fires after every pipeline run with a full metrics snapshot (pipeline mode). This is always emitted, even when no optimization was needed.
  • context:pipeline:cache fires post-LLM to patch cache-specific metrics (cache hit/write/miss tokens) once the API response is available.
  • context:dag_compacted fires after a DAG compaction pass creates new summaries (DAG mode).
  • context:integrity fires when the DAG integrity checker detects and repairs structural issues (DAG mode).
See Compaction for details on what triggers each event.
Skill lifecycle, tool execution, audit logging, observability (tokens and latency), cache break detection, model failover, security, execution graph, provider health, SEP (step-execution planning), and exec command blocking events.
Event NameKey Payload Fields
audit:eventtimestamp, agentId, tenantId, actionType, classification, outcome, metadata
cache:graph_prefix_writtengraphId, nodeId, cacheWriteTokens, timestamp
command:blockedagentId, commandPrefix, reason, blocker, timestamp
graph:startedgraphId, label, nodeCount, timestamp
graph:node_updatedgraphId, nodeId, status, previousStatus, durationMs, error, timestamp
graph:completedgraphId, status, durationMs, nodeCount, nodesCompleted, nodesFailed, nodesSkipped, cancelReason, timestamp, graphCacheReadTokens, graphCacheWriteTokens, graphCacheEffectiveness, nodeEffectiveness
graph:driver_lifecyclegraphId, nodeId, typeId, phase
memory:review_completedagentId, sessionsReviewed, memoriesExtracted, duplicatesSkipped, durationMs, timestamp
model:auth_cooldownkeyName, provider, cooldownMs, failureCount, timestamp
model:catalog_loadedproviderCount, modelCount, timestamp
model:fallback_attemptfromProvider, fromModel, toProvider, toModel, error, attemptNumber, timestamp
model:fallback_exhaustedprovider, model, totalAttempts, timestamp
observability:cache_breakprovider, reason, tokenDrop, tokenDropRelative, previousCacheRead, currentCacheRead, callCount, changes, toolsChanged, ttlCategory, agentId, sessionKey, timestamp, toolsAdded, toolsRemoved, toolsSchemaChanged, systemCharDelta, model, effortValue
observability:latencyoperation, durationMs, timestamp, metadata
observability:token_usagetimestamp, traceId, agentId, channelId, executionId, provider, model, tokens, cost, latencyMs, cacheReadTokens, cacheWriteTokens, sessionKey, savedVsUncached, cacheEligible, responseId, cacheCreation
provider:degradedprovider, failingAgents, timestamp
provider:recoveredprovider, timestamp
security:injection_detectedtimestamp, source, patterns, riskLevel, agentId, sessionKey, traceId
security:injection_rate_exceededtimestamp, sessionKey, count, threshold, action
security:memory_taintedtimestamp, agentId, originalTrustLevel, adjustedTrustLevel, patterns, blocked
sender:trust_resolvedagentId, senderId, trustLevel, displayMode, sessionKey, timestamp
sep:plan_extractedagentId, sessionKey, stepCount, timestamp
skill:createdskillName, scope, agentId, timestamp
skill:executedskillName, durationMs, success, timestamp
skill:failedskillName, error, phase, agentId, timestamp
skill:loadedskillName, source, timestamp
skill:prompt_invokedskillName, invokedBy, args, timestamp
skill:prompt_loadedskillName, source, bodyLength, timestamp
skill:registry_resetclearedMetadata, clearedPromptCache, timestamp
skill:rejectedskillName, reason, violations, timestamp
skill:updatedskillName, scope, agentId, timestamp
skills:reloadedagentId, skillCount, timestamp
tool:executedtoolName, durationMs, success, timestamp, userId, traceId, agentId, sessionKey, params, errorMessage, errorKind, description, truncated, fullChars, returnedChars
tool:policy_filteredprofile, agentId, filtered, timestamp
tool:startedtoolName, toolCallId, timestamp, agentId, sessionKey, traceId, description
The graph:driver_lifecycle event fires at each phase transition during typed node execution. The phase field indicates which stage the driver has reached:
PhaseWhen It FiresDescription
initializedAfter the coordinator calls initialize() on the driver and receives the first actionThe driver has started execution and returned its initial action (typically spawn or spawn_all)
progressWhen the driver returns a progress actionThe driver is reporting intermediate status (e.g., “Round 2 of 3 complete”). Used for observability and UI updates
completedWhen the driver returns a complete action with final outputThe node transitions to the completed state. Output is stored and dependent nodes become eligible for scheduling
partial_completeWhen the driver returns a partial_complete actionPartial results available (e.g., some sub-tasks succeeded, others failed)
failedWhen the driver returns a fail action, or when an unhandled error occurs during driver executionThe node transitions to the failed state. Dependent nodes may be skipped depending on the graph’s onFailure strategy
abortedWhen the coordinator calls onAbort() due to timeout, cancellation, or graph shutdownThe node transitions to the failed state with an abort reason. Resources are cleaned up
Subscribe to this event for driver-level monitoring, logging, or triggering downstream actions based on typed node progress. See the Pipelines: Driver Lifecycle documentation for the full execution flow.Cache cost fields (added in v58.0): The observability:token_usage event includes prompt caching data when the provider supports it. cost.cacheRead and cost.cacheWrite are dollar costs for cached token reads/writes. savedVsUncached is the net savings compared to uncached pricing (positive = money saved). cacheEligible indicates whether the model supports prompt caching. sessionKey identifies the conversation session for per-session cost aggregation.
Channel registration, sender blocking, command queuing, streaming delivery, typing indicators, auto-reply, send policies, debounce, group history, follow-up chains, priority lanes, elevated routing, retry engine, ack reactions, steer lifecycle, block coalescing, unified delivery pipeline, delivery queue, channel health monitoring, delivery hooks, and sub-agent proxy typing events.
Event NameKey Payload Fields
ack:reaction_sentchannelId, channelType, messageId, emoji, timestamp
autoreply:activatedchannelId, senderId, activationMode, reason, timestamp
autoreply:suppressedchannelId, senderId, reason, injectedAsHistory, timestamp
channel:deregisteredchannelType, pluginId, timestamp
channel:health_changedchannelType, previousState, currentState, connectionMode, error, lastMessageAt, timestamp
channel:health_checkchannelType, state, responseTimeMs, timestamp
channel:registeredchannelType, pluginId, capabilities, timestamp
coalesce:flushedchannelId, chatId, blockCount, charCount, trigger, timestamp
debounce:bufferedsessionKey, channelType, bufferedCount, windowMs, timestamp
debounce:flushedsessionKey, channelType, messageCount, trigger, timestamp
delivery:abortedchannelId, channelType, reason, chunksDelivered, totalChunks, durationMs, origin, timestamp
delivery:ackedentryId, channelId, channelType, messageId, durationMs, timestamp
delivery:chunk_sentchannelId, channelType, chunkIndex, totalChunks, charCount, ok, retried, timestamp
delivery:completechannelId, channelType, totalChunks, deliveredChunks, failedChunks, totalChars, durationMs, origin, strategy, timestamp
delivery:enqueuedentryId, channelId, channelType, origin, timestamp
delivery:failedentryId, channelId, channelType, error, reason, timestamp
delivery:hook_cancelledchannelId, channelType, reason, origin, timestamp
delivery:nackedentryId, channelId, channelType, error, attemptCount, nextRetryAt, timestamp
delivery:queue_drainedentriesAttempted, entriesDelivered, entriesFailed, durationMs, timestamp
elevated:model_routedsessionKey, senderTrustLevel, modelRoute, agentId, timestamp
followup:depth_exceededsessionKey, chainId, maxDepth, timestamp
followup:enqueuedsessionKey, channelType, reason, chainId, chainDepth, timestamp
grouphistory:injectedsessionKey, channelType, messageCount, charCount, timestamp
priority:aged_promotionsessionKey, fromLane, toLane, waitTimeMs, timestamp
priority:lane_assignedsessionKey, channelType, lane, reason, timestamp
queue:coalescedsessionKey, channelType, messageCount, timestamp
queue:dequeuedsessionKey, channelType, waitTimeMs, timestamp
queue:enqueuedsessionKey, channelType, queueDepth, mode, timestamp
queue:overflowsessionKey, channelType, policy, droppedCount, timestamp
retry:attemptedchannelId, chatId, attempt, maxAttempts, delayMs, error, timestamp
retry:exhaustedchannelId, chatId, totalAttempts, finalError, timestamp
retry:markdown_fallbackchannelId, chatId, originalParseMode, timestamp
sender:blockedchannelType, senderId, channelId, timestamp
sendpolicy:allowedchannelId, channelType, chatType, reason, timestamp
sendpolicy:deniedchannelId, channelType, chatType, reason, timestamp
sendpolicy:override_changedsessionKey, override, changedBy, timestamp
steer:followup_queuedsessionKey, channelType, agentId, reason, timestamp
steer:injectedsessionKey, channelType, agentId, timestamp
steer:rejectedsessionKey, channelType, agentId, reason, timestamp
streaming:block_sentchannelId, chatId, blockIndex, totalBlocks, charCount, timestamp
typing:proxy_startrunId, channelType, channelId, parentSessionKey, agentId, threadId, timestamp
typing:proxy_stoprunId, channelType, channelId, reason, durationMs, timestamp
typing:startedchannelId, chatId, mode, timestamp
typing:stoppedchannelId, chatId, durationMs, timestamp
Approval gates, config patches, plugin lifecycle, hook execution, auth token rotation, diagnostics, media file extraction, scheduler (cron, heartbeat, tasks), process metrics, observability admin, agent hot-add/remove, MCP server connection lifecycle, notifications, background task lifecycle, system lifecycle, secret management, security warnings, and lifecycle reaction events.
Event NameKey Payload Fields
agent:hot_addedagentId, timestamp
agent:hot_removedagentId, timestamp
approval:requestedrequestId, toolName, action, params, agentId, sessionKey, trustLevel, createdAt, timeoutMs, channelType
approval:resolvedrequestId, approved, approvedBy, reason, resolvedAt
auth:token_rotatedprovider, profileName, expiresAtMs, timestamp
background_task:cancelledagentId, taskId, toolName, timestamp
background_task:completedagentId, taskId, toolName, durationMs, timestamp
background_task:failedagentId, taskId, toolName, error, durationMs, timestamp
background_task:promotedagentId, taskId, toolName, timestamp
config:patchedsection, key, patchedBy, timestamp
diagnostic:billing_snapshotproviders, totalCost, timestamp
diagnostic:channel_healthchannels, timestamp
diagnostic:message_processedmessageId, channelId, channelType, agentId, sessionKey, receivedAt, executionDurationMs, deliveryDurationMs, totalDurationMs, tokensUsed, cost, success, finishReason, timestamp
diagnostic:webhook_deliveredwebhookId, source, event, statusCode, success, durationMs, error, timestamp
hook:executedhookName, pluginId, durationMs, success, error, timestamp
mcp:server:disconnectedserverName, reason, timestamp
mcp:server:reconnect_failedserverName, attempts, lastError, timestamp
mcp:server:reconnectedserverName, attempt, toolCount, durationMs, timestamp
mcp:server:reconnectingserverName, attempt, maxAttempts, nextDelayMs, timestamp
mcp:server:tools_changedserverName, previousToolCount, currentToolCount, addedTools, removedTools, timestamp
media:file_extractedfileName, mimeType, chars, truncated, durationMs, timestamp
media:file_persistedrelativePath, mimeType, sizeBytes, mediaKind, agentId, timestamp
notification:deliveredagentId, channelType, channelId, messageId, durationMs, timestamp
notification:enqueuedagentId, priority, channelType, channelId, origin, timestamp
notification:suppressedagentId, reason, priority, timestamp
observability:metricsrssBytes, heapUsedBytes, heapTotalBytes, externalBytes, eventLoopDelayMs, activeHandles, uptimeSeconds, timestamp
observability:resetadmin, table, rowsDeleted, timestamp
plugin:deactivatedpluginId, reason, timestamp
plugin:registeredpluginId, pluginName, hookCount, timestamp
reaction:cleanupmessageId, channelType, channelId, chatId, removedEmoji, timestamp
reaction:phase_changedmessageId, channelType, channelId, chatId, phase, emoji, previousPhase, timestamp
reaction:stall_detectedmessageId, channelType, channelId, chatId, phase, severity, stallMs, timestamp
reaction:terminalmessageId, channelType, channelId, chatId, phase, emoji, timestamp
scheduler:heartbeat_alertagentId, consecutiveErrors, classification, reason, backoffMs, timestamp
scheduler:heartbeat_checkchecksRun, alertsRaised, timestamp
scheduler:heartbeat_deliveredagentId, channelType, channelId, chatId, level, outcome, reason, durationMs, timestamp
scheduler:job_completedjobId, jobName, agentId, durationMs, success, error, timestamp
scheduler:job_resultjobId, jobName, agentId, result, success, deliveryTarget, timestamp, payloadKind, sessionStrategy, maxHistoryTurns, cronJobModel, onComplete
scheduler:job_startedjobId, jobName, agentId, timestamp
scheduler:job_suspendedjobId, jobName, agentId, consecutiveErrors, lastError, timestamp, deliveryTarget
secret:accessedsecretName, agentId, outcome, timestamp
secret:modifiedsecretName, action, timestamp
security:warncategory, agentId, message, timestamp
system:errorerror, source
system:shutdownreason, graceful
Terminal session lifecycle, spawn failures, keystroke forwarding, and session pool eviction events.
Event NameKey Payload Fields
terminal:session_stateTerminal session state change (created, active, idle, closed)
terminal:spawn_failedTerminal session spawn failed
terminal:keystrokeKeystroke sent to a terminal session
terminal:session_evictedTerminal session evicted from the session pool
Source: packages/core/src/event-bus/events-terminal.ts
All event payloads are TypeScript interfaces. Import EventMap from @comis/core and use your IDE’s type inference to explore the exact payload shape for any event.

Subscribing to Events

Plugins subscribe to events via lifecycle hooks, not directly on the event bus. The hook system provides priority ordering and result merging.
import type { PluginPort, PluginRegistryApi } from "@comis/core";
import { ok, type Result } from "@comis/shared";

const metricsPlugin: PluginPort = {
  id: "metrics-collector",
  name: "Metrics Collector",
  register(registry: PluginRegistryApi): Result<void, Error> {
    // Count tool calls (void hook -- fire and forget)
    registry.registerHook("after_tool_call", (event) => {
      metrics.increment("tool_calls", { tool: event.toolName });
    });
    return ok(undefined);
  },
};
Plugins subscribe to events via lifecycle hooks (see Plugins), not directly on the event bus. The hook system provides priority ordering and result merging. Direct eventBus.on() is available for internal subsystem wiring in the composition root.

Architecture

TypedEventBus in the hexagonal architecture

Plugins

Hook into events via the plugin system

Custom Adapters

Adapters emit channel events

Packages

Which package owns which events