How to Define a Custom RuntimeEvent in Apache Maka
To define a custom RuntimeEvent in Apache Maka, you must declare a new payload interface with a unique literal kind property, extend the RuntimeEvent discriminated union in packages/core/src/runtime-event.ts, and emit the event via the RuntimeEventStore.append() method.
Apache Maka treats every meaningful fact of agent execution as a RuntimeEvent, using a discriminated union pattern where the kind field determines the payload shape. When you define a custom RuntimeEvent in Apache Maka, you extend this type-safe event log to capture domain-specific facts while maintaining full TypeScript exhaustiveness checking across read models and storage layers.
Step 1: Declare the Custom Event Payload Interface
Begin by defining a new interface in packages/core/src/runtime-event.ts following the established pattern of built-in types like RuntimeEventSystemNoteContent (lines 41-46) or RuntimeEventErrorContent (lines 48-56). The interface must include a literal kind property that is unique across all events.
// packages/core/src/runtime-event.ts
export interface RuntimeEventMyCustomContent {
kind: 'my_custom'; // <-- unique literal string
/** Any structured data you want to capture */
meta: Record<string, unknown>;
/** Optional human-readable description */
description?: string;
}
The kind property acts as the discriminant tag. TypeScript uses this literal to narrow the union type when processing events in switch statements or if-else chains.
Step 2: Extend the RuntimeEvent Discriminated Union
Locate the RuntimeEvent type definition near the bottom of packages/core/src/runtime-event.ts and append your new interface to the union. This step ensures that all consumers—persistence stores, UI projections, and read models—recognize the new shape through exhaustive type checking.
// packages/core/src/runtime-event.ts
export type RuntimeEvent =
| RuntimeEventTextContent
| RuntimeEventThinkingContent
| RuntimeEventFunctionCallContent
| RuntimeEventFunctionResponseContent
| RuntimeEventSystemNoteContent
| RuntimeEventErrorContent
// <-- add your custom payload here
| RuntimeEventMyCustomContent;
Failing to add the interface to this union will result in TypeScript errors when attempting to store or project the event, as the type system will not recognize the payload as a valid runtime event.
Step 3: Persist and Emit the Custom Event
Emit the event by calling RuntimeEventStore.append() from your application logic. The SQLite-based store at packages/storage/src/sqlite-runtime-store.ts stores events in a generic JSON column, so no database schema migration is required for new event kinds.
import { runtimeEventStore } from '@maka/storage';
import { RuntimeEventMyCustomContent } from '@maka/core';
const myEvent: RuntimeEventMyCustomContent = {
kind: 'my_custom',
meta: { taskId: '1234', status: 'queued' },
description: 'Task queued by the scheduler',
};
await runtimeEventStore.append(myEvent);
The runtimeEventStore serializes the payload and appends it to the event log, making it available for asynchronous projections and historical queries.
Step 4: Consume the Event in Read Models (Optional)
Most runtime-event projections operate on the generic RuntimeEvent type, but you can add specialized handling logic by checking the kind discriminator. Update mapper files such as packages/runtime/src/session-event-runtime-mapper.ts or custom RuntimeEventReadModel implementations to handle your specific event type.
// packages/runtime/src/runtime-event-read-model.ts
if (event.kind === 'my_custom') {
// TypeScript narrows `event` to RuntimeEventMyCustomContent here
console.log('Processing custom event:', event.meta.taskId);
}
Key Files Involved in Custom RuntimeEvent Definition
| File | Role |
|---|---|
packages/core/src/runtime-event.ts |
Central definition of all payload interfaces and the discriminated union |
packages/storage/src/sqlite-runtime-store.ts |
Persists events using a JSON column; requires no schema changes for new kinds |
packages/runtime/src/runtime-event-read-model.ts |
Example read-model projection that consumes runtime events |
Summary
- Declare the payload: Create an interface in
packages/core/src/runtime-event.tswith a unique literalkindproperty following the pattern ofRuntimeEventErrorContentorRuntimeEventSystemNoteContent. - Extend the union: Add the interface to the
RuntimeEventdiscriminated union type to enable type-safe consumption across the framework. - Emit via store: Use
RuntimeEventStore.append()from@maka/storageto persist events without database migrations. - Project selectively: Handle custom events in read models by discriminating on the
kindfield in mappers or custom projections.
Frequently Asked Questions
Where is the RuntimeEvent union type defined?
The RuntimeEvent union type is defined at the bottom of packages/core/src/runtime-event.ts. This file contains all payload interfaces and the central union that discriminates between them based on the kind property.
Do I need to migrate the database schema for new event types?
No. The default SQLite storage implementation in packages/storage/src/sqlite-runtime-store.ts uses a generic JSON column to store event payloads. Because the schema stores arbitrary JSON, adding new event kinds requires no DDL changes. However, if you implement a custom storage backend with strict column typing, you must ensure the schema accommodates the new payload shape.
How do I ensure my custom event kind is unique?
Choose a descriptive literal string for the kind property that follows the existing snake_case convention (e.g., my_custom_feature). Verify uniqueness by checking the existing payload interfaces in packages/core/src/runtime-event.ts to ensure no other interface declares the same literal. TypeScript will also flag overlapping types if the discriminant is not unique.
Can I emit custom events from any part of the application?
Yes. Any code that can import runtimeEventStore from @maka/storage and your custom event type from @maka/core can emit events. Ensure the event object conforms to your declared interface and call await runtimeEventStore.append(event) to persist it to the runtime log.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →