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

Migrate to the single agent plane

This migration removes public wrappers that owned overlapping loop, chat, and editor-completion behavior. Migrate each call to the capability that owns its lifecycle.

Replace AgentLoopOptions

Rename the annotation to AgentSpec. The fields remain flat, so existing values need no reshaping.

import { AgentSpec, agent_options } from "std/agent/options"

const spec: AgentSpec = agent_options({
  provider: "openai",
  model: "gpt-5-mini",
  loop_until_done: true,
})

Use the component records when a function accepts only part of the contract: AgentModelSpec, AgentExecutionSpec, AgentCapabilitySpec, AgentLifecycleSpec, AgentContextSpec, and AgentObservabilitySpec.

Replace agent_turn

Call agent_loop and set completion policy explicitly. Use done_judge when a judge must approve completion.

const result = agent_loop(harness, task, system, agent_options({
  provider: "openai",
  model: "gpt-5-mini",
  loop_until_done: true,
  done_judge: true,
}))

Read judge decisions from judge_decision session events. The removed wrapper's separate iterations and judge_decisions result summaries are not part of AgentResult.

Replace agent_llm_turn

Use harness.llm.call(prompt, system?, options?). One request belongs to HarnessLlm; adding an agent-named wrapper does not make it an agent loop.

Replace agent_chat_loop

Keep input handling, slash commands, and presentation in the host. Invoke agent_loop once per prompt turn. Pass history when the host owns conversation storage, or reuse a session_id when the Harn session owns it.

const result = agent_loop(harness, message, system, agent_options({
  session_id: conversation_id,
  history: stored_messages,
  tools: tools,
}))

Use typed HITL or agent_await_resumption for suspension. Do not recreate the removed wait_for_user convention as a terminal string.

Move editor completions to the host

std/agent/completions is removed. Editors own cursor state, suggestion UI, acceptance decisions, and product telemetry. Call harness.llm.completion for one completion request and store host-specific envelopes in the product layer.

Update result handling

Annotate loop results with AgentResult from std/agent/contracts. Branch on result.terminal.kind; retain stop_reason only for diagnostics.

agent_loop no longer returns an untyped nullable dictionary. Remove defensive optional access on the result itself (result?.status becomes result.status); keep optional access only for fields whose declared type is optional.

If a reader imports AgentResult from std/agent/artifacts, rename that annotation to AgentResultArtifact. The artifact type remains compatible with persisted v1/v2 files; AgentResult now names only the live loop contract from std/agent/contracts.

import { AgentResult } from "std/agent/contracts"

const result: AgentResult = agent_loop(harness, task, system, spec)
if result.terminal.kind == "natural" {
  return result.visible_text
}
throw "agent stopped: " + result.terminal.kind + ": " + result.terminal.reason