Custom tools execute in your application. For work that cannot finish while the
agent is waiting, return a running result promptly, continue the external job,
then send a final result when it finishes.
This keeps tool calling from blocking the chat for the full lifetime of an
external job. The kickoff releases the pending tool call so the agent can respond
and the conversation can continue while your application finishes the work in
the background.
How the flow works
- The stream emits
agent.custom_tool_use.
- Your application starts the external workflow.
- Within five minutes, post
user.custom_tool_result with status: "running".
- That kickoff resolves the blocked custom tool call and lets the current agent turn continue.
- When the external workflow finishes, post a second result with
status: "completed" or "failed".
- Herm updates the original running result, emits
session.tool_updated, and starts a new agent turn with the final text.
If another turn is running, Herm queues the final result and starts its
continuation automatically when the session becomes idle.
Start the workflow
The pending tool call allows approximately five minutes for a response. Send the
kickoff well before that deadline:
status: "running" is not a progress-only event. It is the response to the
pending tool call, so the original agent turn resumes immediately with the
kickoff text as tool output. Herm does not poll or supervise the external job.
Store the sessionId, tool_use_id, and the accepted result event id with
your job record.
Complete the workflow
When the workflow finishes, send the final result with the same tool_use_id:
For failures, use status: "failed", set is_error: true, and include enough
context in content for the model to respond appropriately.
When a matching running result exists, the completion replaces that result in
event history. Its text is submitted to the agent as a new prompt, which starts a
separate turn. Without a matching running result, the final result is appended as
a new event and still starts a turn.
Live streams receive an ephemeral session.tool_updated event:
Patch the existing event identified by payload.event_id with
payload.payload. The patch event itself is not retained. The client posting the
completion should also apply the updated event returned in the 202 response in
case its stream misses the ephemeral patch.
Retry safety
Final-result submissions are idempotent by session and tool_use_id. A retry
returns the existing result instead of starting another continuation.
An immediate or running result can return 410 response_expired with
"retryable": false if the session stopped waiting for it. Do not retry that
response: Hermes has discarded the original pending tool call, so it can no
longer be delivered. Send a new user message if you want the agent to try the
action again.
Permission policies
Custom tools use the same always_allow, always_ask, and always_deny
permission policies as other tools. With always_ask, handle the preceding
agent.approval_request before expecting agent.custom_tool_use. Your
application remains responsible for authorization, input validation, retries,
idempotency, and side-effect safety. Last modified on August 2, 2026