Skip to content

Quick start — a framework for outside callers

From nothing to another system calling your framework, in eight steps. The framework does the work; HexaEight decides who may ask, what model it uses, which command may run, and exactly what it can reach.

The one idea to hold on to: a runner reaches only the memories you share into it. Your documents are shared as local memories; your APIs are shared as served memories. There is no other door — no workspace memories, no files, no shell.

  • A working agent (the install), with agent r30, harness engine r41 and Activate 1.0.68 or later: hexaeight-activate verify-env.
  • A router route whose model makes real tool calls. Measured: one route’s model answered “what is the weather” by inventing a tool result; another called the tool every time. You will check this in step 7.
  • Python 3.10–3.13, or uv (it fetches one).

The examples use CrewAI; langgraph, pydanticai and agno work the same way. Folder names follow the install: ~/hbia-agent is the licence folder (your identity files), ~/heia-agent is your agent’s folder — use yours if they differ.

Terminal window
cd ~/heia-agent # your agent's folder
hexaeight-activate enable crewai

In the workspace choose runmission → CrewAI_v1_Runner and say hello. The answer is headed Answered by CrewAI. This is the same mission the runner will serve — see What a mission is.

A runner is a second agent, sealed to one mission, that serves outside callers:

Terminal window
hexaeight-activate add-runner --from ~/hbia-agent --dir ~/runner-crewai \
--mission CrewAI_v1_Runner --owner [email protected] --license personal \
--model "<route|model>"

--from supplies the identity only (hardlinked, never copied). The runner gets its own ports, policy, store and working folders.

Terminal window
cd ~/runner-crewai
hexaeight-activate enable crewai

Run inside the runner’s folder, enable installs the mission into the runner’s own store and seals its one command there (this folder is a runner: its own harness root).

4 · Give it information — documents, as memories

Section titled “4 · Give it information — documents, as memories”

The runner sees only its mission until you share something:

Terminal window
hexaeight-activate runner-memory --list
hexaeight-activate runner-memory --share Product-Handbook # linked: no copy
hexaeight-activate runner-memory --share Product-Handbook --copy # or a self-contained copy

A local memory is one you built in the workspace from your documents (Documents it can search). Linked, the runner searches the same index and sees updates; copied, it keeps its own.

An API reaches a runner the same way: as a served memory — a name that stands for a sealed route on another agent.

  1. Seal the API on the agent that runs it — An API you already run:
    Terminal window
    cd ~/api-agent && hexaeight-activate add-api --name weather --port 38490
  2. Point a memory at it. add-api prints the pointer’s exact fields. Add it as a served memory in the workspace — that writes ~/.hexaeight-harness/memories/weather/remote.json — or save those fields yourself to that path (what a pointer holds).
  3. Share it with the runner — this copies that pointer into the runner’s store:
    Terminal window
    cd ~/runner-crewai && hexaeight-activate runner-memory --share weather
  4. Let the runner call that agent, and that agent accept the runner — one rule on each side (the two rules); add-runner --peer <api-agent> or modify-agent --dir ~/runner-crewai --peer <api-agent> writes the runner’s side.

Every call then goes through service_call, over the turn’s ticket, under the runner’s policy. The framework never sees a host, a port or a key.

Other agents need no memory of their own. The runner has connect-to-agent built in — memory_search("connect-to-agent", "<agent name> <question>"). Because a runner always answers someone else, it will not ask a third agent for them unless you switch relay on (No relay by default).

Terminal window
cd ~/runner-crewai && hexaeight-activate restart agent

This stops and starts only the agent in this folder, and gives the runner its own store. Its agent.log must show:

[hexaeight-agent] engine 'missionrun-external' runs ONE mission (sealed): CrewAI_v1_Runner
[licence] … policy rule(s) in force …
[mcp-external] OPENED BY CONFIG (external.allowTools): service_call,who_am_i

A caller is a backend acting for a person. Three stores must name that person as sha512(email) — the runner’s vouch and inbound rule, and the router’s policy. The exact commands are in Other software calling it.

The call, from the caller’s own folder (any folder holding an identity):

Terminal window
cd ~/caller # the caller's identity folder
set -a; . ./env-file; set +a # the caller's identity
export HEIA_DIR=~/caller-heia # its OWN store — never the runner's, or the door answers your question back to you
export HEIA_ASK_TIMEOUT_SECONDS=0 # wait for the work; the runner bounds its own turn
SUB=$(printf '[email protected]' | sha512sum | cut -d' ' -f1)
printf '{"ask":"What is the weather in Chennai?","on_behalf_of":"%s","peer":"<runner-agent-name>","peer_url":"http://127.0.0.1:<runner-port>","capability":"crewai"}\n' "$SUB" \
| ./hexaeight-agent-linux-x64 ask-daemon

peer is the runner’s agent name (its identity), peer_url its port from step 2, and capability a label for this kind of request. Only the output line starting with { is the result; the answer is the result field of its last frame.

Check it really used the tool — each caller session has its own folder under ~/runner-crewai-frontdoor/work/, and the framework’s log there has one line per tool call:

[tool] search:weather('Chennai') -> '[weatherapi.com] Current weather in Chennai … 29.9°C …'

An answer with no matching [tool] line was not fetched — change the runner’s model (step 2).

The runner is runner.py inside the mission: a HexaEight half (identical in every framework) and a framework half, run(question, session_id). Duplicate the mission, change the framework half, and seal your version — see Your framework already works.

you seeit means
an answer that names a source, but no [tool] line in the logthe model invented it — use a model that makes real tool calls
nothing found in '<name>'that memory is not shared into the runner — runner-memory --share <name>
'<tool>' is not available on the external serverthe runner’s external tools do not include it — modify-agent --allow-tools service_call,who_am_i
Connection refusedthe runner is still starting — give it ~25 s after restart agent
a caller can list every memory on the machinea runner made before Activate 1.0.67 — re-create it with add-runner