Cordis Context
Context is Cordis's root container. It is not a plain object but a Proxy proxied by ReflectService.handler — reading ordinary properties goes through the service resolver, while extend() / isolate() / intercept() create child contexts via the prototype chain without modifying the parent. Context both carries built-in services and, via TypeScript's declare module interface merging, lets third-party plugins declare new ctx.<key>s.
The Public Shape of Context
vendor/cordis/src/context.ts:16-33 defines interface Context:- Four built-in services:
events/logger/reflect/registry - The
rootandbaseUrlpointers - Two symbol-based internal maps:
[symbols.isolate]and[symbols.intercept]
These fields are not ordinary properties; they are forwarded through Context's Proxy to the service store.
Context Is a Proxy
The most direct evidence is in vendor/cordis/src/context.ts:42-84: inside the class Context constructor:
new Proxy<this>(this, ReflectService.handler)The constructor creates the root context and mounts the built-in services, with the root fiber using an empty runtime. This is the root cause of "Context is a proxy" — all property reads and writes are intercepted by ReflectService.
Cross-Copy Identification
vendor/cordis/src/context.ts:61-68 implements identification across realms / across Cordis copies:static is(value) { ... }
static { Context.is[Symbol.toPrimitive] = () => Symbol.for('cordis.is') }It uses a global symbol rather than instanceof to identify Context. This matters: dsh makes heavy use of vm sandboxes (for dynamic plugins), where instanceof on a Proxy fails, so Cordis routes around it with a global symbol.
Layering: extend and the Prototype Chain
extend(meta) at vendor/cordis/src/context.ts:99-107:
extend(meta) {
return Object.create(getTraceable(this, this)) // prototype-chain child context
}extend returns an Object.create(this) object; own keys of meta override inherited values, and the parent context is not modified. This means Context's "layering" relies on the JS prototype chain rather than immutable values — reads walk the prototype, writes only write to self.
Isolation: isolate
isolate(name, label?) at vendor/cordis/src/context.ts:121-125:
isolate(name, label?) {
// copy the isolate map and give name a new symbol scope
}The same service name (e.g. shell) can register different implementations in two isolate('shell') subtrees, without interfering with each other. This is critical for multi-agent / multi-session scenarios — every session can have its own shell instance.
Interception: intercept
intercept(name, config) at vendor/cordis/src/context.ts:139-145:
intercept(name, config) {
// attach the intercept config onto the child context
}When a service starts, [symbols.resolveConfig] merges along the prototype chain. This is Cordis DI's equivalent of "parameter injection" — ctx.intercept('llm', { provider: 'x' }) lets every plugin in the subtree see an llm config that merges in this override, while the root context is unaffected.
Service Store and Mixin
The official docs docs/cordis-api/context.md:236-339 list the "Service store and mixins" APIs: ctx.get / ctx.set / ctx.provide / ctx.accessor / ctx.mixin. Their actual definitions live in reflect.ts but are exposed through the Context proxy:
- Writing
ctx.footriggers theReflectServicehandler, which puts the value into the service store. - Reading
ctx.fooresolves the impl in the current scope from the store. ctx.on→ctx.events.onis a mixin-forwarding accessor, which lets a service instance be replaced whilectx.onalways points at the currently active impl.
Why Symbols Instead of Strings
Context.effect / Context.filter / Context.isolate / Context.intercept on Context are all unique symbol static fields. Plugin authors access metadata via ctx[Context.effect] etc., avoiding collisions with string keys — because ctx is a proxy, any string-keyed property access is intercepted by the service resolver; only symbols can reach the internal maps directly.
What to read next
- Dependency Injection and Services — how Service subclasses register and how Registry records them
- Lifecycle and Middleware — the Fiber finite state machine