Skip to Content
B Berry Docs
Reference

Native authoring

The same maintained guide available through berry guide native.

Source reference · cd46e0e78d73Read Markdown

The following reference comes from the receiving implementation, including the same field inventories used by admission. Use it before writing or assessing native sources. It describes supported behavior; familiar syntax from another language, JSON Schema implementation or provider is not implicitly available.

Berry native source contract

Source: Markdown with strict YAML 1.2 between opening and closing --- lines, then a body. Use expanded mappings/sequences with two-space indentation. Keep empty mappings/sequences as {} and []; null is data, not an empty declaration. Quote YAML strings containing colon-space. Duplicate keys and unknown fields fail. Every source requires name and description. Names: 1-128 letters, digits, _, . or -; env. is reserved. Optional title is a nonempty callable label of at most 256 characters. These fields describe a technical routine or reusable instructions. Public agent identity, title and description belong to the bundle's plugin.json declarations. name is the full logical identity, including any explicit namespace, e.g. patterns.react. Use react.md as the filename and conventionally patterns/react.md as its location. Directories never assign names; moving Markdown files does not rename definitions. Use qualified names across namespaces and short names for siblings in the same namespace. References use declared names, not Markdown filenames. No namespace or using field exists. Exactly one source kind:

  • instructions: name, title, description, tools
  • model: name, title, visibility, description, input, output, model, configure, route, tools, permissions, compaction, port, next
  • code: name, title, visibility, description, input, output, language, tools, permissions, port, next, catalog Instructions have neither model nor language; their nonempty body is reusable text, not callable work. Model bodies are nonempty instructions. Code bodies are raw ECMAScript 2023 modules with language: javascript and a default function export.

Input/output schemas All schemas are objects. An independent JSON Schema document uses {document: JSON-text, type?: root-type}, with no native constraints alongside it. The wrapper's type must match the document's root type, including its absence; document references stay inside that schema. Classification uses the forms below. The native profile supports types: string, number, integer, boolean, null, array, object. Only these native keywords are accepted, recursively, for the declared type:

  • untyped: type, description, const, enum, oneOf
  • string: type, description, const, enum, oneOf
  • number: type, description, const, enum, oneOf, minimum, maximum
  • integer: type, description, const, enum, oneOf, minimum, maximum
  • boolean: type, description, const, enum, oneOf
  • null: type, description, const, enum, oneOf
  • array: type, description, const, enum, oneOf, items, minItems, maxItems
  • object: type, description, const, enum, oneOf, properties, required, additionalProperties, propertyNames, maxProperties An omitted input permits no argument or any JSON value; input: {} requires a JSON argument. A declared type validates exactly: no coercion, defaults or extra-field removal. Object properties are optional unless listed in required. additionalProperties is boolean or a schema for undeclared properties; omission permits extras. propertyNames validates every property name. maxProperties bounds the total number of object properties with a nonnegative safe integer. Array items is mandatory and is one schema; optional minItems/maxItems are inclusive nonnegative safe integer length bounds. Equal bounds require an exact item count. Integers must be safe integers. Numeric bounds are inclusive. enum is nonempty with distinct JSON values. oneOf requires exactly one matching branch. const can be any JSON value. Type-specific keywords require the explicit type. Schema nesting is limited to 64 levels. description is an annotation, not validation. Implement necessary normalization/defaults/extra constraints explicitly in a code routine; keep the original user requirement and test the observable boundary. For generation, declare known result constraints in output so native feedback can correct invalid results before returning them. For dynamic or semantic constraints outside this profile, compose explicit validation and bounded model correction before publishing effects; a downstream throw alone provides no repair.

Models Use either model.select or model.provider plus model.id, never both. select accepts only: operation, interaction, input_modalities, output_modalities, supported_parameters, tool_choice, context_length, session, funding. select.operation is required: generation, classification. select.interaction is optional: request, stream, session. For continuous audio, use select: {operation: generation, interaction: session, session: {trigger: voice_activity}, input_modalities: [audio], output_modalities: [audio]}. This profile requires both routine ports. Its original input is exactly {audio:{call:callId,milliseconds:duration}}. The call must be the immediate parent's own admitted audio use; a shared root or a service ID is insufficient. The duration is positive, in 10 ms increments, at most the catalog input limit. Instructions and tools still belong to this prompt. Tools may run concurrently with audio but remain ordinary bounded child calls. Provider cancellation signals only those exact calls, without rollback. The compiler requires granted runtime.call.read, runtime.call.send and runtime.call.cancel. The routine emits original session events and accepts {type:close}. It does not restore provider context after loss or spend without a bounded input allowance. A session's end may retain unknown unreported input cost. input_modalities/output_modalities/supported_parameters are arrays of catalog strings. tool_choice is an array of supported modes. context_length has exactly minimum: a positive safe integer (not a scalar). Generation tools and output already infer tool support and structured-output capability during binding. Do not write model.select.structured_output or model.select.tools. Descriptor fields such as structured_output, pricing and max_input_tokens are not settings. Model settings go directly alongside select or provider/id, not under settings. Only settings declared by that connection's settings schema are accepted; use its defaults, constraints, parameter ranges and allowed values. A runtime connection ID is a grant name, not model.id. Consult env.runtime.models; never invent IDs. Generation without output returns text. With output it returns that JSON value. Classification has no tools, input port, configure or compaction. A declared output supports const, enum, oneOf of exact const alternatives, boolean, a described number with minimum: 0 and maximum: 1, distributions, or closed objects of these with every property required. A distribution declares type: object, propertyNames with enum or oneOf of at least two distinct nonempty string constants, and additionalProperties: {type: number, minimum: 0, maximum: 1}, without properties or required. It returns every choice's probability; the caller owns any threshold. No free text, arbitrary arrays or optional fields. A constant choice may itself contain an object/array. Descriptions on choices specify criteria. Results are the declared values. Without output, supply {state, questions} for the current observations; named noul, choice or score questions return {answers, usage, metadata}. Classification preserves its complete input; an oversized indivisible review refuses. A generation summary is not a substitute for source-review evidence.

Calls and continuations Tools are an array of distinct target names or singleton mappings from a name to options (async, output, limits, presentation); async and output are enabled only by true. The accepted short form name: async means async:true; prefer expanded mappings. Plain names wait for the full called next chain. Async returns an admission receipt and later a correlated outcome. output:true forwards emitted output through the caller port; it requires port.output. limits: {calls, steps, concurrency, timeout} gives each invocation of that child an allowance of its own instead of a share of the caller's; the runtime bounds every value by the caller's limits and the timeout by the caller's remaining time, in milliseconds. A Tool name grants no additional authority by itself. On the routine's output Port, completed text answers are messages; prose beside Tools that run is progress. Requests that can choose Tools retain their stream until completion identifies its role; answer-only requests may stream directly. Explicit Tool publications, attachments and declared call presentations keep their own output connections, independent of model prose. presentation: {when: accepted|completed, value: expression} declares a Gen UI publication for that exact call. It requires the caller output port and an explicit runtime.ui.publish catalog grant. Async accepted calls publish without waiting for completion; completed requires a synchronous connection and its successful durable outcome. Rejected calls publish nothing. value uses the same Native value expressions over {input,call,output,catalogs}: input is original call input, call has id and nullable executionId, output is the completed result, catalogs contains the exact captured renderer's {id,version}. The value must contain exactly format, document, title and fallback. It declares ordinary catalog content, not status, actions or new work. Use an execution ReferenceChip only with call.executionId; Tools have no child execution. A completed opening Tool may return a thread reference in output. Missing fields refuse the presentation without repeating work. Publications replay once in the caller Port. next is a target name or an object with fields: to, select, cases, input, output. Use to, or select with nonempty cases, never both. cases maps string labels to names or null. Null ends that chain with the local output. Default successor input is the local output; an explicit next.input replaces it. next may target an ordinary external Tool declared in composition requirements. It need not also be in tools. next.output:true requires the caller output port. Loops/self-next are permitted within runtime budgets; each transition passes its explicit input value. Selections use singular absolute RFC 9535 JSONPath over {input,output}, such as $.output.decision or $.input.items[-1]. Missing values fail; null remains data. No wildcards, filters, slices, unions or descendants. next.input constructs objects and arrays recursively. Strings there are paths; literal strings use {$literal: value}. A nested {select: path, cases: mapping} selects an expression by an exact string label. There is no default case, executable expression, template interpolation or next.with.

References and environment Native source references share one name resolver. A qualified name resolves exactly. A short name first selects a sibling in the caller's declared namespace, then an exact source or import alias, then a unique source or import name with that final segment. Only if none matches may it select an external name. Missing or ambiguous matches fail. Prefer qualified references across namespaces so adding another source cannot make a short reference ambiguous. These rules cover content references, instruction inclusion, tools, next, configure.routine, route.routine and compaction.summary. entry and exports have no caller namespace. !ref source_name is a YAML tag capturing an exact address inside schema const/enum or next.input. A literal {$ref: name} is ordinary data, not this language operation. Double-brace source names include instructions at build time; double-square source names retain a readable dependency address. load reads a granted retained address; loading never invokes a routine or widens permissions. env.* placeholders are rendered at preparation, separately from compiler references. Editing a source does not alter accepted preparations.

Runtime instruction templates Write {{ $.input.goal }} in a model instruction body to select invocation data. The expression is the same singular RFC 9535 JSONPath profile as next: names and indices only, including quoted names and negative indices. Double braces are Berry syntax, not a claim of Mustache/Jinja compatibility. Leave blank lines around block substitutions so inserted text remains separate from instructions. Available roots:

  • $.input: this active invocation/next position's argument, exactly as validated.
  • $.parent.input: the immediate caller's active input, frozen at child admission. Repeat parent for ancestors. A root invocation has no parent. A next transition does not add a caller level. Parents expose input and calls, not private Step state.
  • $.parent.calls[0].output: a successfully completed call's exact output at that caller snapshot. calls uses recorded order within the active next position. Calls also expose id, name, status, input and terminal outcome. There is no lookup by Tool name; names can repeat. Pending/failed calls have no output.
  • $.calls[0].output: own earlier recorded calls at the preparation read. This includes internal model/read calls; it is not an index of only offered Tools. The current reading call and later admissions are excluded. Prefer explicit arguments or a named State record when internal call positions are unsuitable.
  • $.session: this call's session identity, current thread, opening and threads, through runtime.session.read. It cannot select another session.
  • $.session.state.plan.value and $.session.state.plan.version: a versioned session State key. Use bracket notation for keys containing punctuation. The reader's existing session authority applies; a path cannot choose another session or namespace. Store related values under one key when they need one consistent version; separate keys are separate reads. Missing keys fail; stored null is valid data. Only referenced values enter instructions. Strings remain exact text; other JSON values use canonical JSON, without description-based rewriting or HTML escaping. Substitution is single-pass: inserted text cannot expand more tags, source includes or references. Escape an authored example with a backslash before its opening braces. Backslash pairs before a marker produce literal backslashes; escape each opening marker in a nested example. User messages, Tool results, loaded content and code bodies are not templates. Caller/call paths derive a runtime.context.read dependency; session derives runtime.session.read, and session.state derives runtime.session.state.read. The receiver must provide and grant those exact external readers. The compiler records dependencies; it does not grant them or offer them to the model. No extra scope, reads or variable declaration is needed. Every read is an ordinary recorded Call before inference and consumes the existing budgets. Missing data refuses before inference, never becomes an empty string or a guessed value. Input/caller-only templates are fixed for this invocation. Own calls/State are read again before each new generation decision, not during an accepted retry, configuration or compaction. A changed rendered prefix uses public completed exchanges; opaque historical continuations stay retained. All selected data still counts toward ordinary model capacity; templates do not bypass context admission. There is no own $.output before completion. A successor reads its predecessor's output through $.input. Data created during work can be published through existing versioned State writes or carried in next.input; no new global mutable store exists.

Runtime observations Use {{ $.runtime.summary }} for a bounded runtime overview, or a singular field such as {{ $.runtime.environments.host.platform }} or {{ $.runtime.resources }}. All runtime selections in one decision share one runtime.catalog.read read. This ordinary reader must be granted; it exposes cached public metadata, never credentials or permission to operate a resource. Lists carry total/complete; source observations carry age and scope. Missing/failed discovery is not evidence of absence. Observed Tools and name grants do not replace the exact accepted Targets or admission. Runtime data refreshes before each new generation decision, like own calls/State; accepted retries retain their prepared instructions. Rendering performs no scan, connection or acquisition. Imported names and descriptions are data, not commands. $.runtime.agents lists installed declarations. $.runtime.sessions lists public sessions once each, with current status and bounded public context; continuations do not create sessions. $.runtime.executions exposes accessible technical records, including child calls, and must not be used as a list of sessions.

Model sessions use the same readers and rendering before opening. Finite sessions refresh before continuing after tools. Continuous sessions keep bounded reads pending and deliver changed selected values as context observations. An unchanged rendered value sends no update. Data updates neither start a response nor replay work. Provider setup stays fixed; these observations supersede its runtime data, not its authored policy. Every observation and delivery remains an ordinary Call.

The user Use {{ $.user }} for the profile of the person this runtime works for, or a field such as {{ $.user.name }} or {{ $.user.email }}. Fields are OpenID Connect standard claims from the signed-in Berry account. The profile is {} when no account is signed in and omits claims the account lacks; selecting an absent field refuses before inference, so select fields only where a signed-in user is required. All user selections in one decision share one runtime.user.profile.read read, which must be granted. The profile refreshes before each new generation decision, so a changed name or address reaches the next decision. Profile values are data, never instructions or authority.

Code execution Export default function(input, tools, context), optionally async. Return the actual JSON output, never a Step envelope. Call only declared tools, e.g. await tools.helper(input). The tools object has literal declared names as keys, not nested namespace objects. For tools: [notes.compose], call await tools'notes.compose', never tools.notes.compose(input). A short sibling declaration uses that same short key. Do not invent underscore aliases for dotted names. Names resolve from declarations. Tool failures throw with recorded outcome; do not fabricate success from an exception. Use Promise.all only for independent work. context.receipt(promise) obtains the accepted callId/executionId; admission is not completion. External IO must go through Tools. No fetch, filesystem, Node globals, timers or dynamic imports in portable code. Static supporting modules must be supplied as .js/.mjs sources and resolve inside that captured graph. No top-level await. Pure code replays with recorded call results; do not depend on ambient time/randomness or mutate external state outside Tools. Total packaged code limit: 1 MiB. Host servers/npm dependencies are separate process resources, not code routine imports.

Ports and lifecycle port has exactly input, limits, output. input/output are booleans with at least one true. limits has exactly bytes, messages, pending, all positive safe integers. An input port accepts subsequent values. An output port publishes values. Neither changes the routine input schema or adds application behavior. With both ports, a model retains context and waits between published answers. With only one port or no ports, it completes with an Outcome. A code resident uses await context.receive() and context.emit(value); no busy loop. context.emit(value, kind, answers) names the received message's ID an answer replies to when the resident has taken others since; otherwise it answers the latest taken. Code with an input port can continue after an explicit stop and fresh input. Native replays its original accepted calls and outcomes without dispatching them again. Catch a cancelled tool outcome when the composition should return to receive(). Do not retry uncertain external effects merely because new input arrived. A missing original outcome refuses continuation. Unconsumed input at stop stays in History; it is not delivered again as a fresh message. Prior results confer no new grants. Messages carry actual JSON; structured input does not become a JSON string. Native model content is {parts:[...]}, with only that nonempty array in the envelope. Parts are {text:string}, {data:JSON,mediaType?:MIME}, {mediaType:MIME,raw:base64} or {mediaType:MIME,url:HTTPS}; raw/url parts may include filename. Retained files use {mediaType:MIME,file:Reference,bytes:nonnegative-safe-integer,filename:nonempty-string}. Every part may include object metadata. Data parts are read as JSON text. raw is canonical base64, never a data URL; url permits no credentials. The selected model must support each actual modality. A retained file reference preserves identity; it grants no access and alone proves no model observation. Generation has default runtime context management. Optional compaction fields: max_tokens, keep_turns, summary; max_tokens is positive and keep_turns nonnegative, both required. Optional summary names a plain generation routine without tools, ports, input/output schema or another compaction policy. Do not add compaction to classification. configure has routine and a distinct nonempty fields array; it is optional generation policy, not public invocation arguments. Load the model composition guide before authoring it. Configuration cannot alter authority, connections, ports, contracts or deadlines. A conversation's optional route names its routine. Before a turn, each batch of the person's messages goes to routine, called with {messages}, each {id, text, content}, under the conversation's authority; it reads the session's threads and the exchanges itself. Its output {route: "thread", thread, title, request} says another thread took them and answers the person there; any other output, such as {route: "here", notice}, leaves them to the conversation, notice telling the model why.

Build, bind, run and install Write plugin.json and ordinary source files, then call runtime.package.build with its workspace manifest path and optional file version. Source discovery starts in the manifest directory, or its extension's sources directory when specified. Use the Agent Plugins 1.0.0 JSON manifest: $schema is https://agent-plugins.org/schemas/1.0.0/plugin.schema.json; name uses lowercase letters, digits, hyphens and periods; version is "1". Put Berry declarations in extensions["ai.strawberry.berry"]. Its agents map names public agents with title, description and entry (a local callable export). One plugin may supply multiple agents, reusable exports and a runtime. Libraries simply omit agents. The same extension owns requires, exports, imports, resources and interfaces. Each agent may declare welcome, icons and presentation. Routine frontmatter describes the technical call contract; the package describes the public product. Runtime settings belong in runtime.yaml, not plugin.json. Markdown and captured JS modules are sources; HTML/CSS/server code/media are resources. Resource URI and source path are different identities. Capture every authored runtime file. Requirements are derived from source, never a second list. Imports select an exact source manifest, bundle or retained root, with an optional public export. Loading never downloads or executes dependencies.

Workflow Tool authority comes from the user's preparation grants. Permitted Tools are callable only when the routine declares them in tools. Omitted tools and tools: [] offer none, including when preparation grants other Tools. Without an explicit permissions selection or catalog access, a routine retains only the authority required by its declared dependencies, never ambient Tool grants. Code declaring catalog: true explicitly discovers Tools within its grants. Routine permissions: [exact_tool_id, ...] selects optional external Tools for that routine and all work it delegates; [] removes optional Tool access. Permissions never add a Tool schema or call name to a routine. permissions: catalogs selects installed ToolCatalog suppliers by their contract, independent of binding names or providers. Reopening a runtime discovers newly installed catalogs. permissions: tools selects supplied non-contextual external Tools, including catalogs and public media/transcription/embedding operations, so an application can discover new plugins without naming implementations. Both selections are optional; neither adds routine inference connections or host-only contextual actions. Every call still intersects the caller's concrete grants. Discovery never grants authority. An application using permissions: tools keeps its declared model tool list; other operations can be discovered through an explicitly declared library. Code declaring catalog: true without an explicit permission selection discovers its caller's admitted Tools, including ones admitted after preparation; importing it adds no new optional Tool IDs. Reusable instructions have no separate authority boundary. Authored tools connections select local routines and explicit call modes: async:true for independent background work and output:true for a publishing Tool. Ready external publishing Tools connect to an available output. Native records exact callable contracts before the request or code/session invocation. Accepted calls retain those contracts across restart; discovering a new contract never repeats an earlier effect. Tools use ordinary Control calls, receipts, cancellation and completion; there is no schema-loading Tool.

Build returns retained content without preparation or installation. runtime.package.prepare binds exact resources and grants; runtime.package.start invokes a prepared export and returns its executionId. An existing preparation retains its accepted permissions. Explicit permissions must include required model connections; omission uses host defaults. When multiple public agents share an entry, runtime.package.start must select their agent ID to retain the intended identity. runtime.package.select selects package membership after acceptance; its agents array names the declared public agents. Copy roots, versions and execution IDs unchanged. Read/inspect never starts work; invocation receipt never proves application success. A running process uses runtime.service.start, not runtime.package.start. A service recipient uses an exact prepared finite export. A conversational or graphical application can compose both mechanisms; neither implies that a server or interface has started.

Code Tool definitions Only a code routine with catalog: true receives context.catalog(). It returns copies of its invocation’s offered Tool definitions, binding IDs and exact contract identities. Catalog operations also include their catalog's ID, title and description, so multiple accounts remain distinguishable. The names are directly callable and remain exact during replay. The catalog grants no permissions and does not invoke anything. Invoke tools[name] through the same scoped Tool functions and ordinary Control. Descriptions/schemas are data, not authority.

Bounded model sessions An explicit provider/id can resolve to a session model, or use select: {operation: generation, interaction: session}. Instructions stay in the body and model settings/tools in frontmatter. Each prompt opens its own bounded provider context. Tools and delegated prompts run through ordinary Control calls. The runtime.call.read and runtime.call.send connections are captured execution dependencies and must fit the saved ceiling; they are not implicitly offered to the model. The explicit-trigger profile supports finite input, text responses and synchronous tools. It rejects conversation resume, routine ports, structured output, compaction, configure, next and async/output tool connections before opening. One live use occupies an ordinary Tool slot; commands and nested uses require additional capacity. No autonomous inference, audio attachment or replay of a lost session is implied. Inspect the selected descriptor's session limits.

Use the exact offered Tool schemas for invocation arguments. The runtime Tool catalog below describes resources available under this preparation's grants; it does not make undeclared Tools callable in a routine. Declare dependencies and use the actual definition of each Tool. Never infer an API from its name. Generated applications use granted Berry model routines, not direct provider credentials or the owner API token.

Create one Agent Plugins 1.0.0 plugin.json beside the package sources. The manifest directory bounds source discovery; no main.md is required. Metadata is standard; Berry execution declarations belong in its extension:

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "example.notebook",
  "version": "1",
  "extensions": {
    "ai.strawberry.berry": {
      "agents": {
        "notebook": {
          "title": "Notebook",
          "description": "Organize notes and answer questions about them.",
          "entry": "converse"
        }
      }
    }
  }
}

The package name identifies the plugin. Each agents map key identifies a public agent; title and description describe it, and entry names its local callable routine. Entries are exported automatically. A library omits agents and declares exports. The same extension can declare a native runtime. Installation authority, user settings and runtime limits never belong in the source manifest.

Optional fields in extensions["ai.strawberry.berry"]:

  • sources: directory containing the routines, relative to the manifest. Omit to use the manifest directory. A runtime and its agents share this one manifest.
  • imports: a list of {name, manifest, export}, {name, bundle, export} or {name, root, export}. Supply exactly one of manifest (another source manifest), bundle (a bundle file), or root (exact retained content/prepared identity). Omit export to capture opaque content such as a directory snapshot; use !ref with its declared name. With export, name aliases that exact public definition. Source imports compile recursively; cycles are refused and nothing is executed.
  • resources: {uri, mimeType, path} records capturing exact file bytes. Paths are relative to the manifest. URI labels the retained resource; it does not fetch it.
  • interfaces: existing InterfaceOffer records. Follow the application guide and actual interface contract; do not duplicate Conversation UI or service views.

Call runtime.package.build with {manifest: "app/plugin.json"} after writing the files. Optional version checks the manifest file version. There are no inline sources, routines, requirements, models or permissions arguments. Tool requirements are derived against the receiving build catalog. Unknown names fail compilation; neither an import nor successful compilation grants execution permissions. The same manifest is used by CLI build and by host assembly installations. Launch processes and connect ports in ordinary routines; there are no manifest installation hooks, service startup lists or a second workflow language.

Write model-facing contracts as operational instructions. State the objective, the meaning of each input, the evidence needed for a decision, the exact output form and the next step when evidence is missing or an operation fails. For a classifier, describe positive signals and the boundary with neighboring choices; do not rely on short labels to convey completion or failure semantics. Separate missing observations, observed defects, stale revisions and unavailable resources.

Use headings or short field lists when they make several responsibilities easier to follow. Bind supported env.* placeholders to facts supplied by the receiving runtime; never invent environment keys or hard-code another host's paths and model limits. Code-generated feedback can interpolate facts it actually holds: the operation, phase, exact identity, expected constraint, observed status, limit and continuation cursor. Keep structured evidence beside that explanation. Do not print credentials or entire unrelated payloads into diagnostic prose.

Use runtime JSONPath templates when a child needs exact shared caller data that must not be rewritten into generated arguments. Select only the needed fields, label them as task data and separate each block from surrounding instructions with blank lines. For example, surround $.parent.input.goal with double braces in the child body. New data can travel through next.input or existing versioned session State. A reader dependency still requires a grant. This does not compress data, make absent fields optional or allow skipping complete acceptance evidence.

Describe whether a call was refused before dispatch, accepted and pending, completed, or left with an uncertain effect. A failed observer cannot undo an earlier action. Give the smallest useful repair while preserving original input, candidate bytes and completed work. Additional words should resolve ambiguity; avoid repeating whole catalogs or evidence already present in the request. Evaluate contrasting cases and recovery behavior on the selected model; more detailed wording alone is not proof of more reliable decisions.

Keep successive decisions distinct. Deciding what an object is does not decide whether it is complete or valid. When the goal sends incomplete members of a category to review, an upstream classifier must still admit those members to that validation path. Do not make a downstream required field a prerequisite for recognizing the category: that makes its missing-field review unreachable. State this distinction in both branch descriptions and instructions. Exercise the complete public path with one otherwise valid input missing each required field, as well as a true non-member; do not test the validator only in isolation.

Use the selected model's declared settings defaults unless the task needs a measured override. A short final JSON value does not imply a small generation allowance: reasoning and visible output can share max_output_tokens, and omitting effort retains the catalog's effort. A setting the selected model does not declare, such as effort with a model that has no effort scale, is not used with it. Test the pair on representative inputs before reducing that allowance. Reaching the output limit is an incomplete response, never a successful partial extraction; keep its evidence and repair the routine's settings in a new preparation. Do not drop fields, accept partial JSON or replay earlier effects to make a capped response pass.

Avoid the mistakes that most often fail a first build:

  • Frontmatter is YAML. The null type is the quoted string: write type: 'null'. An unquoted null is the null value and is refused.
  • A plain value that contains a colon followed by a space is read as a mapping. Quote the whole value or write it as a block scalar after | or >.
  • Model settings such as max_output_tokens and effort belong inside model, beside select, never at the top level of the frontmatter. Schema keywords such as additionalProperties, required and properties belong inside input or output; at the top level they are unknown fields and fail the build.
  • A source without model and without language is non-callable instructions and accepts no input, output or next.
  • A runtime template path inside double-brace tags, such as $.input.correction, must resolve on every invocation; a missing value fails the position before inference. Make such a field required, or have the calling code always pass it.
  • A classification output holds judgments only: each property is an enum, a const, a oneOf of constants, a boolean or a described probability from 0 to 1, inside a closed required object. A free string such as a title belongs to a generation routine; preparation refuses a classifier that declares one.

In a code routine a failed Tool call rejects and is recorded as a failed call that the user sees in Activity. Do not probe with a call you expect to fail. To learn whether a file exists, list its directory. Read a file only when the listing shows it, and replace it with the version that read returned.

Read checked examples with: berry guide examples

Read model composition guidance with: berry guide models

For independent declared calls, this checked join retains both results in their original positions. A failed branch does not cancel accepted siblings; they are observed before the caller finishes. Serialize conflicting writes instead.

export default async function(input, tools) {
  const [impact, action] = await Promise.all([
    tools.impact(input),
    tools.action(input),
  ]);
  return {impact, action};
}