Docs navigation

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 showsls, 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.

ParameterRequiredNotes
pathyesDirectory to list.
cursornoResume token from a previous page.
limitno1–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.

ParameterRequiredNotes
pathyesThe 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.

ParameterRequiredNotes
pathyesSearch root to inspect.
field_prefixnoNarrow the catalog to fields under a prefix.
include_facetsnoDefault 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.

ParameterRequiredNotes
pathyesSearch root.
predicatesnoArray of {field, op, value} objects.
sortnoArray of {field, direction} objects.
fieldsnoProjection: which fields to return per match.
facetsnoFacet fields to compute over the match set.
cursornoResume token from a previous page.
limitno1–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.

ParameterRequiredNotes
pathyesSearch root.
measuresyesArray of {name, op, field}; every op except count must name a field.
predicatesnoSame predicate shape as find.
group_bynoFields to group rows by.
sortnoSort the result rows, including by measure name.
limitno1–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.

ParameterRequiredNotes
pathyesFile to read.
formatnoDefault structured; bytes for raw content.
cursornoResume token from a previous page.
offsetnoStarting offset.
limitno1–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.

ParameterRequiredNotes
pathyesFile or subtree to search.
patternyesLiteral substring (case-insensitive).
recursiveyesMust be passed explicitly — there is no default.
cursornoResume token from a previous page.
limitno1–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-*).