capsule-runtime 0.7.0

The runtime that executes Murmur capsules. Runs packaged AI agents in isolation, enforcing WASI capability grants on a Wasmtime component sandbox.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# WIT package versioning policy

Every `murmur:*` WIT package under `crates/capsule-runtime/wit/**` carries an
explicit `@x.y.z` version on its `package` declaration. The version is embedded
into the component-type section of any `.wasm` compiled against it, so the
interface contract an artifact was built against is readable from the binary
itself rather than inferred by probing which functions happen to be exported.

Current versions:

| Package                  | Version  |
| ------------------------ | -------- |
| `murmur:hook`            | `0.9.0`  |
| `murmur:tool`            | `0.1.0`  |
| `murmur:capsule`         | `0.1.0`  |
| `murmur:tool-registry`   | `0.1.0`  |
| `murmur:artifact-manager`| `0.1.0`  |
| `murmur:shell`           | `0.1.0`  |
| `murmur:message`         | `0.1.0`  |
| `murmur:task`            | `0.1.0`  |
| `murmur:task-io`         | `0.1.0`  |
| `murmur:conversation`    | `0.2.0`  |
| `murmur:stream`          | `0.1.0`  |
| `murmur:host`            | `0.1.0`  |
| `murmur:runtime`         | `0.4.0`  |
| `murmur:runtime-guest`   | `0.1.0`  |
| `murmur:driver`          | `0.3.0`  |

`SERVED_WIT_PACKAGES` in `capsule-runtime` (`src/wit_versions.rs`) must move with this table:
`cargo test -p capsule-runtime wit_versions` fails until it matches the `package` declarations under
`wit/`, and `mur doctor` compares installed artifacts against it.

## WIT levels

A WIT level is one number naming the exact set above. `levels.txt` in this directory lists every
level, one `<level> <package>@<version>` line per package, each level listing every package. The
newest level is the one this `mur` serves: `mur version` prints it, and load, install, search,
`mur index` and `mur doctor` decide fit against it.

The file is append-only. A committed level is never edited, renumbered or reused, so a level
number names the same package set on every `mur` and in every artifact's `murmur-build.json`.

A PR that moves any `murmur:*` package version must, in the same change:

1. Move the `package` declaration in every `.wit` file that declares it.
2. Move `SERVED_WIT_PACKAGES` in `src/wit_versions.rs` and the resolver constants it covers.
3. Append level `N+1` to `levels.txt`, listing every package at its new version.
4. Update the Current versions table above.

What enforces it:

| Check | Fails when |
|---|---|
| `cargo test -p capsule-runtime wit_levels` | `SERVED_WIT_PACKAGES` differs from the newest level, naming each differing package and the level to append |
| `murmur_artifact::parse_wit_levels`, through the same tests | A level is numbered out of order or with a gap, lists a package twice or out of order, or equals the level before it |
| `scripts/check-wit-versions.sh` (per PR) | The tree's declarations differ from the newest level |
| `scripts/wit-versions-changed-since.sh <rev>` (release gate) | A level committed at `<rev>` was edited, renumbered or removed |

The docs page `reference/wit-levels.md` generates its compatibility table from this file and the
`v*` release tags at docs build time.

`murmur:hook` started at `0.2.0` because its 9-function `lifecycle` interface
already reflected one prior additive evolution — the original 7-function baseline
plus `on-task-start`/`on-task-end`. Versioning retroactively named that step
rather than starting the package fresh at `0.1.0`.

`murmur:hook` then went to `0.3.0` when `compaction-event` gained
`model: option<string>` and `system-prompt: option<string>` — a field addition to
an existing record, which is always a major bump (see below). In the same step
`murmur:runtime` went to `0.2.0`: it gained a wholly new interface, `inference`,
whose single `run-inference` function the host provides as an *import* to any
hook that declares it (a hook that does not import it is unaffected), which is a
minor bump.

`murmur:hook` then went to `0.4.0` when `hook-output` gained a fifth case,
`reopen-task(string)` (the `on-task-end` control-return). `hook-output` is the
return type of every one of the nine `lifecycle` functions, so widening the
variant changes the wire shape of every export — a major bump. It was **not**
shippable as a purely additive no-version change: `TypedFunc::typed` is
structural and does not admit variant subtyping across the call boundary, so
lifting a pre-`reopen-task` four-case guest return against the new five-case host
type fails with "type mismatch with results". Hooks built against `@0.3.0` or
earlier stopped resolving at this bump and had to be rebuilt.

`murmur:hook` then went to `0.5.0` when `shell-event` gained `binary: string` —
the canonicalized path of the program a shell tool actually invoked. The record
previously carried only `command` (the argument list, never the binary name), so
no hook bound to `on-shell` could tell whether an event was `pytest`, `cargo` or
`ls`. Adding a field to an existing record is always a major bump, per the rule
below; the `0.3.0 → 0.4.0` entry above records what happens if you try to ship
one additively instead. Hooks built against `@0.4.0` or earlier stopped
resolving at this bump and had to be rebuilt.

`murmur:task-io` was created at `0.1.0` for the `read` interface — `input-len`
/ `read-input` / `output-len` / `read-output`, the host-provided window onto a
task's input and result text. The interface is a hook-side import, so the rule
below classifies it as a minor bump, which would have taken `murmur:runtime` to
`0.3.0`. That was not shippable: the package version is part of *every* instance
name in the package, so `murmur:runtime/inference@0.2.0` would have become
`@0.3.0`, and the host accepts exactly one version with no fallback — every
already-published hook importing `inference` would have stopped instantiating
until rebuilt. Leaving `murmur:runtime` at `0.2.0` while adding an interface to
it would have made this table untrue instead.

`murmur:hook` then went to `0.6.0` carrying three shape changes at once, so
the ecosystem pays for one rebuild window rather than three. `message` gained
`id: option<string>` and `source-id: option<string>`; `hook-output` gained a
sixth case, `seed-context(list<message>)`, **appended** so the existing
discriminants `0`–`4` keep their indices; and `task-start-event` gained
`budget-tokens`, `context-window` and `prior-tokens`, all `u64`. Each of the
three is independently a breaking change under the rule below, and each was
wanted by the same programme of work — bundling them means a hook author
rebuilds once and reads one changelog entry. That is the argument for the
bundle, and it is also the argument for refusing the next field: a bump whose
contents are open is a bump nobody can finish paying for. Hooks built against
`@0.5.0` or earlier stopped resolving at this bump and had to be rebuilt.

`murmur:runtime` went to `0.3.0` in the same step: it gained a wholly new
interface, `tokens`, whose single `count` function the host provides as an
ungated import to any hook that declares it. **This is the standing exception to
the new-package rule below.** That rule exists for one reason — bumping a package
renames every instance name in it, so taking `murmur:runtime` to `0.3.0` renames
`murmur:runtime/inference@0.2.0` to `@0.3.0` and forces a rebuild of every
published hook importing it. Here that rebuild is already forced, by the
`murmur:hook@0.6.0` bump above, and every hook binds the `hook` world as a whole.
The marginal cost of folding `tokens` into `murmur:runtime` is therefore exactly
zero, and the interface is `murmur:runtime/tokens.count` by the name the calling
programme asked for. That is the whole of the difference from the `murmur:task-io`
decision above, where no rebuild was already being paid for: when a new interface
arrives on its own, the rule below still applies and it goes in a new package at
`0.1.0`.

`murmur:conversation` was created at `0.1.0` for the `read` interface — the single
`read-messages` function serving the durable conversation record. It arrived on
its own, with no bump already being paid for elsewhere, so the rule below applies
and the `murmur:runtime@0.3.0` exception recorded above does not: folding it into
`murmur:runtime` would have renamed `murmur:runtime/inference@0.3.0` and
`murmur:runtime/tokens@0.3.0`, forcing a rebuild of every published hook to buy
nothing. `read` *uses* `murmur:hook/lifecycle`'s `message` record, which is
a reference and not a change: `murmur:hook` did not move for it, and the only edit
to it in the same step was the doc comment on `message.id`, a patch-tier change
that moves no version.

`murmur:hook` then went to `0.7.0` carrying the two shape changes a *decision-point*
dispatch of `on-tool-call` and `on-shell` needs, bundled so the ecosystem pays for one
rebuild window rather than two. `hook-output` gained a seventh case, `deny(string)`,
**appended** so the existing discriminants `0`–`5` keep their indices; and `shell-event`
and `tool-event` were each restructured around an `outcome: option<...>` field, whose
`none` is what tells a hook the call it is being shown has not run yet. The same step
moved every post-call field of those two records into the new `shell-outcome` and
`tool-outcome` records, and added `argv`/`script` to `shell-event` and `input` to
`tool-event` — the untruncated identity of what is about to run, which a policy hook must
decide on because `command` is a clipped display string. Each of these is independently a
breaking change under the rule below. Hooks built against `@0.6.0` or earlier stopped
resolving at this bump and had to be rebuilt.

`murmur:hook` then went to `0.8.0` when `shell-event` gained
`recipe: option<string>` — the body of the recipe a build-tool invocation names,
read out of the capsule's workdir by the runtime for `make <target>`,
`just <recipe>` and `npm run <script>`. Without it a policy gating `just build`
was deciding on a name, and a name can be redefined underneath the approval by
editing `justfile`. The field sits between `script` and `outcome`, which keeps
the identity fields together and keeps `outcome` last as the field that tells the
decision-point dispatch from the observation one. Adding a field to an existing
record is always a breaking change under the rule below. Hooks built against
`@0.7.0` or earlier stopped resolving at this bump and had to be rebuilt.

That field is the whole of this bump. Nothing else was bundled with it because
nothing else was pending: the `0.6.0` entry above records why a bundle is worth
paying for when several shape changes are already wanted, and equally why a bump
whose contents are open is a bump nobody can finish paying for. `murmur:runtime`
and `murmur:conversation` did not move. Both merely `use` `murmur:hook`'s
`message` record, which does not change shape here, so the `use` lines were
retargeted at `@0.8.0` and the packages themselves stayed where they were —
the same precedent the `murmur:conversation` entry above records.

`murmur:hook` then went to `0.9.0` when `message` gained
`inserted-by: option<context-insertion>`, with the new
`enum context-insertion { replace-context, seed-context }`. It names the hook
output a message entered the conversation through, which only the runtime knows
for certain, at the moment it commits a `replace-context` or a `seed-context`.
Without it a hook reading the conversation could not tell a compaction summary
from a person's turn: the shipped compaction hook returns its summary as
`role: "user"`, and `source-id` is hook-supplied and carries no provenance. The
field is appended last, after `source-id`. Adding a field to an existing record
is always a breaking change under the rule below.

The field is an enum rather than a boolean, and the boolean is declined. The
runtime commits two hook outputs that put messages into a conversation, and a
seeded message is no more a person's turn than a summary is. A single
`is-summary` flag would either leave seeds unmarked, so a seed still reads as a
person's turn, or mark them too, so a hook cannot tell a memory seed from a
summary. The enum carries both facts at the same cost. It names hook outputs,
not producers: a vocabulary of producers, such as the corpus records and
reloaded history that `source-id`'s doc groups with compaction summaries, is
declined as well. Reloaded history is not an insertion, since a reloaded line
keeps the mark it was written with. A corpus record reaches the conversation
only through a hook's `seed-context`, which is all the runtime can vouch for.
The two cases are closed: a third case is a breaking bump, like a new field.

That field and its enum are the whole of this bump. `canceled`, `truncated` and
`fence` stay marks on the conversation record's lines and are not on the WIT
record. `murmur:conversation` went to `0.2.0` and `murmur:runtime` went to
`0.4.0` in the same step, because the `message` record both `use` changed shape:
`read-messages` returns it and `inference-request` carries it. The `0.8.0` entry
above kept both packages still because `message` did not change there. Here it
does, and every hook rebuild is already forced by `murmur:hook@0.9.0`, so moving
the two siblings costs nothing extra: the same reasoning as the
`murmur:runtime@0.3.0` exception. Hooks built against `@0.8.0` or earlier stopped
resolving at this bump and had to be rebuilt.

The `deny` arm is the one `hook-output` case whose contract is *subtractive*: it can only
stop a call the manifest already permitted, and there is deliberately no arm that permits
one. Widening the variant with a permitting case would be a different kind of change from
every bump recorded above — it would let an artifact grant authority a manifest withheld —
and this entry records that no such case exists so a later bump does not add one by
analogy.

`murmur:driver` was created at `0.1.0` for the `process` interface — `describe`,
`launch`, `parse` and `classify-exit`, the contract a process driver exports so
the runtime can drive one harness CLI without knowing anything about it. Its
instance name is `murmur:driver/process@0.1.0`. It is a new package because
nothing existing moves: no published artifact exports or imports anything it
contains, so no instance name changes and nothing is rebuilt. Its world,
`process-driver`, imports nothing of Murmur's, because a process driver is
granted nothing; it lives in its own bindgen tree, `process-driver/`.

`murmur:driver` then went to `0.2.0` when the `process` interface learned to
carry what a harness spent. A new `usage` record — `input`, `output`,
`cache-read`, `cache-creation` and `thinking`, each `option<u64>` and each the
cumulative total for the harness run — arrived with a new `event` case,
`usage(usage)`, **appended** after `retry` so the existing discriminants `0`–`7`
keep their indices; and `description` gained `reports-usage: bool`, last, which
says whether the driver emits that case at all. Adding a case to a variant and a
field to a record are each independently a breaking change under the rule below.
Its instance name is now `murmur:driver/process@0.2.0`.

The bump was paid for by rebuilding: every driver built against `@0.1.0`,
including this repository's own `tests/fixtures/process-driver` components,
stopped loading and was rebuilt against `@0.2.0`. The host keeps no fallback, so
a `@0.1.0` driver is refused at staging with `E-WIT-LEVEL-001`: it does not fit
WIT level 1, it exports `murmur:driver/process@0.1.0`, and level 1 has
`murmur:driver@0.3.0`, with a hint to rebuild it against level 1's packages.
One `@0.1.0` component stays committed under
`crates/murmur-cli/tests/fixtures/process-driver-v1/`, together with a frozen
copy of the `@0.1.0` `.wit` it is built from, so that refusal stays testable
after this and every later bump. That copy lives in the fixture tree rather than
under `wit/`, because the one-version-per-package rule this file's checker
enforces is a rule about the tree the host is built from.

Nothing else was bundled with it, because nothing else was pending: the `0.6.0`
entry above records both why a bundle is worth paying for when several shape
changes are already wanted, and why a bump whose contents are open is a bump
nobody can finish paying for. `murmur:hook` did not move, and deliberately:
`inference-event` still carries `input-tokens: u64` / `output-tokens: u64` with
no optional form, so a `process` turn whose driver reported nothing is handed
`0`. Making those optional would break every published hook artifact to express
a distinction the trace already carries, which this bump declined to do.

`murmur:driver` then went to `0.3.0` when the `process` interface learned to
report a tool call while the model is still writing it. Two new records —
`tool-call-start` (`id`, `name`) and `tool-call-progress` (`id`,
`input-bytes: u64`, cumulative for the call) — arrived with two new `event`
cases, `tool-call-started(tool-call-start)` and
`tool-call-progress(tool-call-progress)`, **appended** after `note` so the
existing discriminants `0`–`11` keep their indices. Adding a case to a variant
is a breaking change under the rule below. Its instance name is now
`murmur:driver/process@0.3.0`.

The progress case carries a byte count, not a fragment of the input, on
purpose. The runtime turns both cases into stream frames, and
`murmur.WatchSession` may be held by a watch-only credential or be public.
Input streamed while the model writes it would reach that stream before a hook
or the call gate had seen the complete call and had the chance to deny it, and
would put on the ephemeral stream what the durable trace does not hold.
Carrying only a count across the interface means the runtime never holds a
call's input before the gate does.

The cost was paid knowingly. Every installed `@0.2.0` driver is refused at
launch with `E-WIT-LEVEL-001` the moment `mur` upgrades: it does not fit WIT
level 1, it exports `murmur:driver/process@0.2.0`, and level 1 has
`murmur:driver@0.3.0`, with a hint to pin a version that fits and run
`mur install`. That includes one pinned in a
`murmur.lock` that launched the day before, and every third-party process
driver. A pinned lock stops reproducing a launch it reproduced yesterday; that
was spent for a one-time correction while the ecosystem is small enough to
rebuild. Recovery is the refusal's hint: pin a release of the driver built
against `@0.3.0` and run `mur install`, or rebuild the driver. One `@0.2.0`
component stays committed under
`crates/murmur-cli/tests/fixtures/process-driver-v2/`, beside the `@0.1.0` one
and with its own frozen `.wit`, so the refusal of the version every published
driver was built against stays testable.

Nothing else was bundled with it. `description` did not gain a field declaring
that a driver sends the new cases: both are optional for a driver, so nothing
needs to declare them.

`murmur:stream` was created at `0.1.0` for the `events` interface, and
`murmur:text` was **retired** in the same step, with no fallback. `events` is
the one interface a tool or driver imports for everything it tells the host
while a turn is still in flight: `emit-chunk` and `emit-thinking-chunk`, carried
over unchanged from `murmur:text/chunks`, and two new functions,
`tool-call-started(id, name)` and `tool-call-input-bytes(id, bytes)`, through
which an http driver reports a tool call the model is writing. Its instance
name is `murmur:stream/events@0.1.0`. `stream` is a WIT keyword, so the source
spells the package `murmur:%stream`; the `%` is escaping and appears in no
instance name.

It is one package rather than a second one beside `murmur:text` because the
process transport already carries every mid-turn signal on one channel — the
single `variant event` of `murmur:driver/process` — and the http transport now
does the same. Tool-call signals do not belong on an interface named `chunks`
in a package named `text`, and leaving text where it was would have split one
channel across two packages.

It is a new package rather than a bump of `murmur:text` because the name
stopped being true: the interface no longer carries only text. A bump would
have cost the same rebuild — every driver importing `murmur:text/chunks@0.1.0`
stops linking either way — and kept a name that misdescribes what it holds.

The cost was paid knowingly. Every installed http driver importing any
`murmur:text` interface is refused at staging with `E-WIT-LEVEL-001`: it does
not fit WIT level 1, it imports `murmur:text/chunks@0.1.0`, and level 1 has no
version of `murmur:text`, with a hint to pin a version that fits and run
`mur install`. The check covers every
`runtime: driver` artifact a capsule declares, not only its inference driver,
because a `switch_driver` target is dispatched through the same linker. This
repository's own driver fixtures were rebuilt against `murmur:stream`. One
`murmur:text` component stays committed under
`crates/murmur-cli/tests/fixtures/streaming-driver-text/`, so the refusal stays
testable.

**A wholly new interface added to a package that already has published
consumers goes in a new package at `0.1.0`.** No existing instance name changes,
no artifact is rebuilt, and this table simply gains a row. Bump an existing
package only when the change touches something that package already ships —
*unless* the same change set already forces a rebuild of every consumer of that
package, in which case folding the interface in costs nothing extra and the
package is bumped instead. `murmur:runtime@0.3.0` is that case, recorded above.

## When to bump

### Patch (`x.y.Z`)

Documentation or comment-only edits with **no** shape or dispatch change: a
reworded doc comment, a clarified reserved-key note, whitespace. Nothing about
the binary ABI or the set/signature of exported functions changes.

### Minor (`x.Y.0`)

Adding a **wholly new** function, or a wholly new interface, that the host treats
as *optional to export* — generalizing the `OPTIONAL_HOOK_FNS` pattern in
`src/hooks.rs`, where a component compiled before the function existed simply
omits it and is never dispatched for it. The `on-task-start`/`on-task-end`
additions that took `murmur:hook` from its 7-function baseline to `0.2.0` are the
canonical example.

A minor bump is **never** the right level for:

- adding a field to an existing `record`,
- inserting a case into an existing `variant`,
- widening or narrowing an existing type.

The Component Model's binary ABI has no structural tolerance for compound-type
shape changes: every field/case is positional in the canonical ABI, so any such
edit is a breaking change even though it "looks additive" in the source. These
are always **major** bumps.

### Major (the breaking tier)

Every `murmur:*` package is pre-1.0, where semver makes the **minor** field the
breaking axis: a breaking change to `0.4.0` ships as `0.5.0`, not `1.0.0`. This
tier is called *major* throughout this document in the sense of "breaking", and
that is what every bump recorded above has done.

Any of:

- a signature change to an existing function (parameters or return type),
- any shape change to an existing `record` or `variant` (add/remove/reorder a
  field or case, change a member type),
- removing or renaming an existing function or interface.

A host built for the old major version cannot safely call a component built for
the new one, and vice versa.

## No compatibility fallbacks

The host accepts **exactly one version of each interface** — the version
declared in this tree — and keeps no fallback for any earlier one. There is no
compat-shim layer, no version-keyed dispatch, and no transition window.

A bump therefore requires every affected artifact to be rebuilt and
republished. An artifact built against a retired version does not silently
degrade: it fails at instantiation with an error naming the interface the host
expects and pointing the author at a rebuild. That is deliberate — a loud
failure with a known fix is preferable to a compatibility layer that accretes
one shim per bump and is never removed.

Practically, this means the cost of a bump is paid at bump time, by whoever
makes it:

1. Bump the version here.
2. Rebuild this repository's own committed components with
   `scripts/rebuild-components.sh` from the repository root, and commit them.
   `scripts/rebuild-components.sh --check` names every one the bump left
   stale, with the command that rebuilds it; the components and the WIT
   subtree each builds against are listed in
   [components.list]components.list.
3. Rebuild the artifacts in `default-artifacts` (see the sync note in
   [README.md]README.md), and republish.

Do not reach for a fallback to defer that work.

If you are reviewing a change that adds a version fallback of any kind — a
compat struct mirroring an old shape, a `match` on a legacy version, a
try-new-then-old resolution chain — that is the thing this policy exists to
reject.

## Unversioned artifacts: fallback removed

Artifacts published before WIT package versioning exported the **unversioned**
instance names (`murmur:capsule/run`, `murmur:tool/run`,
`murmur:hook/lifecycle`). A transitional dual-accept runtime
resolved those interfaces by trying the versioned instance name first and
falling back to the bare unversioned name, so already-published artifacts kept
running unmodified during the transition window.

That fallback has been **removed**. The host now resolves the three
dynamically-instantiated guest interfaces by the **versioned** instance name
only:

- `src/hooks.rs` — `resolve_lifecycle_iface` resolves the single
  `LIFECYCLE_IFACE` name and nothing else: no earlier version, and not the bare
  unversioned name.
- `src/runtime.rs` — `resolve_versioned_iface` resolves
  `murmur:capsule/run@0.1.0` and `murmur:tool/run@0.1.0`.

The host-provided *import* interfaces (`murmur:tool-registry/invoke@0.1.0`,
`murmur:stream/events@0.1.0`, `murmur:task/task@0.1.0`,
`murmur:runtime/inference@0.4.0`, `murmur:runtime/tokens@0.4.0`,
`murmur:task-io/read@0.1.0`, `murmur:conversation/read@0.2.0`) are likewise
registered under the versioned name only.

An artifact that still exports (or imports) only the unversioned name now
**fails to instantiate** with a hard error naming the versioned interface the
host expected and pointing at rebuilding — `mur install` for a default artifact,
or a source rebuild otherwise. Rebuild and republish any such artifact.