A mod is now a plugin built with defineMod() around its register(on,
options); the bridge is the ClaudeCodeMods service (ctx.claudeCodeMods)
that keeps the loaded mods and raises their events. The plugin.json /
hooks.json loader, pluginDirs, and dynamic imports are gone.
prompt.submit context becomes text blocks appended to the human's own
prompt message and $.prompt.submit queues a user-sourced message, so the
bridge adds no message source kind: the persistence record and the
regenerated schema are removed and the artifacts match master again.
Engine correctness from the review round: next() is memoized and every
started run beneath is awaited before a hook's answer settles; a rewritten
tool argument skips the hook with a report pointing at the pre-tool input
rewrite proposal; timers belong to the session whose event started them;
$.tool.call injects a tool's deferred contexts; $.clock.sleep honors the
event signal; a built-in tool answered with { result } is validated against
its output schema before it becomes an error-shaped result; hooks on events
this host never raises are reported at load.
Examples: the three mods from the Claude Code blog post under examples/,
Token Weather and its test unchanged, Blast Radius and Replay Theater
completed from the published fragments, run by their own tests through a
claude-code/testing alias over the new test kit ($.ui.mount/find/press).
- prompt.submit sees and rewrites only the human's own messages, gains the
rewritten text on an image-only prompt, and keeps only typed result fields
- a mod's $.tool.call reaches only the mods loaded before it, attributed to
the caller, instead of re-entering every mod through the pipeline
- a .catch handler shares the failed hook's beneath state so nothing beneath
runs twice; a late next from an abandoned handler is ignored
- hook results are validated before use; detached runs report rejections
- session.end keeps $.state readable until its hooks settle; agent creation
cancellation abandons a waiting session.start hook; agents is read lazily
- a success a hook marks isError becomes a failure; replacement entries clear
on tools/result; per-agent registrations drop with the agent
- $.store writes are serialized per plugin and bounded in UTF-8 bytes;
$.http.fetch bodies are bounded; timers refuse rescheduling after unload
and unload awaits running callbacks; regex matchers reset lastIndex
Add @deepseek-ai/dsh-experimental-claude-code-mods, an alpha bridge that
loads Claude Code mods (plugin directories whose hooks/hooks.json names a
hooks module), runs register(on, options) unchanged, and raises the mod
events from harness extension points: session.start on agent/created,
prompt.submit and turn.start on agent/pre-step, tool.call around
tools/execute, turn.complete on turn/end, session.end on agent/disposed,
and command.run from registered commands. The $ mods API is served over
composed services (commands, tools, user-questions, fs, subprocess,
storage domain, projections); unserved members fail by name.
Includes a middleware chain with Claude Code's failure rules and budget
clock, a claude-code/testing-style test kit, fixtures from the mods
tutorial, full-loop and Loader composition tests, the attribution-only
claude-code-mods message source with its persistence record, and the
regenerated catalogs.
`@deepseek-ai/dsh-experimental-schedule-bundle` shipped in dsh-v0.2.0-rc.1
and rc.2 and was removed when the Web composition started mounting
Schedule itself. A profile that switched it on kept the name in
`dsh.profile.bundles`, so every start reported an unresolvable bundle and
the Plugins page showed a failed entry with no removal action.
`loadProfile` now removes names listed in `RETIRED_BUNDLES` from any
profile's bundle list, alongside the existing installation-owned tuple
normalization, and rewrites the manifest only when the list changes.
Resolutions:
- `docs/subsystems/schedule.*` and `packages/client/ui-schedule/README.*` keep
this branch's shipped-composition text with master's newer wording (Rules and
Records tabs, the icon-button linked-session layout).
- `docs/event-producer-consumer.*` regenerated by `gen-doc-graphs`.
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.
Subpath plugins are not packages, so they never read a package.json and
take their icon from the exported <specifier>/icon resource, like locale.
Package roots keep manifest text fallback and icon, with ./icon as the
fallback image. Drop the bundle-only reader option.
`@deepseek-ai/dsh-schedule` and `@deepseek-ai/dsh-tool-schedule` now depend on
`@deepseek-ai/dsh-subagent`, and the schedule package no longer imports the
Session controller's ownership predicate, so the committed module graph moves.
Review follow-ups on the delegated-child rule:
- Every one of the four tools refuses a delegated caller before dispatch,
reading `delegationDepthOf(exec.agent)` and returning the stable
`subagent_session` refusal, so `schedule_list`, `schedule_delete`, and a
content-only `schedule_update` are covered too. The preset `toolFilter.deny`
stays as the layer that keeps the tools out of a child's prompt.
- The service keys the arming refusal on that same delegation accounting
alone; `hasApiSessionSubagentOwner` returns to package-private and
`@deepseek-ai/dsh-schedule` no longer imports it.
- The preset child row is `time-context` again: the `preset-` prefix separated
it from a Host row that no longer exists, and `verify-cordis-config` passes
with the sibling-style id.
- Docs, Agent Notes, and the generated catalogs follow.