Agent API v1¶
This is the wire reference for trust-dev agent serve.
Use Agent API overview first when you are choosing capabilities rather than implementing the protocol.
Protocol¶
trust-dev agent serve is the first stable automation surface outside VS Code.
It exists to let agents, shell automation, and future hosted workers drive truST without depending on editor internals.
Transport and Security¶
- Transport:
JSON-RPC 2.0overstdio - Framing: one JSON request per input line, one JSON response per output line
- Network listeners: not supported in v1
- Security rule for any future non-stdio mode:
- default to loopback only
- refuse non-loopback unless explicit auth is configured
- unauthenticated remote execution is unsupported
Request Envelope¶
{"jsonrpc":"2.0","id":1,"method":"agent.describe","params":{}}
Each non-empty input line is one complete request. The top-level value MUST be
an object with jsonrpc exactly equal to "2.0", a non-empty string method,
and an explicit id whose value is a string or a JSON number. Notifications
(requests with no id) and null, Boolean, object, or array identifiers are
not supported by v1 and return -32600 with a null response ID. params,
when present, MUST be an object or array. A scalar params value is an invalid
request envelope rather than method-specific input.
Invalid JSON returns one parse-error response with code -32700 and a null
ID. A syntactically valid but invalid envelope returns -32600; it MUST NOT be
misreported as a parse error. Blank lines are ignored. Unknown top-level
members are ignored for JSON-RPC interoperability.
Response Envelope¶
Success:
{"jsonrpc":"2.0","id":1,"result":{"transport":"stdio"}}
Failure:
{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method 'missing' is not available."}}
Every accepted request line produces exactly one response line with the same
ID. A response contains exactly one of result or error, never both and
never neither. Error responses preserve the original string or numeric ID
whenever the envelope supplied a valid one.
Stable Error Codes¶
| Code | Meaning |
|---|---|
-32700 |
Parse error |
-32600 |
Invalid JSON-RPC envelope |
-32601 |
Unknown method |
-32602 |
Invalid params |
-32001 |
Workspace path escapes the workspace root |
-32002 |
I/O or runtime command failure |
-32003 |
Harness not loaded |
-32004 |
harness.run_until exceeded the requested cycle budget |
Error data, when present, is machine-readable. Workspace containment errors
include kind = "path_outside_workspace" and the submitted path. I/O errors
include the normalized in-workspace path when one exists. Harness boundary
errors preserve their stable boundary kind, path, and candidate list. Secrets,
including runtime control tokens and source contents unrelated to the failed
operation, MUST NOT be copied into an error message or error data.
Current Methods¶
| Method | Purpose |
|---|---|
agent.describe |
Return transport info, workspace root, and supported method list |
workspace.read |
Read a file inside the workspace root |
workspace.write |
Write a file inside the workspace root |
workspace.project_info |
Return project-root, source-root, dependency, config, and runtime-orientation metadata |
lsp.diagnostics |
Return machine-readable diagnostics for one ST file or a whole project source tree |
lsp.ast_canonicalize |
Return the canonical normalized AST token stream and 5-gram fingerprints for one source |
lsp.ast_similarity |
Compare two canonical AST token streams and return their similarity evidence |
lsp.format |
Return a formatting preview for one ST file without mutating disk |
runtime.build |
Reuse the trust-runtime build --ci payload path |
runtime.compile_reload |
Run the full diagnose -> build -> reload loop and return a single structured result |
runtime.validate |
Reuse the trust-runtime validate --ci payload path |
runtime.test |
Reuse the trust-dev test --output json payload path |
runtime.reload |
Rebuild the program image program.tpi and send it with bytecode.reload to a running runtime control endpoint |
harness.load |
Load inline/project sources into a deterministic in-process harness |
harness.reload |
Reload harness sources while preserving retain semantics when supported |
harness.execute |
Run one isolated deterministic fixture transaction and return structured step/assertion evidence |
harness.cycle |
Advance cycles, optionally advancing virtual time first |
harness.set_input |
Set a harness input/global/program variable |
harness.get_output |
Read a harness output/global/program variable |
harness.advance_time |
Advance virtual time without executing a cycle |
harness.run_until |
Cycle until a named output matches an expected value |
agent.describe.methods is the authoritative, duplicate-free list of methods
accepted by this version. Every listed method MUST have a dispatch route, and
no accepted public method may be omitted from the list. The response also
reports the canonical workspace root, stdio transport, and jsonl framing.
Method Notes¶
workspace.read¶
Params:
{"path":"src/main.st"}
workspace.write¶
Params:
{"path":"src/main.st","text":"PROGRAM Main\nEND_PROGRAM\n","create_parents":true}
bytes_written is the UTF-8 byte length, not the Unicode scalar or display
character count. When create_parents is false, a missing parent is an I/O
error and no file or directory is created.
Workspace filesystem containment¶
All workspace, project, source-root, and explicit harness-file paths pass two independent gates before any read, write, source collection, build, validation, formatting, reload, or harness load:
- lexical normalization rejects absolute paths, platform drive/UNC roots, empty paths, and any parent traversal that escapes the submitted relative path; and
- filesystem resolution confirms that the target, or the nearest existing ancestor for a target that does not yet exist, remains below the canonical workspace root.
Symbolic links, junctions, reparse points, or other aliasing components MUST
NOT permit access outside the canonical workspace root. For writes with parent
creation enabled, containment is checked before creating any directory. A
containment rejection uses -32001 and performs no filesystem mutation.
Project paths MUST resolve to an existing directory inside the workspace. Project-relative source roots and file paths are resolved relative to that canonical project directory and must remain inside both the project and the workspace. A symlinked project or source path that escapes either boundary is rejected rather than treated as a valid project.
Successful path-bearing results use normalized forward-slash relative paths for machine portability. An I/O failure does not silently return an empty file, an empty project, or a successful zero-byte write.
workspace.project_info¶
Optional params:
{"project":"runtime-a","sources_root":"src"}
Returns a stable orientation payload for agents:
- canonical
project - resolved
sourcesRoot sourceCountsourcesdependencyRootsresolvedDependencies- file presence for
runtime.toml,io.toml,simulation.toml,trust-lsp.toml, and the program imageprogram.tpi lsp.vendorProfilewhentrust-lsp.tomldeclares one- runtime summary (
controlEndpoint, web/discovery/mesh settings, etc.) whenruntime.tomlparses cleanly - IO summary (
drivers,driverCount,safeStateCount) whenio.tomlparses cleanly
If runtime or IO config files exist but fail to parse, the payload keeps the
file path and reports parseError instead of failing the whole request.
runtime.build¶
Optional params:
{"project":"runtime-a","sources_root":"src","issues_limit":500}
issues_limit is optional; a value below 1 is an invalid-params error. When
it is present and the payload carries more issues than the limit, issues[]
is the first issues_limit entries of the same deterministic order and the
response also carries issuesTruncated: true and totalIssues. When it is
omitted, or when the limit is not exceeded, the response is unchanged and
carries neither field. An applied limit also bounds a failed build's
projected error text: the compiler dump keeps its first issues_limit lines
plus one closing line naming the omitted remainder and the dump's total.
Returns the same machine-readable payload shape as trust-runtime build --ci.
lsp.diagnostics¶
Optional params:
{"project":"runtime-a","sources_root":"src","path":"src/main.st","content":"PROGRAM Main\nEND_PROGRAM\n","page":1,"page_size":50}
Rules:
projectis relative to the agent workspace root when presentsources_rootis relative to the selected project root when present, and narrows the listed issues of a whole-project request to the files under it. A folder that does not exist ("sources directory not found") or that holds none of the project's source files is an I/O error (-32002), never an empty listpathis relative to the selected project root when present, and names one of the project's source files; then only that file's issues are listed, andsources_rootdoes not apply. A file outside the project's include paths is an I/O error (-32002) that says so- a
sources_rootorpaththat resolves outside the project or the workspace, through a symbolic link for example, is refused with-32001before anything is analyzed contentis an in-memory override forpath; it is invalid withoutpathpageandpage_sizeare optional paging parameters. They must be given together; either one alone, apagebelow 1, or apage_sizebelow 1 is an invalid-params error (-32602). When they are present,issues[]is the requested window of the same deterministic sort, and the response also carriespage,pageSize,totalIssues, andtotalPages. There is always at least one page, and apagebeyondtotalPagesreturns an emptyissues[]with the honest totals. When both are omitted, the response is the full unpaged list and carries none of the paging fields.
The diagnostics are the language server's: the same findings that trust-lsp check reports for
the project. The project is loaded as the language server loads a workspace folder, with its
trust-lsp.toml: include paths, library paths and libraries, vendor profile, [diagnostics]
toggles and severity overrides. If path is omitted, every source file of the project and its
trust-lsp.toml are diagnosed and listed in one aggregated list; otherwise only path is.
The analysis changes nothing on disk and fetches nothing: it neither reads nor writes the
language server's index cache, and it never clones or fetches a dependency. A git dependency is
used only when its checkout already exists, for example after the editor or trust-lsp check
fetched it; otherwise its trust-lsp.toml finding (L007) says that it is not available.
Only files inside the agent workspace are listed. A library that the configuration names
outside the workspace is analyzed, so that its symbols resolve, but its findings are not listed.
An issue's path is relative to the project root, or the file's full path for a listed file
outside it, such as a library elsewhere in the workspace.
Returns:
targeterrorsandwarnings, counted over every listed issue, also on a page- when
pageandpage_sizeare present:page,pageSize,totalIssues, andtotalPages issues[](the page's, when a page was asked for) withpath, absolutefile,line,column,severity,message, optionalcode, and optional related entries
Every issue uses one-based line and column coordinates, the columns in UTF-16 code units as the
editor counts them, while span.start and span.end remain zero-based UTF-8 byte offsets.
Related entries use the same coordinate convention and name places in the issue's file. errors
and warnings are the exact counts of listed issues whose severity is error and warning;
information and hint issues remain in issues but do not inflate either counter. Issues are
sorted deterministically by file, position, severity, code, and message before a page is cut.
An inline content override is analyzed without writing it to disk.
lsp.ast_canonicalize¶
Optional params:
{"project":"runtime-a","path":"src/main.st","content":"PROGRAM Main\nEND_PROGRAM\n"}
Exactly one effective source is required. Inline content takes precedence
over disk, including when the named file does not exist; otherwise path is
read relative to the selected project. Supplying neither is an error. The
operation never writes the source.
The result contains algorithm, gramSize, parseErrorCount, stream, and
fiveGrams. Version 1 uses
canonical_ast_jaccard_5gram_v1 with a gram size of five. Trivia, identifier
spellings, and literal values do not enter the canonical token stream.
fiveGrams is the sorted, duplicate-free set of canonical five-token windows,
and parse errors remain visible through parseErrorCount rather than silently
changing the algorithm.
lsp.ast_similarity¶
Optional params:
{
"project":"runtime-a",
"left_path":"src/before.st",
"right_content":"PROGRAM After\nEND_PROGRAM\n"
}
The left and right inputs independently follow the canonicalization source
selection rule: inline content wins, otherwise that side requires a
project-relative path. The result reports the same algorithm and gram size,
the Jaccard score, unique sharedGrams, leftGrams, and rightGrams, plus
strict threshold070 and threshold095 decisions. A score exactly equal to a
threshold does not clear it. Two inputs with no five-grams have score 1.0;
the operation does not mutate either file.
lsp.format¶
Params:
{"project":"runtime-a","path":"src/main.st","content":"PROGRAM Main\nEND_PROGRAM\n"}
Rules:
pathis required and is relative to the selected project rootcontentis optional; when omitted, the formatter reads the file from disk
Returns a formatting preview only:
path- absolute
file - formatted
content changed
Inline content takes precedence over the file on disk and is never persisted.
When content is omitted, the selected file must exist and be valid UTF-8.
changed is true exactly when returned content differs byte-for-byte from the
effective input. The returned path is normalized and project-relative;
file identifies its absolute project location.
runtime.validate¶
Optional params:
{"project":"runtime-a","issues_limit":500}
issues_limit follows the runtime.build rule: optional, values below 1 are
invalid params, and an applied limit adds issuesTruncated: true and
totalIssues to the response.
Returns the same machine-readable payload shape as trust-runtime validate --ci.
runtime.test¶
Optional params:
{"project":"runtime-a","filter":"CI_","list":false,"timeout_seconds":5}
Returns the same JSON summary shape as trust-dev test --output json.
runtime.compile_reload¶
Optional params:
{"project":"runtime-a","sources_root":"src","endpoint":"tcp://127.0.0.1:9001","token":"secret","issues_limit":500}
issues_limit follows the runtime.build rule: optional, values below 1 are
invalid params, and an applied limit truncates issues[] to the first
issues_limit entries of the same deterministic order, bounds the
build-failure runtimeMessage and the method's error text the same way, and
adds issuesTruncated: true and totalIssues to the response.
Behavior:
- collects the project's diagnostics as
lsp.diagnosticsdoes, the language server's, with the samesources_rootrule - blocks build/reload when the listed diagnostics contain errors
- reuses the normal
trust-runtime buildpath when diagnostics are clean - reuses the same
bytecode.reloadcontrol-endpoint path asruntime.reload
Returns a single machine-readable loop result with:
targetdirty(currently alwaysfalsefor agent-driven file writes)errorswarningsissuesruntimeStatusruntimeMessage- optional
build - optional
reload - when
issues_limittruncated the list:issuesTruncated(true) andtotalIssues
This is the preferred agent method for iterative write -> diagnose -> build -> reload
loops because it keeps diagnostics and reload state in one payload.
runtime.reload¶
Optional params:
{"project":"runtime-a","sources_root":"src","endpoint":"tcp://127.0.0.1:9001","token":"secret"}
Behavior:
- rebuilds the program image
program.tpifor the selected project - reads the rebuilt image from disk
- sends it with
bytecode.reloadto the runtime control endpoint from the project bundle or the explicitendpointoverride
Returns a combined payload with the build report plus the control reload result.
Current scope:
- intended for same-project runtime iteration and agent repair loops
- source-aware attached-session reload workflows are still future work
harness.load¶
Either load inline sources:
{
"inline_sources": [
{"text":"PROGRAM Main\nEND_PROGRAM\n"}
]
}
Or load a project from the current workspace:
{"project":"runtime-a"}
Or load explicit files relative to the workspace root:
{"files":["src/main.st","src/tests.st"]}
Source selection is unambiguous and fail-closed:
inline_sources,files, andprojectare mutually exclusive selectors;- an explicit inline or file list must be non-empty;
- every inline source must contain non-empty source text;
- explicit file paths must be unique after normalization and must pass the workspace containment rules; and
- when no selector is supplied, the configured agent workspace is the project.
harness.load replaces the stateful harness session. harness.reload requires
an already loaded stateful session and uses the selected replacement sources.
Load or reload failure leaves no partially replaced session.
harness.execute¶
harness.execute is an isolated fixture transaction. It accepts the same
source selector as harness.load, plus ordered steps, ordered assertions,
and a final watch list:
{
"inline_sources": [
{"path":"main.st","text":"PROGRAM Main\nEND_PROGRAM\n"}
],
"steps": [
{"op":"set_input","name":"start","value":{"type":"BOOL","value":true}},
{"op":"cycle","count":1,"dt_ms":10}
],
"assertions": [
{"kind":"output_equals","name":"running","equals":{"type":"BOOL","value":true}}
],
"watch":["running"]
}
Every execute request creates a fresh harness. It neither reads nor mutates the
stateful session owned by harness.load, harness.reload, and the individual
harness methods. The harness performs its documented initial cycle during
load; stepsRun counts only explicitly requested steps.
Supported steps are set_input, set_access, bind_direct,
set_direct_input, advance_time, cycle, run_until, and restart.
Steps execute in request order. cycle.count defaults to one. Restart mode
defaults to cold; accepted spellings are case-insensitive cold and warm.
An unknown operation, malformed typed value, negative duration, zero or
otherwise invalid execution bound, or unsupported restart mode is invalid
params (-32602) before any later step executes.
Supported assertions are output_equals, access_equals, and
direct_output_equals. Assertions execute in request order after all steps.
A value mismatch is an assertion_failed result entry with typed expected
and actual values. A boundary-resolution error is a structured failure entry
with the stable boundary kind, path, and candidates; it is not converted into
a successful assertion and does not erase earlier assertion results.
The method returns a JSON-RPC success envelope whenever it produced a complete
fixture result, even when that result has status = "fail". The result
contains:
sourceCount,stepsRun,status, andpassed;- assertion
total,evaluated,passed, andfailed, satisfyingevaluated = passed + failedand never exceedingtotal; - a final typed
watchSnapshot; and - ordered failure entries identifying compile, runtime-cycle, step, run-until-timeout, boundary, or assertion failures.
A compile failure occurs before any step and reports stepsRun = 0. A step
failure stops later steps and assertions, retains the zero-based stepIndex
and failing step, and returns the best available watch snapshot. A run-until
timeout preserves the typed expected value and the last readable actual value.
Assertion mismatches do not prevent later independent assertions from being
evaluated. passed is true exactly when the failure list is empty; successful
results omit failures.
Malformed request structure and invalid argument values use JSON-RPC
-32602. Compile/runtime/boundary outcomes reached by a valid fixture request
are represented inside the result so batch callers can distinguish a failed
fixture from a broken protocol call.
harness.cycle¶
{"count":10,"dt_ms":10,"watch":["q","et"]}
dt_ms advances virtual time before each cycle. watch returns typed values
for the named outputs after the final cycle.
harness.set_input¶
{"name":"start_pb","value":{"type":"BOOL","value":true}}
harness.get_output¶
{"name":"motor_run"}
harness.advance_time¶
{"duration_ms":25}
harness.run_until¶
{
"name":"q",
"equals":{"type":"BOOL","value":true},
"dt_ms":10,
"max_cycles":5,
"watch":["q","et"]
}
If the cycle budget is exhausted, the response uses error code -32004 and
returns the expected value in the error data.
Typed Values¶
Harness values use typed JSON payloads so agents do not need to guess IEC data shapes. Examples:
{"type":"BOOL","value":true}
{"type":"INT","value":7}
{"type":"TIME","nanos":30000000}
{"type":"STRING","value":"hello"}
{"type":"ARRAY","dimensions":[[0,1]],"elements":[{"type":"BOOL","value":true},{"type":"BOOL","value":false}]}
trust-harness uses the same value encoding. See
docs/guides/TRUST_HARNESS_PROTOCOL.md.
Current Scope Boundary¶
The following are intentionally not in v1 yet:
lsp.code_actions- attached-session source-aware
runtime.reload
Those stay pending until the code-action surface is extracted into a shared service instead of living only inside the LSP handler stack, and until attached-session reload semantics are lifted out of the VS Code-specific runtime panel flow.