fix(invariants): address Codex review of dev invariants (PR 2)

- HMR state soundness: inject sessions, rebuild per-session trace by replaying
  each existing session's log at (re-)apply, so a reload mid-turn no longer
  falsely rejects the next event
- tighten nesting: turn/end rejects an open step; step/start rejects an open
  step; chunk/message/tool events must name the open turn+step; pendingCalls
  clears at step/end so a cross-step tool/result can't satisfy a stale call
- drop the default export (it stripped the inject metadata when loaded by
  name; functional plugins expose named exports only — matches tool-bash)
- document deepFreeze's top-down precondition; sync RFC 005/008 bodies to the
  as-implemented decision
This commit is contained in:
Tianyi Cui
2026-06-13 23:50:43 +08:00
parent 11a29fdefe
commit 89e63f1436
5 changed files with 167 additions and 41 deletions

View File

@@ -6,14 +6,16 @@ Dev-mode event-contract invariants and session-log freeze. A pure-listener plugi
## Plugin
```ts
import Invariants from '@deepseek-ai/dsh-invariants'
A functional plugin — register the module namespace (this is what loading by name in `cordis.yml` does):
await ctx.plugin(Invariants) // freeze on (default)
```ts
import * as Invariants from '@deepseek-ai/dsh-invariants'
await ctx.plugin(Invariants) // freeze on (default)
await ctx.plugin(Invariants, { freeze: false }) // assert contract, don't freeze
```
`inject`: none required — it listens on `session/created`, `session/event`, and `agent/status`, all emitted by services it does not depend on directly.
`inject`: `['sessions']` — it reads `ctx.sessions.list()` at apply time to rebuild trace state for sessions that already exist (so a hot reload mid-turn doesn't falsely reject the next event). It listens on `session/created`, `session/event`, and `agent/status`.
### Config