Runtime introspection tools
Harness authors can put model identity and runtime facts in prompts, but model-visible prose goes stale and models often answer identity questions from their own training prior. The runtime-introspection bundle gives the model a tool surface that reports the facts the runtime actually resolved for the current turn — model id, provider, context window, Harn release, host harness, capability matrix, and compaction policy.
It is opt-in. Minimal harnesses that never call
runtime_introspection_tools(reg) get no introspection surface at all;
hosts that want truthful answers wire it in explicitly.
Quick start#
import { runtime_introspection_tools } from "std/agent/introspection"
fn search_workspace(_args) {
return "results"
}
pipeline answer_identity_questions(harness: Harness, task) {
let reg = tool_registry()
reg = runtime_introspection_tools(reg)
reg = tool_define(
reg,
"search_workspace",
"Search the active workspace.",
{
handler: search_workspace,
parameters: {query: {type: "string"}},
returns: {type: "string"},
},
)
const result = agent_loop(harness,
task,
"You are a helpful coding assistant.",
{tools: reg, model: "claude-opus-4-7"},
)
return result
}
When the model asks "what model am I?" it now emits
current_model() and gets back
{model: "claude-opus-4-7", model_alias: nil, family: "anthropic-claude", tier: "high", resolved: true}
instead of guessing from training data.
Tools#
Every tool is executor: "harn" and dispatches through the VM stdlib
short-circuit (no Harn closure allocated per call). Each returns a dict
with a resolved flag — false until the first harness.llm.call on the
thread populates the snapshot.
| Tool | Returns |
|---|---|
current_model | {model, model_alias, family, tier, resolved} |
current_provider | {provider, tool_format, resolved} |
current_context_window | {context_window, runtime_context_window, resolved} (token counts) |
current_harn_version | {harn_version} — embedded HARN_VERSION from the build |
current_harness | {harness} — host shell embedding the runtime |
available_runtime_capabilities | {capabilities, resolved} — provider/model capability matrix |
current_compaction_policy | {policy} — transcript compaction policy (engine default today) |
runtime_introspection_tools(registry, options?)#
Add the bundle to a registry. Idempotent — calling it twice does not duplicate entries. Options:
{only: ["current_model", "current_provider"]}— narrow the surface to specific tools.{exclude: ["current_compaction_policy"]}— drop specific tools. Use this when the model doesn't need everything (smaller schemas, fewer tokens spent on tool definitions).onlyandexcludeare mutually exclusive;onlywins when both are set.
harness.runtime.introspection()#
A direct read of the underlying snapshot, exposed as a builtin for tests, observability, and scripts that need the data without going through a tool call:
const snap = harness.runtime.introspection()
harness.stdio.log("provider=" + to_string(snap?.provider))
harness.stdio.log("model=" + to_string(snap?.model))
harness.stdio.log("harn=" + to_string(snap.harn_version))
All fields are nil until at least one harness.llm.call has run on the
thread; harn_version and harness are always populated.
Configuring the harness identity#
current_harness() reports the value of the HARN_HARNESS environment
variable, trimmed. When unset or empty, it falls back to "harn" (the
bare CLI). Hosts that embed the runtime set it during process startup:
HARN_HARNESS=ide-host harn run main.harn
HARN_HARNESS=claude-code harn run main.harn
HARN_HARNESS=cloud harn run main.harn
The value is opaque to the runtime — pick whatever string your observability and prompt code want to read.
Allowlist#
The snapshot exposes only these fields, regardless of what the runtime knows internally:
harn_version, harness, provider, model, model_alias, family,
tool_format, tier, context_window, runtime_context_window,
capabilities
The introspection bundle deliberately does not surface:
- API keys or auth tokens
- Environment variables outside
HARN_HARNESS - The raw system prompt, conversation messages, or tool definitions
- Host-private paths (cwd, config files, log directories)
- Per-session budgets, rate-limit state, or cost accounting
Hosts that need richer introspection should expose their own bridged tools rather than extending this surface.
Introspection vs prompt prose#
Prompt prose ("You are running on Claude Opus 4.7 with a 200k context window") goes stale the moment the operator changes the model alias or the provider rotates a release. Introspection tools cannot go stale — they read the resolved facts the runtime is actually using for this turn.
Use the introspection tools when the model needs to:
- Answer identity questions truthfully (defensible product behavior)
- Adapt strategy based on context window (e.g. "I have 100k tokens left, I can read the whole file" vs. "I'm near the limit, I'll summarize")
- Pick capability-dependent code paths (vision, tools, structured output) without the script having to thread the capability matrix into the prompt
Keep prompt prose for stable, persona-level guidance.