Event System¶
Mozaiks is event-driven, but not every event means the same thing.
The event system exists to let deterministic app behavior, AI workflow orchestration, UI reactivity, notifications, and hosted product capabilities cooperate without collapsing into one control plane.
Core Rule¶
Event ownership follows the layer that owns the fact.
| Event family | Owner | Purpose | Kernel concern |
|---|---|---|---|
domain.* | app module / app backend | durable business facts after deterministic mutations | no |
workflow.* | workflow runtime | workflow lifecycle facts and workflow-level checkpoints | yes, as execution metadata |
runtime.* | runtime substrate | internal orchestration, validation, fan-out, resume, and control state | yes |
chat.* | runtime transport | live chat transcript and tool execution stream | yes |
artifact.* | runtime or generator workflow | artifact lifecycle facts | yes for transport, no for business meaning |
ui.* | app UI contract | primitive updates and client-side UI reactions | no |
notification.* | platform host notification service | notification lifecycle | no |
platform.* | product/platform layer | hosted product facts | no |
hosted.* | hosted-only capability packs | paid hosted product capabilities | no |
The runtime may transport many event families. Transport is not ownership.
Current frontend event model¶
The frontend currently has three canonical event lanes:
| Lane | Current producer | Current consumer | Purpose |
|---|---|---|---|
chat.* | runtime transport | ChatPage | transcript, lifecycle, tool-call transport |
chat.tool_call | runtime UI tool transport | ChatPage + workflow UI renderer | interactive or artifact workflow surfaces |
ui.* | workflow/page/platform UI emitters | useAppEventBus primitives | typed primitive reactions such as refresh, modal open, stat update |
New interactive workflow UI should follow the chat.tool_call transport path. |
Response-required AG2 input interactions normalize onto chat.tool_call with interaction_type=input_request. That is the only canonical browser-facing lane for runtime-managed interactive input. In chat-ui, generic text input requests now default to display=composer.
Layer Responsibilities¶
Runtime App¶
mozaiksai/hosts/runtime.py and mozaiksai own execution transport:
- WebSocket delivery
- chat stream events
- AG2 event stream handling
- runtime control events
- workflow execution checkpoints
- persistence of runtime session state
- transport envelopes such as
chat.text,chat.run_complete, andchat.tool_call
chat.run_complete is slice-scoped. Clients and orchestrators must read its status field:
1means terminal workflow completion0means the workflow paused awaiting user input and remains resumable
The runtime must not define app business events such as invoices, bookings, campaigns, payments, or app-specific status changes.
Platform App¶
mozaiksai/hosts/platform.py owns app-host event integration:
- module action endpoints
- module event validation
- event ingress from app modules or external app backends
- workflow trigger resolution
- notification/subscription derivation
- page and admin surfaces that react to app state
The platform host connects app facts to workflow execution. It does not make AI workflow stream events into app facts.
Current implementation:
ModuleExecutorinjectsModuleContext.emit(...)into module handlers.- Module handlers emit canonical envelopes through the runtime dispatcher.
ModuleEventRouteris registered bymozaiksai/hosts/platform.pyafter app modules are loaded.ModuleEventRouterreads each loaded module'ssubscriptions.yamlandnotifications.yaml.- Notification targets create platform notification intents and emit
notification.created. - Capability targets resolve against workflow
orchestrator.yamltrigger declarations by matchingcapability_id. mozaiksai/hosts/platform.pyindexes capability-triggered workflows fromworkflows/*/orchestrator.yaml, creates the routed chat session, and auto-starts background execution when transport is available.- Capability resolution emits
platform.workflow_capability_started. - Handler targets route the event payload directly to a named module action method, enabling module-to-module event-driven calls without HTTP indirection.
Subscription target reference¶
# subscriptions.yaml
subscriptions:
- id: my_subscription
event: domain.tasks.task_created # event type to react to
# Target kind 1: notification
# Creates a platform notification intent from notifications.yaml rule.
target:
kind: notification
notification_id: task_created
- id: my_workflow_trigger
event: domain.tasks.task_created
# Target kind 2: capability
# Resolves capability_id against workflow orchestrator.yaml triggers.
# Starts a workflow session when a matching trigger is found.
target:
kind: capability
capability_id: tasks.review
- id: my_handler_call
event: hosted.other_module.something_happened
# Target kind 3: handler (module-to-module event routing)
# Routes payload fields as keyword arguments to the named module action.
# Format: hosted.{module_id}.{handler_method_name}
# The receiving handler method must accept the event payload fields as kwargs.
handler: hosted.{module_id}.{handler_method_name}
Handler target rules:
- Use
handler:(nottarget: kind: handler) — it is a top-level key. - The handler reference format is
hosted.{module_id}.{method_name}. - The event payload is unpacked as keyword arguments into the handler method.
- The receiving method must be declared as an action in its
module.yamlor be a documented subscription handler on the handler class. - Handler targets are for deterministic module-to-module reactions. For AI-driven reactions, use a capability target to trigger a workflow instead.
/api/notifications/countreads unread platform notification intents for the current principal.
Workflow-trigger resolution stays platform-owned: modules publish validated domain.* or hosted.* events, and the platform host resolves them to workflow sessions without module code importing workflow internals.
Studio And Mozaiks Product¶
mozaiksai/hosts/studio.py and mozaiksai/hosts/mozaiks.py own product-layer events:
- build lifecycle
- app project lifecycle
- hosted collaboration
- marketplace
- hosted billing and revenue-share capabilities
These are product facts, not universal runtime assumptions.
Hosted product modules currently center on examples such as:
investor_marketplace, which publisheshosted.marketplace.*communications, which publisheshosted.communication.*
Those events can drive Studio, admin, marketplace, notification, and hosted collaboration behavior. They must not be treated as generic generated-app events.
Modules¶
Modules own deterministic app facts. A module action may publish domain.* events only after it commits the corresponding state change.
Example:
POST /api/modules/tasks/create
-> tasks handler validates and saves task
-> emits domain.tasks.task_created
-> platform host resolves subscriptions and workflow triggers
Workflows¶
Workflows own AI orchestration events, not app business facts.
A workflow may:
- emit
workflow.*checkpoints - emit
runtime.*events through AG2 custom-event handling - emit
ui.*events to update primitive UI - emit interactive UI surfaces through workflow UI tool transport, which the runtime currently serializes as
chat.tool_call - call a module action that then emits
domain.*
A workflow should not directly invent durable domain facts unless it is calling the app/module contract that owns that fact.
Frontend handling¶
1. chat.* transport stream¶
ChatPage is the primary consumer of runtime transport events.
It handles:
- transcript stream events such as
chat.text - lifecycle events
- tool-call envelopes
- AG-UI state replay/snapshot events
The surface reducer observes that stream to keep layout and workflow state in sync.
2. Workflow UI tool lane¶
Interactive workflow-owned UI is a chat.tool_call transport concern.
Current canonical runtime path:
workflow tool
-> transport.send_tool_call_event(...)
-> runtime emits kind=tool_call
-> transport maps to chat.tool_call
-> ChatPage inserts `toolCall` artifact/inline message state
-> WorkflowUIRouter resolves workflow-scoped component
AG2 InputRequestEvent now follows the same transport lane, using interaction_type=input_request. Generic text reply defaults to display=composer; UserInputRequest remains the fallback component identity when a workflow explicitly wants inline input or the request cannot use the composer (for example, password entry).
Key implications:
- workflow-owned artifact cards and interactive panels are not
ui.*primitive events - they are chat-session-scoped UI surfaces
- their display mode (
composer,inline,artifact,view) influences the frontend surface state machine
3. Typed ui.* primitive lane¶
Primitive UI events are a separate lane from workflow UI tools.
Current canonical frontend path:
typed websocket envelope with type=ui.*
-> useCoreWebSocket.ingestEvent(...)
-> useAppEventBus
-> primitive subscription by component_id or modal_id
This lane is for lightweight browser reactions:
ui.datatable.refreshui.form.set_fieldui.form.resetui.stat.updateui.modal.openui.modal.closeui.alert.show
These events update existing surfaces. They do not mount workflow-owned React artifact components.
4. Client-only routing lane¶
routing.transition.* is a local shell bus, not a runtime/domain event family.
It is used for transition UI:
- transition component emits
routing.transition.resolve - shell resolves navigation or chained transition state
Keep it separate from business and runtime events.
Canonical Event Loop¶
user action or integration
-> module action
-> deterministic state commit
-> domain.* event
-> platform host validates event
-> subscriptions / notifications / workflow triggers resolve
-> notification.created emitted when a notification rule matches
-> workflow starts or resumes when configured
-> runtime emits chat.*, runtime.*, workflow.*, artifact.* stream events
-> UI renders stream and primitive updates
-> workflow saves results through module actions when app state changes
-> module emits new domain.* event if another durable fact became true
Where Events Are Declared¶
| Contract | File |
|---|---|
| Module-published domain events | modules/{module}/events.yaml |
| Module event reactions | modules/{module}/subscriptions.yaml |
| Notification derivation | modules/{module}/notifications.yaml |
| Workflow trigger policy | workflows/{workflow}/orchestrator.yaml |
| Workflow runtime stream and AG2 custom event types | runtime code in mozaiksai/core/events/ |
| Page primitive UI reactions | ui/pages/*.yaml and primitive component contracts |
| Hosted-only product events | hosted capability pack contracts |
Persistent pages may also launch workflow sessions through declarative page actions, but that is a page-action contract, not an event-stream contract. Those launches use runtime workflow ids and session routing, not chat.tool_call.
orchestrator.yaml¶
orchestrator.yaml is not the global event catalog.
It may declare:
That means the workflow wants to react to a domain event. The event itself is owned by the module that publishes it.
AG2 Custom Events¶
AG2 custom events are runtime execution events. Use them for AI/runtime facts:
- agent produced validated structured output
- decomposition was planned
- artifact became ready
- handoff was requested
- user input is required
Do not use AG2 custom events as app business events. If the AI result changes app state, the workflow must call a module action and let the module publish the domain event after the state commit.
UI Events¶
ui.* events are client reaction commands, not durable app facts.
Examples:
ui.datatable.refreshui.form.set_fieldui.stat.updateui.modal.open
These events may be emitted by a workflow or a page action. They update the live browser surface and should not be used for business correctness.
ui.* is also distinct from workflow UI tool transport:
- use
ui.*for primitive refresh/open/update actions - use workflow UI tools for rendered interactive cards, forms, artifact panels, and workflow-specific React surfaces
Hosted Capability Events¶
Hosted-only capabilities such as MozaiksPay use hosted.* or product-scoped platform.* events. They plug into the platform/product layer as capability packs. They must not become kernel assumptions.
Collapse Rules¶
Do not:
- encode workflow names into domain event names
- use
chat.*messages as durable business facts - let modules publish
runtime.*orworkflow.* - let hosted billing/revenue-share events leak into the runtime kernel
- let
orchestrator.yamlbecome the app event catalog - let UI events substitute for committed app state
- treat workflow UI tools and primitive
ui.*events as the same lane - let workflows publish notification records directly when a module event and notification rule should own the notification