Skip to main content

Module agent_surface

Module agent_surface 

Source
Expand description

GAP-SG-142: agent-native reshaping (--select, --filter, --sort, --dedupe-by, --max-items, --count-only, --truncate-content, --max-output-bytes) applied at the single stdout emission point. GAP-SG-142: agent-native reshaping of the JSON envelope.

Every subcommand used to hand its whole envelope back to the caller, so an agent had to keep a jaq filter in its prompt just to read one field. This module gives the CLI the projection / filter / sort / dedup / limit / truncation surface the sibling tools already expose, applied at a single point: crate::output serializes the response, hands the resulting serde_json::Value to apply, and writes what comes back.

Working on the serialized value rather than on each command’s response struct is what keeps this DRY — one implementation covers the whole CLI and no subcommand needs to know the surface exists.

§Invariants

  • Failures always reach the caller. An envelope carrying error: true or ok: false is emitted verbatim; --filter shapes result rows, never the error contract.
  • JSON Schema documents are never shaped. --print-schema output is recognised by its $schema member and passes through untouched.
  • Truncation is never silent. Anything that removes data records it under the agent_surface member and raises a top-level truncated flag.
  • Derived arrays never survive a reshape. Members that merely restate the reshaped array (memories, entities, direct_matches, graph_matches, related_memories) are dropped and listed under aliases_removed. Without a knob the surface is inert, so the envelope stays byte-for-byte identical to the pre-v1.2.2 output and the v1.0.66 alias contract is untouched.
  • NDJSON streams bypass the surface. Line-oriented emitters keep one record per line; reshaping them would change the stream contract.

§Scope of each knob (GAP-SG-191)

An envelope may carry more than one array, and the three ceilings do NOT all reach the same members. The split is deliberate, and it follows from what each knob removes:

knobreacheswhy
--max-output-bytesevery arrayit removes whole elements to hit a byte budget the caller set for the envelope as a whole
--max-itemsevery arraysame: it removes whole elements, so a secondary member simply gets the same cap
--select, --filter, --sort, --dedupe-byprimary array onlythey act on the fields or the ordering of elements

A secondary array is a different collection, not a restatement of the primary one: graph pairs nodes with edges. Projecting id over edges would rewrite every element to {} and erase source/target — the projection would destroy the collection rather than narrow it. Filtering and sorting fail the same way, on keys that member never had.

Until v1.2.4 --max-items also stopped at the primary array, so graph --select id --max-items 2 answered with two nodes and all 59 066 edges: 4.55 MB for a request that asked for two items. Members shortened by the cap are listed under agent_surface.secondary_capped.

Precedence for every numeric knob is the crate-wide one: CLI flag > XDG config set > named constant. No product environment variable is read.

Modules§

budget
Hard ceiling on the serialized size of one envelope (--max-output-bytes).
filter
Filter expression grammar for --filter.
gate
The refusals. One place decides when a shaping request cannot be honoured.
shape
Stateless reshaping primitives applied to the result array of an envelope.
stream
GAP-SG-215: the NDJSON stream contract, decided.
target
GAP-SG-205: the resolved database target, reported on every envelope that resolved one.
universe
GAP-SG-201: what the QUERY already discarded, declared so the output surface can stop describing a set it never saw.
vocabulary
GAP-SG-202 / GAP-SG-203: resolving the caller’s keys against the envelope BEFORE any predicate runs.

Structs§

AgentSurface
Resolved output-shaping request for the current process.

Functions§

active
true when the installed surface would change an envelope.
apply
Applies surface to value, honouring the invariants documented above.
apply_global
Applies the installed surface to value.
apply_with_premises
The body of apply, with BOTH ambient facts supplied rather than read.
apply_with_target
The body of apply, with the resolved target supplied rather than read.
get
Borrows the installed surface, or an inert one when init never ran.
init
Installs the process-wide surface. Idempotent, first call wins.