Skip to content
源码版本47f9438 (dsh@0.1)

Dependency Resolution and Ordering

Cordis's "load order" is not a topological sort but a service-availability-driven reactive system — a fiber only transitions LOADING → ACTIVE when all services declared in inject are active; otherwise it stays PENDING forever (no error, because a provider may arrive later).

Not a Topological Sort

This is the key mental shift for understanding Cordis plugin loading: there is no explicit "load A before B" ordering. A plugin declaring inject: ['tools', 'systemPrompt'] stays PENDING until both services are active; if tools itself declares inject: ['systemPrompt'], Cordis resolves transitively.

_checkImpl: Whether a Service Is Injectable

_checkImpl(name) at vendor/cordis/src/fiber.ts:597-609:

ts
_checkImpl(name) {
  // look up ctx.reflect._getImpl(name, true)
  // if the impl exists and check() passes, write into _store
  // otherwise delete
}

This is the "is the service injectable" check.

_refresh: The Epoch String

_refresh() at vendor/cordis/src/fiber.ts:611-623:

ts
_refresh() {
  // iterate Object.keys(this.inject)
  // if any _store[name] is missing, set epoch to INACTIVE (PENDING)
  // otherwise concatenate ':uid:uid' into the new epoch string
}

_setEpoch(epoch) at vendor/cordis/src/fiber.ts:625-639: an epoch change triggers _updateState —:

  • INACTIVE → non-INACTIVE triggers _reload() (LOADING)
  • The reverse triggers _unload() (UNLOADING)

Why uid Concatenation Instead of a Boolean

_setEpoch concatenates uids instead of using a boolean: when a provider is reloaded its uid changes, which changes the epoch string of every downstream fiber and cascades a reload — this is the root cause of HMR needing no manual bookkeeping.

Two Ways to Write inject

Resolved Inside plugin()

plugin(plugin, config) at vendor/cordis/src/registry.ts:316-336:

ts
plugin(plugin, config) {
  // new runtime → new Fiber(ctx, config, Inject.resolve(plugin.inject), runtime, …)
  // inject is resolved at fiber construction time into ctx.intercept overrides
}

inject() Sugar

inject(inject, callback) at vendor/cordis/src/registry.ts:300-302 is sugar for plugin({ inject, apply: callback, name: callback.name }): the callback executes when the service becomes available, and is unloaded and re-run when the service disappears.

Dynamic Plugin Startup: guardedPlugin

startHostHalf

startHostHalf(group, plugin, reportGuardFailure) at packages/extensions/cordis-host-runner/src/lifecycle.ts:22-45:

ts
async function startHostHalf(group, plugin, reportGuardFailure) {
  await group.await()                              // wait for the group to be ready
  const fiber = group.ctx.plugin(guardedPlugin(plugin))  // mount after wrapping
  await fiber.await()
  // on failure fiber.dispose() — no FAILED fiber residue left
}

group.await() ensures the cordis-dynamic group fiber that the dynamic plugin mounts onto is ready before spawning a child fiber, avoiding orphan fibers mounted while the group is not yet ACTIVE.

The guardedPlugin Sandbox

The guardedPlugin wrapper: the apply of a dynamic plugin receives sandboxContext(ctx, reportFailure) rather than the real ctx, exposing only a whitelist of verbs (ctx.get / ctx.on / ctx.provide / ctx.effect); framework internals and context-valued returns are rejected.

The Sandbox is not a security boundary: the host-sandbox.ts comment explicitly states "is not containment: host-realm helper functions remain an escape route". Dynamic plugins are for cooperative packages (observable and disposable), not for defending against malicious code.

Diagnostics: Why My Plugin Won't Load

missingServices(ctx, fiber) at packages/extensions/cordis-host-runner/src/lifecycle.ts:55-57:

ts
function missingServices(ctx, fiber) {
  // use ctx.get(service) === undefined to list services the fiber is still waiting on
}

This is the diagnostic answer to "why won't my plugin load". The "settled-but-pending" report of the cordis_inspect_self tool comes from this function, telling the agent: "your plugin is waiting on the fs service".

Reactive Model Summary

There is no "scheduler" ordering anything; each fiber independently observes the services it cares about and reacts automatically when service availability changes.

Unofficial community learning site. Content based on the MIT-licensed deepseek-ai/deepseek-harness source. · Privacy · Terms · About