Skip to main content

Agent Harness Runtime Scaffold

Status (2026-05-28): SUPERSEDED on the production execution path by the RIG runtime control plane (#696 / #947 / #981 / #1024). The @humanwork/agent-harness package may persist for in-process Hermes session lifecycle, but it is no longer the platform's runtime authority โ€” /tools/execute, /v1/tasks*, and the legacy direct-execution authority paths were removed in #696. See docs/architecture/RIG_HARD_CUT_RUNTIME_PORT_PLAN.md and docs/architecture/ISOLATION_SANDBOXING_BINARIES.md for the current control plane.

This scaffold captures the runtime abstraction approved in the Slack thread: Humanwork owns the API contract, prompt/config, org context, approval policy, and persistence boundary; Hermes, Claw, PI, or another engine plugs in behind a runtime interface.

Cut Lineโ€‹

Above the harness, the Humanwork API stays responsible for routing, auth, org resolution, quotas, billing, and durable platform persistence.

The harness is the reusable lifecycle layer:

  • createAgentSession(...) creates a fresh per-call session.
  • createAgentSessionRuntime(...) owns the current session through a small PI-style lifecycle wrapper, with hooks for session start, switch, shutdown, and replacement.
  • SessionManager.inMemory() keeps pure ephemeral sessions.
  • SessionManager.jsonl(root) keeps PI-style durable session logs.
  • session.subscribe(...) exposes lifecycle, message, tool, approval, and error events to the platform.
  • toolCallHandlers provide PI-style pre-tool interception. Handlers can mutate tool input or block execution before the normal approval policy runs.
  • session.prompt(message) runs the runtime loop until completion, approval pause, or failure.

Below the harness, an AgentRuntime adapter owns execution. Hermes is one adapter, not the harness itself.

The first concrete adapter is HermesAgentServiceRuntime, which calls the existing Python agent service /v1/chat contract. That keeps the current Hermes CLI implementation in place while proving the harness can drive the working system.

Prompt And Config Ownershipโ€‹

The platform side supplies systemPrompt, allowed tools, orgContext, and approvalPolicy when creating the session. The runtime receives those values as input; it does not define the Humanwork product contract.

import { createAgentSession, SessionManager } from '@humanwork/agent-harness';

const { session } = await createAgentSession({
sessionId: conversationId,
sessionManager: SessionManager.jsonl('/workspaces/acmefinancial/sessions'),
runtime: hermesRuntime,
systemPrompt: orgConfig.systemPrompt,
orgContext: {
orgId,
specialistId,
conversationId,
},
tools: orgConfig.allowedTools,
approvalPolicy: {
mode: 'risk',
gatedRisks: ['high', 'destructive'],
},
});

session.subscribe((event) => {
if (event.type === 'message_update') {
// Stream to Slack/platform.
}
if (event.type === 'approval_required') {
// Persist pending step and ask the expert.
}
});

const result = await session.prompt(userMessage);

For hosts that need to own session replacement separately from prompt execution, use the runtime wrapper:

import {
createAgentSessionRuntime,
SessionManager,
} from '@humanwork/agent-harness';

const runtime = await createAgentSessionRuntime(
{
sessionId: conversationId,
sessionManager: SessionManager.jsonl('/workspaces/acmefinancial/sessions'),
runtime: hermesRuntime,
systemPrompt: orgConfig.systemPrompt,
},
{
hooks: [
(event) => {
if (event.type === 'session_shutdown') {
// Detach host-owned streams or UI before switching/dispose.
}
},
],
},
);

await runtime.prompt(userMessage);

Hermes Agent Service Smokeโ€‹

Start the existing Python agent service, then run:

AGENT_SERVICE_URL=http://localhost:8000 \
AGENT_SERVICE_SECRET="$AGENT_SERVICE_SECRET" \
bun run agent-harness:smoke -- "Can you confirm the harness can reach Hermes?"

That path is:

@humanwork/agent-harness
-> HermesAgentServiceRuntime
-> Python agent service /v1/chat
-> agent/hermes_client.py
-> real hermes chat CLI

The smoke writes a PI-style session log under .agent-harness-sessions/ by default. Override it with HARNESS_SESSION_LOG_DIR.

Approval Modesโ€‹

auto is the default and runs the full loop without pausing.

risk pauses only for configured tools or risks, for example destructive external writes.

step pauses before every tool execution. This supports the issue #172 style expert approval flow without making it mandatory for normal tasks.

State Modelโ€‹

The harness is process-ephemeral, not state-free. Session state is represented as an ordered log. For the MVP scaffold, that log can be in-memory or JSONL on disk. The Humanwork API can later flush JSONL into Postgres/R2 without making the runtime adapter aware of platform storage.

Current Locationโ€‹

The scaffold lives in packages/agent-harness as a standalone package. The repo is wired as a Bun/Turborepo workspace, so callers can import it as @humanwork/agent-harness once the package is consumed by the API or another runtime host.