Files
PaperClipAI/packages/paperclip-runner/docs/hermes.md
T
DottaandPaperclip 334db53750 fix(runner): retain unlabeled prefixes and complete tool JSON
Retain the published message identity when a provider begins labeling deltas. Preserve the original full-prefix oracle, separate subsequent message boundaries, and keep unknown JSON result fields inspectable. Record native, server and browser proof and the pending release gates.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-09 05:19:40 -05:00

15 KiB
Raw Blame History

Hermes native runner (pending qualification)

Hermes runs as a per-turn native ACP process through ACPX 0.13.1. The bridge adapts questions, active steering, tool policy, accounting and strict recovery; Hermes retains its own agent loop, native tools and memory. There is no Hermes gateway daemon, native cron scheduler, or SQLite polling transcript adapter. The standalone hermes_local and hermes_gateway adapters retain their contracts.

Distribution

The source-owned declaration is src/providers/hermes/version.json: release v2026.9.24, commit f97608f178d1ffeca59860195ab7da295f7c8e5f, Python 3.12.14, ACP SDK 0.9.0 and ACPX 0.13.1. Provisioning verifies the source archive and upstream uv.lock, then installs the locked ACP, MCP, Anthropic, Bedrock and Google extras. Use uv 0.12.17. No installation occurs during an agent turn. The source download uses GitHub's immutable codeload archive and checks the existing archive digest. Transient HTTP failures receive at most three attempts within two minutes; throttling respects Retry-After up to 30 seconds and longer delays produce an actionable setup failure. Downloads require no credentials.

# From the repository root, with an absolute, nonexistent destination:
node packages/paperclip-runner/scripts/provision-hermes.mjs \
  "$PWD/packages/paperclip-runner/provider-assets/hermes/darwin-arm64"
# Linux amd64 uses the linux-x64 destination instead.

For an installed public package, run paperclipai runtime setup hermes as the same OS user that runs Paperclip. The command uses the packaged setup entrypoint and materializer, downloads public pinned dependencies without model calls, and verifies all execution bytes before publishing the runtime to ~/.paperclip/runtimes/hermes/<platform>/<closure-digest>. It re-verifies an existing installation and refuses to overwrite invalid state. Setup is explicit; npm lifecycle hooks and agent turns do not provision Python. Read-only or global npm installations use this account cache without writing to the package directory.

The materializer emits a complete closure digest. Compare it with the reviewed platform pin in hermes-distributions.ts; never adopt an unexpected digest at runtime. Runtime launch verifies the installed bytes and host sandbox before credential staging, then opens a private verified snapshot for execution. Python uses -I -B and the bundled interpreter. The public server package carries the bridge, provisioner and materializer; Python is a separately provisioned asset, included by --candidate-providers=hermes in provider packs.

macOS command tools require sandbox-exec. Linux command tools require bubblewrap and permission to create its process/mount namespaces. Installing bubblewrap alone does not grant that permission. A default Docker container was observed to reject namespace creation; setup fails before credentials are staged there. Actual Daytona namespace behavior remains a qualification requirement. Building a provider pack verifies its bytes without probing the builder's sandbox; the execution host performs that probe at admission.

Maintainers can build the qualification image on the existing EC2 fleet with the Docker Runner check workflow. Select publish_eval_image=true and candidate_provider=hermes, and select the source branch with target_branch. The workflow resolves that branch to an immutable commit before checkout. The image-only job has a 45-minute deadline, includes the pinned Linux runtime, signs its digest and verifies public retrieval, image labels and provider-pack identity. It uses no model credentials or inference. Its retained artifact contains the immutable image reference and verification records.

This image build does not prove that an execution host permits Hermes's sandbox namespaces. Actual Daytona tool execution and restore remain separate release gates. The full-stack campaign workflow retains its own content-addressed image and signature contract.

Connections and identity

Use the existing account/model picker and the hermes_runner managed projection. API keys map to the matching native provider. Codex and Grok subscriptions are projected into Hermes auth.json and translated back through existing freshness, ownership and revocation checks. Claude subscriptions use the existing managed OAuth-token path. Custom Chat Completions, Responses and Messages routes retain their protocol, endpoint and authentication choice. Bedrock retains its managed region and bearer credential. These are implemented projections, not evidence that each provider/method has passed live qualification.

Native execution input v7 carries a value-free connection fingerprint and optional authorized attachments. Existing v1–v5 inputs and Dot v6 remain replayable. The reader also accepts recorded Hermes v6 qualification inputs when their provider is explicitly ACPX Hermes, normalizing them to v7. The fingerprint covers the selected account epoch and routing configuration; token refresh does not replace a compatible session. Model, account, endpoint and permission changes use the existing replacement rules.

Credentials use the ephemeral runtime path and process-lifetime fence. Native auth, refresh handoff, configuration, environment files and diagnostic logs are excluded from recovery copies. Only hermes/memories and hermes/skills enter managed agent-file storage. Session databases stay private to a normalized conversation. Assigned Paperclip skills remain separate protected inputs.

Between turns, authenticated run attachment refreshes the registered agent-file working copy and assigned tool bindings. The session still pins prompt, bundle, skill, connection, model, and permission identities. Unknown policy changes and changes within the same run are rejected.

Native session admission has a 60-second bound for the verified private Python copy and ACP initialization. The controller allows 75 seconds around cold startup, recovery and later-turn restoration. Once a turn is accepted, its start-event deadline remains 30 seconds. Ordinary commands, cancellation and process cleanup retain their existing bounds; this startup allowance does not extend the task's execution deadline.

Interaction contract

  • Reasoning and assistant text use distinct message identities. Native tool call IDs preserve overlapping calls and edit snapshots.
  • Assistant message identity v2 includes the provider message and provider turn. A final snapshot replaces only its own deltas, retaining preceding commentary and steered messages. Providers without message IDs retain the original identity. If a provider first supplies an ID after an unlabelled prefix, that prefix and the continuation retain one item identity and one final snapshot. A later distinct native message gets its own item. Existing PRP v1 events replay unchanged. This is an identity change within the current event contract, not a new wire field or transcript rewrite.
  • Native questions publish a 65,536-character limit for text and custom answers. The form, canonical response validation and bridge count UTF-16 code units consistently, so an accepted answer can resume the native callback.
  • Images accept PNG, JPEG, WebP and GIF; documents accept UTF-8 text/Markdown. Limits are eight attachments, 2 MiB per item and 4 MiB total. The combined message and attachments also have a 7 MiB JSON-encoded limit, including escaping and metadata, to fit the encrypted Runner command frame. The controller authorizes and reads content before the turn; URLs and arbitrary local paths are rejected. Models without image support can reject image input.
  • The negotiated _meta.paperclipHermes.version = 1 extension uses ACP wire methods _hermes/turn_started, _hermes/ask_questions, _hermes/steer, _hermes/usage and _hermes/delegation. Python's ACP SDK adds the leading underscore. Questions and controls bind to the active session and turn token.
  • Steering requires an explicit native acknowledgement. Follow-ups remain in the durable Paperclip queue. Stop cancels native work and pending input before bounded provider-process cleanup.
  • Restored history is recorded without becoming new assistant text or live tools.
  • Restoration requires nonempty native history and follows the native compaction chain. Missing history and failed persistence are errors.
  • Usage is a per-prompt delta. Clients negotiate billingReceipts: 1 inside _meta.paperclipHermes; the optional closed paperclip.usage.billing/v1 receipt remains replay-compatible with older events.
  • For the managed OpenRouter Chat Completions route, the bridge observes the pinned SDK's wire responses and sums provider-reported usage.cost, including retries and auxiliary synchronous calls. The receipt carries request counts, completeness and an exact nine-decimal USD amount. Paperclip binds it to the selected OpenRouter billing identity and the current native turn before using it for accounting. Cumulative session cost and native model-price estimates cannot supply this receipt.
  • During Stop, requests and steering close immediately. The same admitted Hermes prompt may still deliver its final usage notification with the exact native session and turn token before prompt settlement. Stream or process closure ends that receipt window. Missing charges remain unpriced.
  • Failed, interrupted, unsupported asynchronous and background delegated work leave measurement totals incomplete. Known positive reported subtotals survive, but missing charges remain unpriced. Explicit reported zero is accepted only for a complete receipt. Other provider/protocol routes retain their existing unavailable billed-cost behavior. Complete live billing, including delegated work, remains a qualification gate; these checks do not qualify a provider.
  • Once the native provider boundary closes, an optional versioned accounting settlement can acknowledge that known subtotal even when tokens or charges are incomplete. Missing token totals stay unknown, incomplete charges stay unpriced, and the existing controller finalization and capture-failure fences still control acknowledgement. Older incomplete receipts remain pending.
  • A governed input wait can stop transcript consumption before native usage arrives. For Hermes's per-turn lifecycle, the runtime joins the authenticated notification mapper after owned shutdown and reads its last usage event. This read-only fact must match the original runner, session, run and turn, then enters the existing durable accounting journal. It cannot reopen tool authority. Missing, invalid or timed-out reads do not invent usage or cost. This optional execution-result field carries an existing v1 PRP event; it does not change persisted execution inputs or the Rust wire contract.
  • An applied pending request_human_input response for a question, confirmation or checkbox confirmation stops Hermes in its native tool completion callback, before another model request. The bridge publishes that tool's completed result after native finalization. Both Runner event pumps read the prompt usage receipt before forwarding this completion to the controller. The tool bridge also defers its own completed human-input fact until the native terminal notification, which follows that receipt. The managed Hermes profile selects this launch policy; other profiles retain their existing order. Other tool activity still streams immediately. Tool identities and results are preserved; wait and accounting authority remain with the controller. Missing usage remains unknown.
  • Paperclip owns task titles. Hermes keeps its immediate derived session title; its paid background title upgrade is disabled in the managed profile so it cannot start inference after a turn's usage receipt has closed.

The managed execution middleware applies planning/read-only, protected-path and permission policy to inline, concurrent and delegated native tools. Assigned MCP tools use the authenticated runner bridge; startup fails if that catalog cannot be loaded. Ambient configured MCP discovery and unmanaged plugin/config loading are disabled.

Proactive work

Paperclip owns scheduling. Native cronjob, gateway messaging and native task mutations are disabled in this profile. The live manage_routine semantic tool creates, updates, pauses and resumes self-assigned routines through the existing routine service. Updates require the current revision and retries require an idempotency key. Listing/inspection use existing authorized read APIs.

Scheduled firings create ordinary Paperclip work with routine provenance and retain existing ownership, budget, pause, concurrency and duplicate-fire rules.

Provisioning requires uv 0.12.17 and, on macOS, Command Line Tools for install_name_tool plus codesign. The Python closure normalizes interpreter installation paths so local consumers and image builds reproduce the same pin. Credential cleanup runs after verified exit even when learned-state collection fails; save failures remain visible through normal runtime errors.

Qualification

Hermes stays visibly pending. Candidate execution is an operator diagnostic opt-in, never enabled by agent-supplied session JSON. The server's existing PAPERCLIP_RUNNER_ACPX_QUALIFICATION allowlist accepts an exact Hermes/model entry. The runner CLI accepts --candidate-profile hermes.

pnpm --filter @paperclipai/paperclip-runner build:typescript
pnpm --filter @paperclipai/paperclip-runner test:hermes:transport

The browser campaign definitions and implementation record are supplied by qualification PR #15436. Apply that companion PR before running its commands:

pnpm test:e2e:runner -- --list --suite extended-harnesses
pnpm test:e2e:runner -- --id extended-harnesses.runner-acpx-hermes.local.hello-complete

The opt-in transport tests execute the pinned native process against a deterministic HTTP model fixture. It is not paid-model, browser or Daytona qualification. The fixtures exercise the ACPX host and the full TypeScript, Rust PRP and packaged sidecar path, including image delivery and semantic task completion. Native question batches cover single selection, multiple selection, custom answers and free text. They require one callback result, reject a second answer, and check both question cancellation and Stop during a pending question. Stop must prevent a later model request and cannot produce task completion. The companion Product E2E catalog contains five local and five Daytona Hermes cells using a managed OpenRouter connection. Each connection method, native control, attachment, persistence, remote restoration and permission mode must pass its release criterion with inspectable live evidence before promotion. The implementation record is included in that qualification PR.

The Hermes Native Transport PR workflow builds and runs the same native fixtures on GitHub-hosted Linux amd64 and Mac arm64, with a stripped provider environment and deterministic loopback endpoints. Its CI artifacts include source checkout and tree identity, runtime closure, tool versions, fixture logs, and a checksum for the exact staged daemon used by the fixtures. A binary archive preserves executable permissions for subsequent acceptance work without local Rust builds. Check the recorded job and fixture outcome before using an artifact; failed jobs also retain evidence. This is credential-free transport and host-sandbox evidence; it does not replace paid-provider, browser, subscription or Daytona qualification.