Bring your own engine
Everything so far used the engine that ships with the agent. This page is the other path: your own code behind the same identity and policy boundary.
There is no SDK and no library to link against. An engine is a process the agent runs, which is why it can be written in anything.
Your code stays yours, in your environment. Nothing is packaged into the agent, and nothing of ours is imported into your project — you keep your own dependencies, your own versions and your own release cycle. The agent starts a process and reads its output; that is the entire coupling.
The contract
Section titled “The contract”The agent starts your process with an argument list it builds, and reads the reply out of the JSON your process writes to stdout. You declare both halves when you seal it:
| field | what it is |
|---|---|
file | your executable |
argsNew | the argv for a new conversation |
argsResume | the argv for continuing one |
replyField | which field of your JSON output holds the reply |
sessionIdField | which field carries the session id |
nativeSession | true if your engine tracks its own sessions; the agent then passes the id back to you |
workdir | where the process runs |
Two placeholders are substituted into the argv before your process is started:
{message} what the caller said{sessionId} the conversation this belongs toA real entry, from a working agent:
{ "type": "process", "file": "/home/you/my-engine.sh", "argsNew": ["-p", "{message}", "--output-format", "stream-json"], "argsResume": ["-p", "--resume", "{sessionId}", "{message}", "--output-format", "stream-json"], "replyField": "result", "sessionIdField": "session_id", "nativeSession": true, "workdir": "/home/you/work"}So the whole of your obligation is: be startable with those arguments, and print JSON containing the fields you named. Everything else — proving who is calling, deciding whether they are allowed, encrypting both directions, keeping the session — is the agent’s, and already done by the time your code runs.
| the agent supplies | you supply |
|---|---|
| identity, and proof of the caller’s | the work |
| encryption, both directions | |
| sessions, and the id that threads them | |
| policy: who may call, and what they may reach |
When to write one
Section titled “When to write one”| You already have the logic | a service, a framework, an internal tool — put it behind an identity without rewriting it |
| You need a language we do not ship | it is a process, so the language is yours |
| You want your own loop | your own tools, your own model calls, your own control flow |
If you only want to expose an existing HTTP service to other agents, you do not need this page — An API you already run is shorter and seals a named route instead.
This is not theoretical, and you may not need to start from nothing. Ten agent frameworks run end to end this way with no code changes — CrewAI, AutoGen, LangGraph, LlamaIndex and the rest, each keeping its own native session mechanism. Each comes with a working runner you can put behind an agent and then replace with your own. See Your framework already works.
Sealing it
Section titled “Sealing it”The entry is stored under the agent’s own key, so it goes in through the CLI:
cd my-agenthexaeight-activate engine --add <type> \ --name my-engine \ --model "<route>|<model id>" \ --router "<router-identity>|http://127.0.0.1:5100" \ --file /home/you/my-engine.shAn engine that does its own work and never calls a model still takes --model and --router,
because the sealed entry has one shape. Point it at any route; it will not be used.
Writing the full entry
Section titled “Writing the full entry”For anything beyond the defaults — your own argsNew, a different replyField — write the object
and seal it directly:
./hexaeight-agent-linux-x64 add-engine --name my-engine \ --router "<router-identity>|http://127.0.0.1:5100" \ --json @./entry.jsonadd-engine seals the object verbatim, so anything you did not change survives byte for byte.
Wrappers
Section titled “Wrappers”file is often a small wrapper script rather than the engine itself. A wrapper sits in front of
your process and shapes the turn before it runs — runner provisioning uses one to impose a single
procedure and strip anything a caller sends that tries to choose another.
Two rules if you write one:
- It receives the argv the agent built, and must exec the real engine with everything it did not deliberately change.
- It must live outside the agent’s folder. Every turn runs with that folder masked, so a script kept inside it does not exist as far as the spawned process is concerned.
What your process runs inside
Section titled “What your process runs inside”- the identity folder is masked — your code cannot read
env-file, even though it runs as the agent - shell access follows the exec policy; a sealed procedure with no cmdset cannot run commands at all
- stdout is the reply channel. Keep diagnostics on stderr, or they become part of the answer
Checking it is sealed
Section titled “Checking it is sealed”[hexaeight-agent] engines: SEALED store (N engine(s))[hexaeight-agent] engine 'my-engine' -> process-> process is your engine. Sealed to one procedure, you get the stronger line instead:
[hexaeight-agent] engine 'my-engine' runs ONE mission (sealed): My_WorkflowChanging one field later
Section titled “Changing one field later”Export the sealed entry as plaintext, edit one field, re-seal:
./hexaeight-agent-linux-x64 export --plaintext --out ./dump.json# edit, then:./hexaeight-agent-linux-x64 add-engine --name my-engine --router "<router>" --json @./entry.jsonPass
--fileagain on a re-seal if the entry had a wrapper. Omitting it unbinds the wrapper silently: no error, and the turn returns in a second or two with no work done.
The export is plaintext and contains everything sealed in the entry, including any headers. Write it somewhere you control and remove it afterwards.