Hane Platform
Agents and AgentRuns
How agent definitions, drivers, tools through the Tool Gateway, approvals inside runs, conversation context, and evaluations work.
This page explains how Hane agents are defined and run. It covers which drivers are available where, how agents call tools, how outbound actions are approved, and how to evaluate an agent.
Agents and AgentRuns
Section titled “Agents and AgentRuns”An agent is a definition with a purpose, the tools it may use, limits, and input and output schemas. You write it as a manifest (Agent in agents/v1alpha1) and activate it in a ReleaseBundle, like any other definition.
An AgentRun is one run of an agent from the active release. It holds the input, the result, a summary of tool calls, the tokens used, and the status. A run starts as queued or running and ends as succeeded, failed, cancelled, or limit_exceeded.
| Field | Meaning |
|---|---|
purpose, examples |
What the agent does. Reception also uses these to pick an agent from free text |
input, output |
JSON Schema for the input, and for the result returned with submit_result. The result must have a status |
driver |
How the agent runs (see the table below) |
instructions, script |
Instructions for the model, or the steps for the scripted driver |
tools |
Tools the agent may call. The Tool Gateway checks this list on every call |
limits |
maxToolCalls, maxSeconds, maxModelTokens (tokens for one run; default 50,000) |
approval, handover |
Assignment rules for approving outbound actions and for handing work to a person |
workflows, connections, toolExposures |
Workflows the agent may start, and customer APIs it may call |
apiVersion: agents/v1alpha1kind: Agentmetadata: name: vehicle-checkspec: purpose: Look up a vehicle's model, inspection expiry, and mileage in the fleet ledger input: type: object required: [vehicle] properties: vehicle: { type: string, title: Registration number } driver: opencode instructions: |- Find the vehicle with data.find_record and answer with submit_result. If it is not found, hand over to the fleet desk with request_human_help. Never invent values. tools: [data.find_record, request_human_help] handover: assignment: fleet-desk limits: maxToolCalls: 4 maxSeconds: 120 output: type: object required: [status, vehicle] properties: status: { type: string } vehicle: { type: string }Drivers
Section titled “Drivers”The driver decides what runs the agent. Every driver calls tools through the same Tool Gateway and returns its result with submit_result.
driver |
How it works | Where it runs and availability |
|---|---|---|
scripted |
Calls tools in the order given in script. Uses no model |
Runs in every environment |
llm |
Lets a model choose the tools | Preview Runs on recorded responses in local development. Not yet enabled in the cloud environment |
opencode |
Starts the open-source harness opencode in a sandbox container. Model calls go through the Model Gateway | Preview Runs in the cloud sandbox. Verified with Cloudflare Workers AI as the upstream model |
codex, claude |
Starts Codex CLI or Claude Code on a developer’s machine and lets it use only Hane’s MCP tools | Preview For local development and verification only. Not offered in the cloud environment |
CLI drivers (opencode, codex, claude) choose a pair of CLI version and sandbox image with cliProfile. Without it, each driver uses its baseline pair. The ReleaseBundle fixes which pair runs, so upgrading or rolling back a CLI goes through the same evaluation and activation path as any definition change.
Tools and the Tool Gateway
Section titled “Tools and the Tool Gateway”Agents always call tools through the Tool Gateway. The driver attaches a short-lived RunToken issued for that run. On every call, the Tool Gateway checks that the tool is in the definition’s tools, that the run has not been stopped, and that no limit is exceeded. When you cancel a run, the next call is rejected without waiting for the RunToken to expire.
The main tools are:
| Group | Examples |
|---|---|
| Reading the ledger | data.find_record, data.get_record, ontology.query, ontology.knowledge |
| Reading documents | documents.search, documents.read_pages, documents.read_summary |
| Moving work forward | workflow.start, workflow.get_case, tasks.create, request_human_help |
| Customer APIs | integration.describe, integration.read, integration.execute |
| Outbound actions | communication.send_email, communication.send_sms, communication.place_call, browser.run_flow |
| Results | submit_result (submit_review for review agents) |
To turn an operation of an API you publish into an agent tool, define a ToolExposure. The resulting tool definitions (ToolDefinition: input and output schemas, impact, timeout, and so on) are listed by GET /v1/agents/tools. See Integrations.
Tool results, document text, and incoming email are treated as untrusted input. Instructions written in them never grant permissions.
Approvals inside a run
Section titled “Approvals inside a run”Outbound actions such as email, SMS, phone calls, and updates through a customer API do not run when the agent calls them. Hane fixes the recipient and content into an ActionRequest with a digest and creates an approval task. Once approved, the action runs exactly once through the execution ledger.
- Who may approve is decided when the action is proposed, by the DeciderPolicy named in the agent’s
approval.decider. Without one, a person decides every action. - Depending on the rules, a review agent can approve, reject, or escalate to a person, or a policy approval can approve without creating a task. Decisions by AI or by policy never count as human approval.
- A decider override (
POST /v1/agents/decider-overrides) returns a tool’s approvals to people only, without waiting for a new ReleaseBundle. - An action whose outcome is unknown becomes
unknown. Hane does not resend it; a person reconciles it (POST /v1/agents/action-requests/{id}/reconcile).
See Approvals for the full flow.
Starting a run
Section titled “Starting a run”An AgentRun starts in one of these ways:
- Pass the agent name and input to
POST /v1/agents/runs - Pass free text to
POST /v1/agents/requests(reception), which chooses the agent and the input - A Workflow
agentstep, a schedule, or a message in the built-in chat
import { createHaneClient, unwrap } from "@hane/sdk";
const client = createHaneClient({ baseUrl, token: async () => accessToken });const run = await unwrap( client.POST("/v1/agents/runs", { body: { agent: "vehicle-check", input: { vehicle: "架空 300 あ 10-02" } }, }),);With shadow: true, the run skips outbound actions and handovers and only keeps a record.
Limits and budgets
Section titled “Limits and budgets”The definition’s limits cap a single run. A Budget in platform/v1alpha1 sets daily (UTC) limits for a project and environment.
apiVersion: platform/v1alpha1kind: Budgetmetadata: name: daily-budgetspec: agentRunsPerDay: 200 externalActionsPerDay: 50 modelTokensPerDay: 200000 alertAtPercent: 80When Hane creates a run that uses a model, it first reserves maxModelTokens from the daily budget. If not enough remains, the run does not start. Read today’s usage with GET /v1/agents/budget.
Conversation context
Section titled “Conversation context”In built-in chat conversations, Hane stores the messages, run inputs and results, and action states, and builds the context passed to the model from them. It does not depend on CLI sessions.
With /v1/agents/conversations/{id}/memory, you can read and save the current context as versioned JSON, and start a new conversation from a saved history. The saved JSON never contains RunTokens, API keys, or credentials, and a restored history never overrides current permissions or the execution ledger.
Evaluations
Section titled “Evaluations”An EvalSuite in agents/v1alpha1 lists up to 50 pairs of input and expected result. POST /v1/agents/evals runs the agent in shadow mode for each case and compares the result fields. GET /v1/agents/evals/compare compares two evaluations of the same suite case by case.
apiVersion: agents/v1alpha1kind: EvalSuitemetadata: name: lease-qa-evalspec: agent: lease-qa passRate: 1 cases: - name: Contract not in the ledger input: { contract_no: C-9999 } expect: { status: handed_over }