Docs Core
Agent Interface
Reference for the seven-verb agent namespace surface: parameters, hard limits, request/response shapes, evidence URIs, and the canonical workflow.
NoKV exposes one query surface to agents: seven verbs over the same paths the
filesystem shows — ls, stat, catalog, find, aggregate, read, and
grep. The agent discovers what exists, learns which fields are queryable, and
pays to read only what it needs; predicates, sorting, projection, and
aggregation run inside the metadata engine instead of in the context window.
The surface is defined by the AgentNamespace trait (six methods), which has
three implementations:
MetadataClient— over the network, against the metadata service;NoKvFsClient<O>— over the network, with object storage attached;NoKvFs<M, O>— embedded, in-process.
One caveat to know up front: index registration
(register_namespace_index) is currently available only on the embedded
implementation — it is not exposed over the network surface yet.
ls — list direct children
Lists the direct children of a path. Non-recursive.
| Parameter | Required | Notes |
|---|---|---|
path | yes | Directory to list. |
cursor | no | Resume token from a previous page. |
limit | no | 1–100, default 100. |
Request
{"path": "/yanex/runs", "limit": 50}
Response
{"path": "/yanex/runs", "entry_count": 2,
"entries": [{"path": "/yanex/runs/run-1", "name": "run-1", "kind": "directory", "size_bytes": null, "entry_count": 5}],
"next_cursor": null, "truncated": false}
kind is one of file | directory | symlink.
stat — one compact card for a single path
Returns a compact card describing one path: shape, schema, body descriptor, catalog summary, and indexed values.
| Parameter | Required | Notes |
|---|---|---|
path | yes | The path to describe. |
Request
{"path": "/yanex/runs/run-1/metadata.json"}
Response
{"card": {"path": "...", "name": "metadata.json", "kind": "file", "size_bytes": 13,
"record_count": 1, "schema": {"record_type": "json_object", "fields": ["status"]}, "sample": "...",
"body": {"producer": "yanex", "size": 13, "content_type": "application/json"},
"catalog": {"filterable": [...], "sortable": [...], "facetable": [...], "facets": []},
"indexed_values": [{"field": "run.status", "value": "completed"}]}}
Note: body is a descriptor, not the file content.
catalog — discover queryable fields
Reports which fields under a path are filterable, sortable, and facetable —
the discovery step before find or aggregate.
| Parameter | Required | Notes |
|---|---|---|
path | yes | Search root to inspect. |
field_prefix | no | Narrow the catalog to fields under a prefix. |
include_facets | no | Default false; when true, returns value distributions. |
Request
{"path": "/yanex/runs", "include_facets": true}
Response
{"path": "/yanex/runs", "catalog_empty": false,
"catalog": {"filterable": [{"operators": ["eq","ne","in","prefix","suffix","contains","gt","gte","lt","lte","exists","not_exists"], "fields": ["run.status","run.script"]}],
"sortable": ["run.duration_seconds"], "facetable": ["run.status"],
"facets": [{"field": "run.status", "values": [{"value": "completed", "count": 12}], "distinct_count": 1, "truncated": false}]},
"child_catalogs": []}
When the catalog at a path is empty, the verb automatically scans the first 20
child directories and returns up to 5 non-empty child_catalogs as candidate
search roots.
find — predicate search with projection and sort
Predicate search plus projection, sorting, and facets — all pushed down into the engine.
| Parameter | Required | Notes |
|---|---|---|
path | yes | Search root. |
predicates | no | Array of {field, op, value} objects. |
sort | no | Array of {field, direction} objects. |
fields | no | Projection: which fields to return per match. |
facets | no | Facet fields to compute over the match set. |
cursor | no | Resume token from a previous page. |
limit | no | 1–10, default 10. |
find explicitly rejects an include parameter.
Request
{"path": "/yanex/runs",
"predicates": [{"field": "run.status", "op": "eq", "value": "completed"}],
"sort": [{"field": "run.duration_seconds", "direction": "desc"}],
"fields": ["run.script", "run.duration_seconds"], "limit": 5}
Response
{"path": "/yanex/runs", "match_count": 1,
"matches": [{"path": "/yanex/runs/run-1", "values": {"run.script": "train.py", "run.duration_seconds": 42}}],
"facets": [], "next_cursor": null, "truncated": false}
Projectable built-in fields: path, name, kind, size_bytes,
body.content_type, body.producer, plus any indexed field.
aggregate — summary rows
Summary rows over a match set: count, sum, avg, min, max, with
optional grouping.
| Parameter | Required | Notes |
|---|---|---|
path | yes | Search root. |
measures | yes | Array of {name, op, field}; every op except count must name a field. |
predicates | no | Same predicate shape as find. |
group_by | no | Fields to group rows by. |
sort | no | Sort the result rows, including by measure name. |
limit | no | 1–100, default 20. |
Request
{"path": "/yanex/runs",
"predicates": [{"field": "run.script", "op": "eq", "value": "train.py"}],
"group_by": ["run.status"],
"measures": [{"name": "runs", "op": "count"}, {"name": "avg_dur", "op": "avg", "field": "run.duration_seconds"}],
"sort": [{"field": "runs", "direction": "desc"}]}
Response
{"path": "/yanex/runs", "input_match_count": 12, "row_count": 12, "group_count": 2,
"groups": [{"key": {"run.status": "completed"}, "values": {"runs": 10, "avg_dur": 41.5}}], "truncated": false}
read — read a file body
Reads a file body, structured (records) or raw bytes.
| Parameter | Required | Notes |
|---|---|---|
path | yes | File to read. |
format | no | Default structured; bytes for raw content. |
cursor | no | Resume token from a previous page. |
offset | no | Starting offset. |
limit | no | 1–100, default 100. |
Request
{"path": "/yanex/runs/run-1/metadata.json", "format": "structured"}
Response
{"path": "...", "total_size_bytes": 13, "format": "structured", "record_type": "json_object",
"record_count": 1, "items": [{"index": 0, "value": {"status": "completed"}}], "bytes": null,
"cursor": null, "next_cursor": null, "truncated": false}
record_type is one of
directory_entries | json_array | json_object | yaml_mapping | text_lines.
Guard: a structured read of a file with more than 100 records fails
immediately, and the error message tells the agent to use stat or find
instead — the error itself is the prompt that steers the agent back to the
cheap path.
grep — literal substring search with line numbers
Case-insensitive literal substring search over file bodies, returning line-numbered matches.
| Parameter | Required | Notes |
|---|---|---|
path | yes | File or subtree to search. |
pattern | yes | Literal substring (case-insensitive). |
recursive | yes | Must be passed explicitly — there is no default. |
cursor | no | Resume token from a previous page. |
limit | no | 1–100, default 100. |
Request
{"path": "/yanex/runs/run-1/artifacts", "pattern": "best_model", "recursive": true}
Response
{"tool": "grep", "path": "...", "pattern": "best_model", "recursive": true,
"evidence": "nokv-native:///yanex/runs/run-1/artifacts", "snapshot_id": 1,
"matches": [{"path": ".../artifacts/stdout.txt", "line_number": 3,
"snippet": "Checkpoint: best_model_1.0_1.pt",
"evidence": "nokv-native:///yanex/runs/run-1/artifacts/stdout.txt@generation:1#L3"}],
"files_scanned": 1, "bytes_read": 31, "next_cursor": null, "truncated": false}
Of the seven verbs, only grep exposes evidence URIs in its JSON responses —
the other serializers omit them on purpose.
Common facts
Predicate operators. eq, ne, in, prefix, suffix, contains,
gt, gte, lt, lte, exists, not_exists. The in operator requires an
array value; exists and not_exists ignore value.
Hard caps. find returns at most 10 matches per page; ls, read, and
grep at most 100 per page; aggregate at most 100 groups. An out-of-range
limit fails with an error shaped {name} must be between 1 and {max}.
Evidence URIs. The format is
nokv-native://{path}@generation:{N}#L{n} for grep line matches, or
#item:{i} for structured record indexes. The @generation component pins
the evidence to a specific
version of the file, so a citation stays valid even after the file is replaced.
Only grep exposes evidence URIs in responses.
Canonical workflow. The interface card handed to agents states it directly:
“Plan for few turns; a typical question resolves with one catalog call, one find or aggregate call, and scoped greps only when log body text is needed.”
Trying the verbs today
The seven verbs ship in the Rust SDK; there is no CLI or MCP entry point for them yet. The best way to see them at work is the open agent-interface benchmark, where this surface answers real questions over an 875-run experiment corpus side by side with raw SQL.
Source of truth: the verb definitions live in
crates/nokv-client/src/agent.rs on main (the crates were renamed from
nokvfs-* to nokv-*).