Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Agent plane ownership

Harn has one public agent-loop entrypoint: agent_loop. HarnessAgent owns session mutations, checkpoints, event emission, transcript projection, and terminal classification. HarnessLlm owns one model request, and HarnessTools owns dispatch. Keeping those responsibilities separate prevents a convenience wrapper from creating another lifecycle or result vocabulary.

Typed stages#

The loop crosses agent_stage(agent, session_id, stage, input?) at each safe injection boundary. AgentStage is a closed vocabulary covering iteration, compaction, tool dispatch, command hold, daemon idle, and loop exit. The seam accepts only HarnessAgent; model and tool handles stay in the stages that use them. Every checkpoint event, queued injection drain, and session hook therefore observes the same stage name.

Specification and result contracts#

AgentSpec is the intersection of six named records: model, execution, capability, lifecycle, context, and observability. These records are a static projection over one flat runtime value, not nested configuration and not a second normalizer.

AgentResult describes the value returned by agent_loop and HarnessAgent.session_finalize. Its terminal member is projected from the Rust AgentTerminalKind owner and checked against it in the VM test suite. ACP metadata, CLI protocol artifacts, A2A task status/metadata, replay run records, and Harn callers consume that projection instead of classifying final_status or stop_reason themselves. A completion judge that cannot accept before its deadline or policy limit produces completion_unverified; A2A projects that to failed. Text-only nudge exhaustion produces policy_no_progress, while a stall-governor hard stop after repeated equivalent actions or observations produces policy_thrash. A2A suspension remains wire-compatible as working plus metadata.harn.pause until the proposed paused state is standardized.

Tool registries#

AgentModelSpec.tools references ToolRegistry from std/tools. It no longer contains a local structural copy. The typechecker and conformance suite verify that registries cross package boundaries and remain assignable to AgentSpec.

Package boundary#

The agent contracts, LLM dialects, and provider catalog remain in harn-vm. They share normalization, dispatch, replay, and terminal semantics with the runtime, and every current Rust consumer needs that complete behavior. Harn Cloud links harn-vm and harn-serve; Burin and 20eq consume the versioned Harn toolchain rather than a dialect-only library.

Extracting a catalog or dialect crate would create another versioned projection without an independent consumer or release cadence. The deeper boundary is the small typed interface inside harn-vm: capability data selects one dialect, AgentSpec configures one loop, ToolRegistry supplies its tools, and AgentResult reports its terminal state. Generated catalog artifacts and drift checks keep external projections aligned with the same release.

Completion checkpoints#

Built-in completion judges are terminal checkpoints, not worker turns. Harn projects the immutable worker transcript into bounded effect and verification evidence. Judge requests exclude callable tool schemas and raw transcript replay. The judge request and response are not appended to harness.agent.messages(session_id).

The task, rubric, and output schema occupy the stable start of the request. The changing user message carries the latest mutation, verification, problem, and transcript-integrity evidence. This placement lets a provider reuse the stable prompt prefix, but cache behavior is only a performance detail.

The harn.completion_judge_cache.v1 checkpoint records cache eligibility, a hash of the static prefix, and provider-reported cache reads and writes. These fields are telemetry. They do not prove equivalent evidence and never reuse a prior verdict.

Deadline admission uses monotonic remaining time, the configured operation budget, and the terminal reserve. prompt_token_estimate is a rough character-count estimate, not a provider tokenizer result, and does not decide admission. The VM bounds the complete checkpoint operation, including local setup and response parsing.

harn.completion_judge_evidence_projection.v1 records what evidence entered the request, including how many read-only calls carried a record in the packet's research_summary and how many did not. harn.completion_directive_receipt.v1 records the final action, reason, admission result, and whether repair feedback reached the next turn. evidence_id and transcript digests correlate these records and reveal changed input; they never substitute for a new judgment.

A continue result injects repair feedback because it is meant to steer the worker. An accept or stop_unverified result changes only events and terminal state. Tests should assert the worker transcript around accepted judge calls instead of inferring isolation from event counts.