Events are the core communication mechanism between Cordis plugins. Harness uses them extensively for loosely coupled extension points.
## Basic use
### Listen for an event
```ts ignore-check
ctx.on('event-name', (payload) => {
// Handle the event.
})
```
### Emit an event
```ts ignore-check
ctx.emit('event-name', payload)
```
## Event modes
Cordis provides several event modes for different interaction contracts.
### emit — broadcast
Every listener runs synchronously and return values are ignored:
```ts ignore-check
// Emit
ctx.emit('my-plugin/ready', { id: 'worker-1' })
// Listen
ctx.on('my-plugin/ready', ({ id }) => {
console.log(`${id} is ready`)
})
```
### bail — short circuit
Listeners run in order; the first non-`undefined` result becomes the final result:
```ts ignore-check
// Dispatch
const result = ctx.bail('some-check', input)
// Listen: a returned value stops later listeners.
ctx.on('some-check', (input) => {
if (shouldBlock(input)) return 'blocked'
// Return undefined to continue to the next listener.
})
```
### serial — ordered execution
Listeners run in registration order and asynchronous results are awaited. The first listener to return a non-empty value stops further execution:
```ts ignore-check
await ctx.serial('setup-phase', context)
```
### waterfall — pipeline
Each listener may wrap the downstream result to form a processing chain. A listener **must call `next()` to delegate downstream**; omitting the call vetoes the pipeline:
Harness Cordis events use `namespace/action` names, including `agent/step`, `agent/request`, `agent/request-error`, `tools/result`, and `session/event`. The generated `cordis-surface` regions on the [subsystem pages](../../../subsystems/core.md) record complete signatures and modes.
`turn/*`, `step/*`, `tool/call`, `tool/result`, and `compact/*` are durable session-event types, not same-named Cordis events. To observe them, listen to `session/event` and inspect `event.type`.
## Event listeners are effects
A listener registered with `ctx.on()` is removed automatically when its plugin unloads:
```ts ignore-check
export function apply(ctx: Context) {
// This listener is removed when the plugin disposes.