Skip to content
5 min read · 937 words

Context channels

This is a featured battery

The channel is per skill. The deployer chooses the default; the source may override it.

A channel is how a loaded skill's operational prose reaches the model on each dispatch. Under handle — the default — the body stays spooled and the model sees a compact pointer costing a few dozen tokens, calling a reader tool when it actually needs a section, so the guidance never crowds out the conversation's budget. Under inline, the full body is rendered into the trust-tier envelope every dispatch: no reader round-trip, but the whole thing is charged for as long as the skill stays loaded. Everywhere else in the industry "loading" a skill means appending its manual to the chat log permanently, which is the unglamorous reason nobody ships an unload. ADK reassembles dispatch context from scratch, so both channels are ephemeral projections and unload reclaims either one. The trade is budget, not permanenceinline costs more while it is loaded, and costs nothing once it is not.

Handle: the cheap default

The manager spools the body once at load and adds the resulting retrievable to each turn's turnRetrievables. The model sees an unguessable handle rather than the body. The handle is a small fixed context cost per dispatch; body text enters context only when the model deliberately calls a reader.

The detail that matters is that the body is spooled as a SpooledMarkdownArtifact, not an opaque text blob. Core does not hand the model a crude "fetch the file" primitive — it forges structure-aware navigation tools over the document:

  • artifact_md_headings — the heading tree, so the model inspects the shape of the skill before reading a word of it
  • artifact_md_sections — pull one named section, not the file
  • artifact_md_frontmatter — the metadata alone
  • artifact_md_code_blocks — just the examples
  • artifact_md_links, artifact_md_images, artifact_md_text, artifact_md_ast
  • plus the seven base readers every artifact gets: artifact_head, artifact_tail, artifact_grep, artifact_cat, artifact_byte_length, artifact_line_count, artifact_estimate_tokens

That is the difference between reading the manual into context and consulting the index to read one page. A 4,000-token skill can answer a question for the cost of a heading list and one section. Progressive disclosure is not a feature this battery adds — it falls out of spooling the body as markdown, and core forges the tools automatically.

Nothing forces the model to navigate well rather than reaching straight for artifact_cat. What the machinery guarantees is that it never has to pay for the whole document to find one answer.

Inline: budget, not permanence

inline sets the retrievable's inline flag. ADK renders the body in the trust-tier envelope on each dispatch. It costs the body size on every dispatch while loaded, but it still is not appended to conversation history. Unloading removes the retrievable, so the next assembled context contains neither the body nor its handle.

ts
import { createSkillManager } from '@nhtio/adk/batteries/skills'
import type { SkillDescriptor, SkillSource } from '@nhtio/adk/batteries/skills'

const descriptor = {
  id: 'reports', name: 'Reports', description: '...', version: '1.0.0', channel: 'inline',
} satisfies SkillDescriptor
const source = /* a SkillSource that returns descriptor */ null as never
const manager = await createSkillManager({
  sources: [source],
  defaultChannel: 'handle',
  gate: async () => undefined,
})
// reports is inline because its descriptor overrides the manager default.

We are about to state an opinion

Use handle. We are not neutral about this.

A plugin system that demands the whole plugin be resident in memory just to reach one function is an antipattern the development community abandoned decades ago. Dynamic linking, lazy loading, code splitting, demand paging — the entire industry agreed that you load the index and fetch the page, and it agreed a long time before anyone was writing agents.

inline is that dead pattern, revived on the theory that agentic systems are somehow different. They are not. A model reaching into a 4,000-token document for one section has exactly the problem a linker had in 1985, and it has the same solution.

So inline exists because your body might be forty words and a reader round-trip is genuinely not worth it, and because we will not pretend to know your deployment better than you do. That is the case for it. It is a narrow case. Reach for handle first, and reach for inline only when you can say out loud why paging is not worth it for this specific skill.

What unload reclaims

unload_skill removes the skill retrievable from the current context and unregisters tools carrying meta.skill. Since contexts are rebuilt per dispatch, later context assembly has no skill body. This is a design consequence, not a cleanup trick.

What unload reclaims is the instruction set — the body, the tools the skill exported, and reader results that read that body's artifact while it was loaded. What it deliberately leaves alone is work — findings, script output, and anything the agent produced. Backing bytes in the consumer's spool store are the consumer's to manage; the reader contract does not delete them. This is a three-way split: instruction set (reclaimed), work (durable), and backing bytes (consumer-managed).

This separation is the feature

An agent loads a skill, uses it, unloads it, and keeps every finding — then loads the next skill against a context that is not still carrying the previous one's manual. A deactivation that also destroyed the work would make deactivation useless: you could never unload anything you had actually used. Deactivating a plugin does not roll back the rows it wrote.

A reader forged before unload in the same iteration may still resolve and read its durable spool bytes. Core closes that window by pruning ephemeral readers and re-forging from the next iteration's current retrievables. The battery does not revoke an outstanding reader and does not claim that it fails loudly — and the bytes it reads were always meant to outlive the skill.

See Artifacts for output readers and Middleware for projection timing.