Migrating from 0.6.x to 0.7.0
Harn 0.7.0 replaces the implicit transcript_policy dict with
first-class sessions. Session lifecycle is driven by
imperative builtins, and unknown inputs hard-error instead of silently
no-op’ing.
This guide lists every removed surface with a side-by-side rewrite.
transcript_policy on workflow nodes
The per-node policy dict is gone. Its fields moved to two dedicated setters plus lifecycle verbs.
Before (0.6)
workflow_set_transcript_policy(graph, "summarize", {
mode: "reset",
visibility: "public",
auto_compact: true,
compact_threshold: 8000,
compact_strategy: "truncate",
keep_last: 6,
})
After (0.7)
// Shape the node's compaction behavior:
workflow_set_auto_compact(graph, "summarize", {
auto_compact: true,
compact_threshold: 8000,
compact_strategy: "truncate",
keep_last: 6,
})
workflow_set_output_visibility(graph, "summarize", "public")
// To reset the stage's conversation explicitly before execution,
// open a caller-controlled session and wire it into the node's
// model_policy:
let sid = agent_session_open("summarize-v2")
workflow_set_model_policy(graph, "summarize", {session_id: sid})
agent_session_reset(sid)
mode: "fork" maps to agent_session_fork(src, dst?) called before
workflow_execute, wiring the fork id into the node’s
model_policy.session_id. mode: "continue" is the new default — two
stages sharing a session_id share a conversation automatically.
transcript_id / transcript_metadata on llm_call
Both keys were removed. Session id subsumes them.
Before
let result = llm_call("hi", {
transcript_id: "chat-42",
transcript_metadata: {user: "ada"},
})
After
// `session_id` is honored by `agent_loop`; `llm_call` is single-shot.
// For conversational continuity, move to agent_loop:
let sid = agent_session_open("chat-42")
let result = agent_loop("hi", nil, {session_id: sid})
If you relied on the transcript_metadata bag, attach it to the
session via your own store or pass per-call context through the
metadata field of injected messages. transcript_summary (per-call
summary injection for mid-loop compaction output) is unchanged.
transcript option on llm_call / agent_loop
Passing a raw transcript dict through the transcript option is now a
hard error.
Before
let t = transcript()
let result = agent_loop("task", nil, {transcript: t, provider: "mock"})
After
let sid = agent_session_open()
let result = agent_loop("task", nil, {session_id: sid, provider: "mock"})
// `agent_session_snapshot(sid)` if you want the transcript back as a dict.
The loop loads prior messages from the session store as a prefix before running and persists the final transcript back on exit.
Lifecycle via dict (mode: "reset" | "fork")
Previously some call sites accepted a lifecycle dict. That pattern is gone — call the verbs explicitly:
mode: "reset"→agent_session_reset(id)mode: "fork"→let dst = agent_session_fork(src)(optionally with a caller-provideddstid)mode: "continue"→ no-op; just reuse the samesession_id
Subscribers
CLOSURE_SUBSCRIBERS (thread-local in agent_events.rs) was removed.
Subscribers now live on SessionState.subscribers.
agent_subscribe(id, cb)opens the session lazily and appends.agent_session_forkdoes not copy subscribers — a fork is a conversation branch, not an event fanout.clear_session_sinksonly clears external ACP-style sinks now; it no longer evicts sessions.
Unknown-key / unknown-id behavior
A class of silent pass-throughs is now an error:
- Unknown
agent_session_compactoption keys. - Missing
roleonagent_session_inject. - Negative
keep_last. reset/fork/close/trim/inject/length/compactcalled against an unknown session id.
exists, open, and snapshot remain tolerant of unknown ids by
design.
agent_loop terminal status
max_iterations reached without a natural break now reports
status = "budget_exhausted" (previously "done"). If your host keys
off "done" to detect “agent is finished,” add "budget_exhausted" to
the accept list — the loop ran out of rope, not out of work. Daemon
loops in the same condition no longer silently relabel to "idle".
See the Sessions chapter for the full model and the 0.7.0 entry in the changelog for the complete breaking-change list.