Default Artifacts
Murmur publishes the artifacts below. Declare one in artifacts: and install it with
mur install — none of them has to be built locally.
mur search reads the same index from the terminal.
This page also documents the environment the runtime hands hook and driver components.
Inference drivers
An inference driver turns the runtime's provider-agnostic request into one provider's wire format.
Declare one with runtime: driver and name it in
inference.driver.artifact.
| Artifact | Provider |
|---|---|
murmur-driver-anthropic |
Anthropic Messages API |
murmur-driver-openai |
OpenAI Chat Completions, and the Responses API for gpt-5 and later models |
murmur-driver-deepseek |
DeepSeek |
Every driver reads the same inference environment and passes
inference.max_tokens through unchanged as the provider's
output cap.
Hooks
A hook's binding, execution mode and commit policy are fixed by the artifact, not by the entry that
declares it — see Hook artifacts. A hook with no capabilities:
block on its manifest entry gets no directory and no network at all; the ones that write a file
need a filesystem grant.
| Artifact | Fires on | What it does |
|---|---|---|
murmur-hook-compact |
on-compaction |
Summarizes conversation history when the session token threshold is reached, and replaces the context with the summary |
murmur-hook-debug |
Every event | Appends one JSON object per lifecycle event to hook-debug.jsonl |
murmur-hook-diff-summary |
Every event | Snapshots files before each editor tool call and emits a unified-diff summary at the end of the turn |
murmur-hook-eval |
Every event | Scores the session against the configured scorers and writes eval.jsonl |
murmur-hook-grafana |
Every event | Emits OpenTelemetry spans for each lifecycle event to a Grafana Tempo OTLP/HTTP endpoint |
murmur-hook-memory |
on-task-start |
Seeds a task with the relevant, budget-bounded slice of the conversation record read so far |
murmur-hook-regression-verifier |
Every event | Watches a task's test runs and reopens the task when a change broke previously-passing tests |
murmur-hook-shell-desc |
on-stage |
Returns enriched tool manifests for common shell binaries at staging time |
murmur-hook-debug
The quickest way to see what the runtime dispatched during a session. It is the only shipped hook
that runs execution_mode: async, so the agent loop never waits on it.
artifacts:
- name: murmur-hook-debug
version: "1.0.0"
runtime: hook
capabilities:
filesystem:
scope: .
scope: . mounts the accessible workdir as the hook's current directory, which is where
hook-debug.jsonl lands.
murmur-hook-grafana
artifacts:
- name: murmur-hook-grafana
version: "1.0.0"
runtime: hook
observability:
otel_endpoint: "http://localhost:4318"
Without observability.otel_endpoint the hook logs a warning to
workdir/logs/hook-murmur-hook-grafana.log and exports nothing for that session. The session runs
as normal.
murmur-hook-eval
artifacts:
- name: murmur-hook-eval
version: "1.0.0"
runtime: hook
observability:
eval:
dataset_id: my-dataset
scorers:
- type: exit_ok
name: success_check
- type: max_turns
name: turn_limit
max: 5
Without an observability.eval block the hook logs a warning to
workdir/logs/hook-murmur-hook-eval.log and scores nothing. The session runs as normal.
See Scorer types for what each scorer
measures and the shape of the file it writes, observability.eval
for the fields each scorer takes, and mur eval for reading and comparing eval
files.
Tools
| Artifact | What it does |
|---|---|
murmur-tool-code-coverage |
Spectrum-based fault localization (Ochiai / Tarantula) over per-test LCOV reports the agent has already captured. Reads a repository indexed by murmur-tool-code-graph |
murmur-tool-code-graph |
Indexes a Rust or Python repository into a symbol/edge graph and answers structured queries over it. Symbols are addressed by a stable identity rather than by file:line |
murmur-tool-corpus |
Append-only, schema-validated record store for a capsule's own notes, behind the capsule's capabilities.state grant |
murmur-tool-create |
Scaffolds a new tool artifact directory: murmur.yaml, a stub implementation and a README |
murmur-tool-editor |
Reads, writes, patches and searches files without a shell grant |
murmur-tool-git |
Structured git operations returning typed JSON, in place of git in capabilities.shell.allow |
murmur-tool-registry-search |
Searches the artifact index by keyword |
murmur-tool-request-input |
Pauses the agent loop and requests external input over A2A; the answer comes back as the tool result |
murmur-tool-test-report |
Parses a test-runner output file the agent has already captured into a structured list of failures |
murmur-tool-corpus
An append-only JSON-lines store for records a capsule wants to keep across turns —
state/corpus.jsonl, reachable only where the manifest entry grants
capabilities.state; every operation returns
state_unavailable without it.
| Operation | Does |
|---|---|
append |
Writes one record of a declared type. external_id makes a retried call idempotent, returning the first call's id with deduped: true; withdraws: <id> retires an earlier record instead of deleting it |
get |
Reads one record by id, withdrawn or not |
read_recent |
Newest non-withdrawn records of one type |
search |
Term-matched hits, each {id, type, created_at, score, excerpt} — the excerpt is one matching line, not the record body |
verify |
Reports every unreadable line by number, parse error and preview. Needs the state grant but not the configuration |
Record types, their JSON Schema, and the caps on read_recent's n and search's k come from
this tool's own config: block — see Choosing a config block. An
entry with none gets config_missing on every operation but verify. A line that fails to parse
is skipped rather than failing the call: the response carries skipped_lines and
skipped_line_count so the damage reaches the trace on the next call.
murmur-tool-registry-search
Returns ranked matches with name, version, runtime, description and publication date, so a capsule can discover artifacts mid-session. It takes three inputs:
| Input | Required | Notes |
|---|---|---|
query |
yes | Matched case-insensitively against artifact name, description and tags |
registry |
no | Omit for the public index; local for the local artifact store; a URL or absolute path for a custom registry |
limit |
no | Maximum results to return. Default: 10 |
The public index is the same artifacts-index.json that mur search reads.
mur new declares this tool in the generator capsule it cold-boots, and fails
with E-RUN-008 if it is not installed first.
Skills
| Artifact | What it does |
|---|---|
murmur-skill-create-manifest |
How to write a valid murmur.yaml. mur new loads it into its generator capsule |
murmur-skill-investigation-checkpoint |
The checkpoints/decisions.json convention for recording and reusing investigative verdicts across a session |
Hook environment
The runtime injects these into every hook component. They are the whole environment a hook sees — nothing from the host is inherited.
| Env var | Injected when | Value |
|---|---|---|
MURMUR_OTEL_ENDPOINT |
observability.otel_endpoint is set |
The configured OTLP endpoint URL |
MURMUR_EVAL_CONFIG |
observability.eval is set |
The eval block as JSON: dataset_id and the parsed scorers list |
MURMUR_ARTIFACT_CONFIG |
This hook's entry in the operator's manifest declares config: |
That entry's config: block as compact JSON |
MURMUR_DATASET_ID |
mur eval run is driving a dataset and observability.eval.dataset_id is set |
observability.eval.dataset_id |
MURMUR_CASE_ID |
mur eval run is driving a dataset |
The case_id of the case being run |
MURMUR_FORMATION_ID |
MURMUR_FORMATION_ID is set in the host environment |
Forwarded unchanged |
Driver and tool environment
When the manifest configures inference:, the runtime injects these into the driver component and
into every tool artifact.
| Env var | Value | Under transport: process |
|---|---|---|
MURMUR_INFERENCE_TRANSPORT |
inference.transport |
process |
MURMUR_INFERENCE_MODEL |
inference.model |
The configured model, empty when inference.model is omitted |
MURMUR_INFERENCE_ENDPOINT |
http://127.0.0.1:9 followed by the path of inference.endpoint — https://api.moonshot.ai/v1 gives http://127.0.0.1:9/v1. Nothing listens there: the runtime sends the driver's requests to this address on to inference.endpoint with the key attached |
Empty |
MURMUR_INFERENCE_DRIVER |
inference.driver.artifact |
Empty |
MURMUR_INFERENCE_DRIVER_CONFIG |
inference.driver.config as JSON. Not set when the field is absent |
Not set |
MURMUR_CAPSULE_NAME |
name from the manifest |
Same |
MURMUR_CAPSULE_VERSION |
version from the manifest |
Same |
MURMUR_SESSION_ID |
The session ID | Same |
MURMUR_CAPSULE_URL |
localhost:<port>, the address the capsule's HTTP server bound |
Same |
One further variable is injected per artifact rather than per session:
| Env var | Injected when | Value |
|---|---|---|
MURMUR_ARTIFACT_CONFIG |
This artifact's entry in the operator's manifest declares config: |
That entry's config: block as compact JSON |
It reaches the declaring artifact and no other, and the runtime sets it whether or not inference:
is configured. A native tool receives no per-artifact environment, so a config: block there is
reported as W-SEC-015 and delivers nothing.
transport: process loads no driver component: inference.endpoint, inference.driver and
inference.api_key are rejected in the manifest, and the agent loop spawns inference.command
instead. Tool artifacts still receive the whole table.
Driver inference_auth: block
No component receives inference.api_key. A driver's own murmur.yaml declares how its provider
takes the key, and the runtime attaches that header to each request the driver sends to
MURMUR_INFERENCE_ENDPOINT. A transport: http driver without a usable block refuses to start with
E-RUN-025.
inference_auth:
header: Authorization
value: "Bearer {key}"
| Field | Type | Required | Notes |
|---|---|---|---|
inference_auth.header |
string | yes | A valid HTTP header name. Any header of the same name the driver sets, in any case, is replaced |
inference_auth.value |
string | yes | The header value. Must contain {key} exactly once, which is replaced by inference.api_key. With no api_key, no header is sent |