$dshubuser@dshub:~/plugins$
  • Plugins
  • Submit
  • Blog
Events: Let Plugins Cooperate Without Knowing Who Listens
2026/08/25

Events: Let Plugins Cooperate Without Knowing Who Listens

Use typed events and five dispatch modes to understand how Cordis broadcasts facts, runs listeners concurrently, or hands over decisions.

The distinction: services are for direct calls; events notify unknown consumers. The dispatch mode defines waiting, return values, and short-circuit behavior.

A plugin does not need to know how many listeners exist. It emits a typed event, and other plugins subscribe within their own lifecycles.

01 / Declare a typed event

Cordis event sender and independent listeners connected through a typed event name
import type { Context } from '@deepseek-ai/cordis'

declare module '@deepseek-ai/cordis' {
  interface Events {
    'stats/report'(name: string, count: number): void
  }
}

export function report(ctx: Context) {
  ctx.emit('stats/report', 'tool_call', 1)
}

Declaration merging gives ctx.emit and ctx.on the event name and payload types. It emits no runtime wiring; the connection still comes from registering emitters and listeners.

A listener can live in a completely separate plugin:

ctx.on('stats/report', (name, count) => {
  console.log('[stats] ' + name + ' -> ' + count)
})

ctx.on() is an effect owned by the current Fiber, so unloading the plugin removes the listener without a manual removeListener registry.

02 / Five modes are public semantics

Cordis event dispatch modes comparing emit parallel serial bail and waterfall
ModeCallMeaning
emitctx.emitSynchronous dispatch; ignore listener return values
parallelawait ctx.parallelRun all listeners concurrently and await them
serialawait ctx.serialAwait in order; the first result other than null, false, or undefined wins
bailctx.bailSynchronous form of serial
waterfallctx.waterfallListeners wrap downstream through next()

Failure behavior is part of the mode too: emit does not await returned promises, although a synchronous listener throw escapes the call; parallel awaits all listeners and throws an AggregateError if any rejects; serial, bail, and waterfall do not swallow listener failures.

The caller cannot freely substitute a mode. The mode is part of the event’s public agreement and should be explicit in the event declaration or owning subsystem documentation.

03 / How events and services divide work

Services directly request a capability, such as ctx.tools.register() or ctx.sessions.fork(). Events expose facts or requests to independent extension points:

  • Durable facts use session/event.
  • Live Agent coordination uses agent/* events.
  • Tool execution uses tools/* events.

If a fact must survive a reload, put it in the session log. A transient event alone cannot replace durable recording.

04 / The complete path through a stats service

import { Service, type Context } from '@deepseek-ai/cordis'

export class StatsService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'stats')
  }

  private counts = new Map<string, number>()

  bump(name: string) {
    const next = (this.counts.get(name) ?? 0) + 1
    this.counts.set(name, next)
    this.ctx.emit('stats/report', name, next)
  }
}

The provider updates the count and emits. The reporter listens and prints. Their connection is stats/report, not a file path shared between the two plugins.

Where to go next

  • waterfall: Interception and Short-Circuiting
  • Services and Context
  • Cordis Inside DeepSeek Harness

Sources: events tutorial, Cordis Events implementation, and event domains in Harness architecture.

All Posts

Author

avatar for DSH Research
DSH Research

Categories

  • Agent Infrastructure
01 / Declare a typed event02 / Five modes are public semantics03 / How events and services divide work04 / The complete path through a stats serviceWhere to go next

More Posts

Composition and HMR: Import New Code, Dispose the Old Fiber, Then Activate
Agent Infrastructure

Composition and HMR: Import New Code, Dispose the Old Fiber, Then Activate

Understand stable ids, configuration groups, and Cordis HMR’s unload-reload cycle, including how to diagnose plugins stuck in PENDING.

avatar for DSH Research
DSH Research
2026/08/28
Configuration: Give Plugins Validated Options and Defaults
Agent Infrastructure

Configuration: Give Plugins Validated Options and Defaults

Define Cordis plugin configuration with Schemastery and see how defaults, path-aware errors, and FAILED Fibers prevent a sick start.

avatar for DSH Research
DSH Research
2026/08/27
Services and Context: Share Capabilities Without Binding Implementations
Agent Infrastructure

Services and Context: Share Capabilities Without Binding Implementations

Use Service, Context, and declaration merging to understand how providers, consumers, and scopes connect DeepSeek Harness capabilities.

avatar for DSH Research
DSH Research
2026/08/22

Newsletter

Join the community

Subscribe to our newsletter for the latest news and updates

[dshub] [zsh]$ dshub ls$ dshub submit$ dshub blog
-- NORMAL -- ✓ 2026 dshub