# Bundle format

What a `.bylm.json` file holds: every field the app exports and the marketplace accepts,
with its type and limits. The same rules are published as a [JSON Schema](/schema/bundle/v1.json).
A field is named by its path: dots between fields, `[]` after a list.

## Contents

- [Fields](#fields)
  - [`format`](#format)
  - [`formatVersion`](#formatversion)
  - [`exportedBy`](#exportedby)
  - [`namespaceId`](#namespaceid)
  - [`secrets`](#secrets)
  - [`constants`](#constants)
  - [`providers`](#providers)
  - [`tools`](#tools)
  - [`agents`](#agents)
- [Shared parts](#shared-parts)
  - [`Origin`](#origin)
  - [`SecretBindings`](#secretbindings)
  - [`ConstantBindings`](#constantbindings)
  - [`Endpoint`](#endpoint)
  - [`ModelSettings`](#modelsettings)
  - [`WeightsFile`](#weightsfile)

## Fields

### `format`

string · required

Marks the file as a Bring Your LM bundle, so a reader tells it from any other JSON before reading further.

- always `"bylm-bundle"`

### `formatVersion`

string · required

The version of the format the file is written in. An app refuses a major version it does not read; a later minor version may add fields an older app does not know.

- length at most 16 characters
- pattern `^1\.[0-9]+$`

### `exportedBy`

object or null

The app build that wrote the file. Absent from a bundle the marketplace puts together for a download.

### `exportedBy.build`

integer or null

The build number. Absent from a build that was not numbered, such as a local one.

- range 0 to 1000000000

### `exportedBy.version`

string · required

The app's version.

- length at most 64 characters

### `namespaceId`

string · required

Whose ids the items' ids are: the exporting device's, or the publishing marketplace account's. With an item's id, it names one entity anywhere.

- length 36 characters
- pattern `^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`

### `secrets`

array of objects

The secrets the file's providers and tools need, each declared by a label and never by its value. An item names the secret each of its slots uses.

- size at most 64 items

### `secrets[].id`

string · required

The secret's id within the file, which an item's slot names.

- length 1 to 128 characters

### `secrets[].config`

object · required

The secret as the importer is asked for it.

### `secrets[].config.name`

string · required

What the secret is called, and what a person is asked for by.

- length 1 to 80 characters

### `secrets[].config.kind`

string

What the secret is.

- one of `"apiKey"` `"other"`
- default `"apiKey"`

### `secrets[].config.hint`

string or null

Where or how to get one, shown beside the name.

- length 1 to 1000 characters

### `secrets[].config.helpUrl`

string or null

A page where one is made, opened in a browser and never requested by the app.

- length 1 to 2048 characters

### `constants`

array of objects

The constants the file's providers and tools place, each declared by its name and, when the exporter shared it, its text. An item names the constant each of its slots places.

- size at most 64 items

### `constants[].id`

string · required

The constant's id within the file, which an item's slot names.

- length 1 to 128 characters

### `constants[].name`

string · required

What the constant is called, and what the importer is asked for by.

- length 1 to 80 characters

### `constants[].hint`

string or null

Where to find it, shown beside the name.

- length 1 to 1000 characters

### `constants[].helpUrl`

string or null

A page that shows it, opened in a browser and never requested by the app.

- length 1 to 2048 characters

### `constants[].value`

string or null

The text, when the exporter shared it. The importer keeps it or types their own.

- length 1 to 1024 characters

### `providers`

array of objects

The providers in the file: the servers models are reached at, and how. A provider that runs on the device does not travel; a model that runs there is named by its files.

- size at most 64 items
- default `[]`

### `providers[].id`

string · required

The provider's id on the device that exported it. A model's settings name the provider by it.

- length 36 characters
- pattern `^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`

### `providers[].origin`

object or null

Where the provider was imported from, when it was imported rather than set up on the exporting device.

Fields as in [`Origin`](#origin), under Shared parts.

### `providers[].secrets`

object

The secret each of the provider's slots uses, by the id it is declared under. A provider that sends a key has the slot `apiKey`.

Fields as in [`SecretBindings`](#secretbindings), under Shared parts.

### `providers[].constants`

object

The constant each {constants.\<slot>} in the provider's address or headers places, by the id it is declared under.

Fields as in [`ConstantBindings`](#constantbindings), under Shared parts.

### `providers[].config`

object · required

The provider's settings.

### `providers[].config.name`

string · required

The provider's name, as the app shows it.

- length 1 to 80 characters

### `providers[].config.connection`

object · required

The API the provider speaks, and where it is.

One of 6 kinds, told apart by `type`.

### `providers[].config.connection.openAiChatCompletions` — `type: "openAiChatCompletions"`

Any server that speaks the OpenAI chat completions API.

### `providers[].config.connection.openAiChatCompletions.endpoint`

object · required

The server requests go to, and how they authenticate.

Fields as in [`Endpoint`](#endpoint), under Shared parts.

### `providers[].config.connection.openRouterChatCompletions` — `type: "openRouterChatCompletions"`

OpenRouter's Chat Completions API, one account that reaches many upstream models.

### `providers[].config.connection.openRouterChatCompletions.endpoint`

object · required

The server requests go to, and how they authenticate.

Fields as in [`Endpoint`](#endpoint), under Shared parts.

### `providers[].config.connection.claudeChatCompletions` — `type: "claudeChatCompletions"`

Claude models over Anthropic's OpenAI-compatible Chat Completions API.

### `providers[].config.connection.claudeChatCompletions.endpoint`

object · required

The server requests go to, and how they authenticate.

Fields as in [`Endpoint`](#endpoint), under Shared parts.

### `providers[].config.connection.ollama` — `type: "ollama"`

An Ollama server on your machine or your network.

### `providers[].config.connection.ollama.endpoint`

object · required

The server requests go to, and how they authenticate.

Fields as in [`Endpoint`](#endpoint), under Shared parts.

### `providers[].config.connection.llamaServer` — `type: "llamaServer"`

A llama.cpp server you host.

### `providers[].config.connection.llamaServer.endpoint`

object · required

The server requests go to, and how they authenticate.

Fields as in [`Endpoint`](#endpoint), under Shared parts.

### `providers[].config.connection.llamaServer.fetchModelProps`

boolean

Asks the server to describe each model it serves, so the app knows each model's context length and capabilities. Slow: the server loads every model in turn to answer.

- default `false`

### `providers[].config.connection.llamaLib` — `type: "llamaLib"`

The llama.cpp engine built into the app, running a model file on the device, with no server.

No other fields.

### `tools`

array of objects

The tools in the file that a user set up, which its agents may attach.

- size at most 64 items
- default `[]`

### `tools[].id`

string · required

The tool's id on the device that exported it.

- length 36 characters
- pattern `^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`

### `tools[].origin`

object or null

Where the tool was imported from, when it was imported rather than set up on the exporting device.

Fields as in [`Origin`](#origin), under Shared parts.

### `tools[].secrets`

object

The secret each of the tool's slots uses, by the id it is declared under. A fetch tool whose request references `{apiKey}` has the slot `apiKey`.

Fields as in [`SecretBindings`](#secretbindings), under Shared parts.

### `tools[].constants`

object

The constant each {constants.\<slot>} in the tool's request places, by the id it is declared under.

Fields as in [`ConstantBindings`](#constantbindings), under Shared parts.

### `tools[].config`

object · required

The tool's settings.

One of 3 kinds, told apart by `type`.

### `tools[].config.fetchUrl` — `type: "fetchUrl"`

One HTTP endpoint the model calls, and what is done with the response before the model reads it.

### `tools[].config.fetchUrl.name`

string · required

The fetch tool's name. The model sees the tool named with it after `fetch_url-`, and an agent attaches the tool by it.

- length 1 to 54 characters
- pattern `^(?:[a-zA-Z0-9_-]+)$`

### `tools[].config.fetchUrl.description`

string

What the model reads to know what the tool does and when to call it.

- length at most 4000 characters
- default `""`

### `tools[].config.fetchUrl.url`

object · required

The address requested, an RFC 6570 URI template whose placeholders are filled from the call's arguments.

One of 1 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.url.uriTemplate` — `syntax: "uriTemplate"`

An RFC 6570 URI template.

### `tools[].config.fetchUrl.url.uriTemplate.template`

string · required

The template.

- length 1 to 2048 characters

### `tools[].config.fetchUrl.method`

string

The HTTP method of the request.

- one of `"GET"` `"HEAD"` `"POST"` `"PUT"` `"PATCH"` `"DELETE"`
- default `"GET"`

### `tools[].config.fetchUrl.body`

object or null

The request's body. Absent sends none.

One of 3 kinds, told apart by `contentType`.

### `tools[].config.fetchUrl.body.application/json` — `contentType: "application/json"`

A JSON body.

### `tools[].config.fetchUrl.body.application/json.content`

object · required

The template the body is written from, in its syntax.

One of 2 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.body.application/json.content.placeholders` — `syntax: "placeholders"`

JSON with `{name}` and `{+name}` placeholders.

### `tools[].config.fetchUrl.body.application/json.content.placeholders.template`

string · required

The template.

- length at most 16000 characters

### `tools[].config.fetchUrl.body.application/json.content.minijinja` — `syntax: "minijinja"`

A MiniJinja template, in Jinja's syntax, whose output must be JSON. It reads each call argument by its name, a linked secret as `secrets.<slot>`, a linked constant as `constants.<slot>` and the request's metadata as `meta`. Write a value with `tojson`, which quotes and escapes it. An argument the call left out is undefined: `tojson` writes it as `null`, and printing it any other way fails the call. Test it with `is defined` or give it a `default`.

### `tools[].config.fetchUrl.body.application/json.content.minijinja.template`

string · required

The template.

- length at most 16000 characters

### `tools[].config.fetchUrl.body.application/x-www-form-urlencoded` — `contentType: "application/x-www-form-urlencoded"`

A form-encoded body.

### `tools[].config.fetchUrl.body.application/x-www-form-urlencoded.content`

object · required

The template the body is written from, in its syntax.

One of 1 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.body.application/x-www-form-urlencoded.content.uriTemplate` — `syntax: "uriTemplate"`

An RFC 6570 URI template, such as `q={q}`.

### `tools[].config.fetchUrl.body.application/x-www-form-urlencoded.content.uriTemplate.template`

string · required

The template.

- length at most 16000 characters

### `tools[].config.fetchUrl.body.text/plain` — `contentType: "text/plain"`

A plain-text body.

### `tools[].config.fetchUrl.body.text/plain.content`

object · required

The template the body is written from, in its syntax.

One of 1 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.body.text/plain.content.placeholders` — `syntax: "placeholders"`

Text with `{name}` and `{+name}` placeholders.

### `tools[].config.fetchUrl.body.text/plain.content.placeholders.template`

string · required

The template.

- length at most 16000 characters

### `tools[].config.fetchUrl.variables`

array of objects

The call's arguments, each declared with a type and a description. A placeholder whose argument is not declared takes an optional string.

- size at most 32 items
- default `[]`

### `tools[].config.fetchUrl.variables[].name`

string · required

The argument's name, as written inside its placeholder's braces.

- length 1 to 64 characters

### `tools[].config.fetchUrl.variables[].type`

string

The JSON type of the argument.

- one of `"string"` `"integer"` `"number"` `"boolean"`
- default `"string"`

### `tools[].config.fetchUrl.variables[].array`

boolean

The argument is a list of values of its type.

- default `false`

### `tools[].config.fetchUrl.variables[].required`

boolean

The model has to pass the argument.

- default `false`

### `tools[].config.fetchUrl.variables[].description`

string

What the model reads to know what to pass.

- length at most 1000 characters
- default `""`

### `tools[].config.fetchUrl.variables[].default`

boolean, integer, number or string or null

What an argument the model leaves out stands for, as a value of the argument's type. A list or a required argument takes none.

Either:

- boolean
- integer
- number
- string; length 1 to 1000 characters

### `tools[].config.fetchUrl.headers`

array of objects

Headers sent with every request, in the order written.

- size at most 32 items
- default `[]`

### `tools[].config.fetchUrl.headers[].name`

string · required

The header's name.

- length 1 to 128 characters

### `tools[].config.fetchUrl.headers[].value`

object

The header's value. Empty sends no header of that name, not even one the tool would add itself.

- default `{"syntax":"placeholders","template":""}`

One of 1 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.headers[].value.placeholders` — `syntax: "placeholders"`

Text with `{name}` and `{+name}` placeholders.

### `tools[].config.fetchUrl.headers[].value.placeholders.template`

string · required

The template.

- length at most 1024 characters

### `tools[].config.fetchUrl.apiKeyOptional`

boolean

The endpoint answers without the API key as well as with it. Without a key stored, each header whose value references `{apiKey}` is left off the request. Only a key every reference to which is in a header value can be optional.

### `tools[].config.fetchUrl.responseType`

object or null

The content types a response may carry, which make the request's `Accept` header; a response of any other type is an error. Absent accepts any.

### `tools[].config.fetchUrl.responseType.acceptedTypes`

array of strings

The content types accepted, such as `application/json`. Empty accepts any.

- size at most 16 items
- default `[]`
- item length 1 to 128 characters

### `tools[].config.fetchUrl.errorHandling`

object or null

Which responses are errors, and what the model reads for one: an error skips the output filters and reaches the model marked as failed. Absent marks no response as failed.

### `tools[].config.fetchUrl.errorHandling.statuses`

object

Which response statuses are errors.

- default `{"type":"clientAndServer"}`

One of 3 kinds, told apart by `type`.

### `tools[].config.fetchUrl.errorHandling.statuses.clientAndServer` — `type: "clientAndServer"`

Every status from 400 to 599.

No other fields.

### `tools[].config.fetchUrl.errorHandling.statuses.serverOnly` — `type: "serverOnly"`

Every status from 500 to 599.

No other fields.

### `tools[].config.fetchUrl.errorHandling.statuses.custom` — `type: "custom"`

The statuses the listed ranges cover.

### `tools[].config.fetchUrl.errorHandling.statuses.custom.ranges`

array of objects · required

The ranges of statuses that are errors, each from one status to another, both included.

- size at most 16 items

### `tools[].config.fetchUrl.errorHandling.statuses.custom.ranges[].from`

integer · required

The first status of the range.

- range 100 to 599

### `tools[].config.fetchUrl.errorHandling.statuses.custom.ranges[].to`

integer · required

The last status of the range.

- range 100 to 599

### `tools[].config.fetchUrl.errorHandling.emptyIsError`

boolean

A success the output filters render to nothing is an error too.

- default `false`

### `tools[].config.fetchUrl.errorHandling.result`

object

What the model reads in place of the error's body.

- default `{"type":"body","prependStatusLine":true}`

One of 2 kinds, told apart by `type`.

### `tools[].config.fetchUrl.errorHandling.result.body` — `type: "body"`

The body as it came, with the status line before it when chosen.

### `tools[].config.fetchUrl.errorHandling.result.body.prependStatusLine`

boolean · required

Put the status line before the body, so an empty body still says what went wrong. The two read as text; without the line the body keeps its own content type.

### `tools[].config.fetchUrl.errorHandling.result.template` — `type: "template"`

A template rendered over the error. One that fails to render reads as the status line and the body.

### `tools[].config.fetchUrl.errorHandling.result.template.template`

object · required

The template, in its syntax.

One of 2 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.errorHandling.result.template.template.mustache` — `syntax: "mustache"`

A Mustache template over the error.

### `tools[].config.fetchUrl.errorHandling.result.template.template.mustache.template`

string · required

The template.

- length 1 to 8000 characters

### `tools[].config.fetchUrl.errorHandling.result.template.template.minijinja` — `syntax: "minijinja"`

A MiniJinja template, in Jinja's syntax. It reads the status code as `status`, its reason phrase as `reason`, the body as text as `body`, and the body parsed as `json`, which is undefined when the body is not JSON. A value it reads that is missing fails the render, which reads as the status line and the body: test with `is defined` or give a `default`.

### `tools[].config.fetchUrl.errorHandling.result.template.template.minijinja.template`

string · required

The template.

- length 1 to 8000 characters

### `tools[].config.fetchUrl.retry`

object or null

Retries a rate-limited request rather than answering with the error. Absent retries nothing.

### `tools[].config.fetchUrl.retry.on503`

boolean

Retries a 503 as well as a 429.

- default `false`

### `tools[].config.fetchUrl.retry.maxAttempts`

integer

How many times the request is sent at most, the first time included.

- range 1 to 20
- default `3`

### `tools[].config.fetchUrl.retry.baseBackoffMs`

integer

The first wait, in milliseconds, when the server sends no `Retry-After`. Each wait after it is twice as long.

- range 0 to 600000
- default `1000`

### `tools[].config.fetchUrl.retry.maxDelayMs`

integer

The longest any one wait may be, in milliseconds.

- range 0 to 3600000
- default `60000`

### `tools[].config.fetchUrl.capture`

object or null

Records each exchange for the response details screen; the model never reads the record. Absent records nothing.

### `tools[].config.fetchUrl.capture.maxChars`

integer

How many characters of each block are kept: the body and each filter's output are cut separately.

- range 0 to 1048576
- default `65536`

### `tools[].config.fetchUrl.cost`

object or null

Reads what a call cost from its response, so it joins the conversation's money totals. Absent reads no cost.

### `tools[].config.fetchUrl.cost.expression`

object · required

What reads the cost from the response, in the language its kind names.

One of 1 kinds, told apart by `type`.

### `tools[].config.fetchUrl.cost.expression.jq` — `type: "jq"`

A jq program over the response body. It reads the status code as `$status`, the headers by lower-cased name as `$headers`, the body as text as `$bodyText` and the constants the tool links, by slot, as `$constants`.

### `tools[].config.fetchUrl.cost.expression.jq.program`

string · required

The program. It returns the cost as a number or a numeric string, or nothing for a call that cost nothing.

- length 1 to 1000 characters

### `tools[].config.fetchUrl.cost.unit`

object

What the expression's number counts.

- default `{"type":"usd"}`

One of 2 kinds, told apart by `type`.

### `tools[].config.fetchUrl.cost.unit.usd` — `type: "usd"`

US dollars.

No other fields.

### `tools[].config.fetchUrl.cost.unit.credits` — `type: "credits"`

The endpoint's own credits, priced in US dollars.

### `tools[].config.fetchUrl.cost.unit.credits.usdPerCredit`

number · required

What one credit costs, in US dollars.

- range 0 to 1000

### `tools[].config.fetchUrl.cost.onError`

string

What an expression that fails does to the call.

- one of `"ignore"` `"error"`
- default `"ignore"`

### `tools[].config.fetchUrl.outputFilters`

array of objects

Filters run in order over the response, each over what the one before it produced. The model reads what the last one produces.

- size at most 8 items
- default `[]`

One of 3 kinds, told apart by `type`.

### `tools[].config.fetchUrl.outputFilters[].template` — `type: "template"`

A Mustache template rendered over the JSON it is given.

### `tools[].config.fetchUrl.outputFilters[].template.template`

object · required

The template the JSON is rendered into.

One of 2 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.outputFilters[].template.template.mustache` — `syntax: "mustache"`

A Mustache template.

### `tools[].config.fetchUrl.outputFilters[].template.template.mustache.template`

string · required

The template.

- length 1 to 16000 characters

### `tools[].config.fetchUrl.outputFilters[].template.template.minijinja` — `syntax: "minijinja"`

A MiniJinja template, in Jinja's syntax. It reads the JSON input as `body`, the call's arguments as `args`, the exchange as `request` and `response` (`response.headers` by lower-cased name), and the linked constants as `constants`. A value it reads that is missing fails the call: test with `is defined` or give a `default`.

### `tools[].config.fetchUrl.outputFilters[].template.template.minijinja.template`

string · required

The template.

- length 1 to 16000 characters

### `tools[].config.fetchUrl.outputFilters[].template.outputMimeType`

string

What the filter's output is, which the conversation renders it as. A filter after it that reads JSON needs `application/json`.

- one of `"text/plain"` `"text/markdown"` `"application/json"`
- default `"text/plain"`

### `tools[].config.fetchUrl.outputFilters[].template.emptyIsError`

boolean

A template that renders to nothing fails the call.

- default `false`

### `tools[].config.fetchUrl.outputFilters[].jq` — `type: "jq"`

A jq program run over the JSON it is given.

### `tools[].config.fetchUrl.outputFilters[].jq.program`

string · required

The jq program. It sees the response, its headers and the call's arguments, and nothing else.

- length 1 to 16000 characters

### `tools[].config.fetchUrl.outputFilters[].jq.outputMimeType`

string

What the filter's output is, which the conversation renders it as. A filter after it that reads JSON needs `application/json`.

- one of `"text/plain"` `"text/markdown"` `"application/json"`
- default `"application/json"`

### `tools[].config.fetchUrl.outputFilters[].jq.multipleOutputs`

string

What the filter hands on when the program emits other than one value.

- one of `"first"` `"array"` `"joinLines"`
- default `"first"`

### `tools[].config.fetchUrl.outputFilters[].jq.rawOutput`

boolean

Hands a string on unquoted, as `jq -r` does.

- default `false`

### `tools[].config.fetchUrl.outputFilters[].jq.emptyIsError`

boolean

A program that emits nothing fails the call.

- default `false`

### `tools[].config.fetchUrl.outputFilters[].llm` — `type: "llm"`

A model asked one question about the text it is given, whose answer is passed on instead.

### `tools[].config.fetchUrl.outputFilters[].llm.model`

object · required

The provider and model asked, and the settings it runs with.

Fields as in [`ModelSettings`](#modelsettings), under Shared parts.

### `tools[].config.fetchUrl.outputFilters[].llm.instruction`

string

The system prompt the model is given: what to do with the text. Empty sends none.

- length at most 8000 characters
- default `""`

### `tools[].config.fetchUrl.outputFilters[].llm.userTemplate`

object

The message the model is sent, rendered over the response.

- default `{"syntax":"mustache","template":"{{body}}"}`

One of 2 kinds, told apart by `syntax`.

### `tools[].config.fetchUrl.outputFilters[].llm.userTemplate.mustache` — `syntax: "mustache"`

A Mustache template.

### `tools[].config.fetchUrl.outputFilters[].llm.userTemplate.mustache.template`

string · required

The template.

- length at most 8000 characters
- default `"{{body}}"`

### `tools[].config.fetchUrl.outputFilters[].llm.userTemplate.minijinja` — `syntax: "minijinja"`

A MiniJinja template, in Jinja's syntax, as is the instruction. Both read the text the filter is given as `body`, the call's arguments as `args`, the exchange as `request` and `response` (`response.headers` by lower-cased name), and the linked constants as `constants`. A value they read that is missing fails the call: test with `is defined` or give a `default`.

### `tools[].config.fetchUrl.outputFilters[].llm.userTemplate.minijinja.template`

string · required

The template.

- length at most 8000 characters
- default `"{{body}}"`

### `tools[].config.fetchUrl.outputFilters[].llm.outputMimeType`

string

What the filter's output is, which the conversation renders it as. A filter after it that reads JSON needs `application/json`.

- one of `"text/plain"` `"text/markdown"` `"application/json"`
- default `"text/plain"`

### `tools[].config.fetchUrl.outputFilters[].llm.maxInputChars`

integer

How many characters of the text the model is shown. Zero shows all of it.

- range 0 to 10000000
- default `20000`

### `tools[].config.fetchUrl.outputFilters[].llm.timeoutMs`

integer

How long the model may take to answer, in milliseconds.

- range 1000 to 3600000
- default `600000`

### `tools[].config.fetchUrl.outputFilters[].llm.answerSchema`

object or null

The schema the answer is held to, so it arrives as data. Absent asks for prose.

### `tools[].config.fetchUrl.outputFilters[].llm.answerSchema.schema`

object · required

A JSON Schema the provider holds the answer to. Any schema of draft 2020-12 is taken, nested at most 16 objects and lists deep, with at most 64 entries in any one of them and 2000 characters in any key or text; which keywords a provider honours is the provider's to say.

- follows [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema)

### `tools[].config.fetchUrl.outputFilters[].llm.answerSchema.strict`

boolean

Asks the provider to hold the answer to the schema exactly.

- default `false`

### `tools[].config.memory` — `type: "memory"`

A named store of notes the model reads and writes, kept across conversations.

### `tools[].config.memory.name`

string · required

The memory store's name. The model sees the tool named with it after `memory-`, and an agent attaches the tool by it.

- length 1 to 57 characters
- pattern `^(?:[a-zA-Z0-9_-]+)$`

### `tools[].config.memory.availableKeys`

array of strings

The keys the model may write notes under; it writes under no other.

- size at most 64 items
- default `[]`
- item length 1 to 128 characters

### `tools[].config.conversationTitle` — `type: "conversationTitle"`

A tool through which the conversation's own model sets the conversation's title and summary.

### `tools[].config.conversationTitle.name`

string · required

The tool's name. The model sees the tool named with it after `conversation_title-`, and an agent attaches the tool by it.

- length 1 to 45 characters
- pattern `^(?:[a-zA-Z0-9_-]+)$`

### `tools[].config.conversationTitle.description`

string or null

What the model is told about when and how to call the tool. Given, it replaces the app's own text; the app adds no other instruction about the tool.

- length 1 to 4000 characters
- pattern `\S`

### `agents`

array of objects

The agents in the file.

- size at most 64 items
- default `[]`

### `agents[].id`

string · required

The agent's id on the device that exported it. An agent that calls it names it by this id.

- length 36 characters
- pattern `^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`

### `agents[].origin`

object or null

Where the agent was imported from, when it was imported rather than set up on the exporting device.

Fields as in [`Origin`](#origin), under Shared parts.

### `agents[].config`

object · required

The agent's settings.

### `agents[].config.name`

string · required

The agent's name, as the app shows it.

- length 1 to 80 characters

### `agents[].config.shortDescription`

string or null

What other agents are shown to tell this one apart. It never enters the agent's own turn.

- length at most 300 characters

### `agents[].config.modelSettings`

object

The model the agent runs, and its generation settings.

- default `{}`

Fields as in [`ModelSettings`](#modelsettings), under Shared parts.

### `agents[].config.systemPrompt`

object · required

The instructions the agent's model is given in every conversation.

One of 1 kinds, told apart by `type`.

### `agents[].config.systemPrompt.plain` — `type: "plain"`

The instructions, sent as written.

### `agents[].config.systemPrompt.plain.content`

string · required

The instructions.

- length 1 to 50000 characters
- pattern `\S`

### `agents[].config.welcomeMessage`

object or null

Opens every new conversation as the agent's first message.

One of 1 kinds, told apart by `type`.

### `agents[].config.welcomeMessage.plain` — `type: "plain"`

The message, shown as written.

### `agents[].config.welcomeMessage.plain.content`

string · required

The message.

- length 1 to 2000 characters
- pattern `\S`

### `agents[].config.maxToolRounds`

integer or null

How many rounds of answer and tool calls one turn may take; 0 is no limit. Absent uses the app's own limit.

- range 0 to 10000

### `agents[].config.maxStreamAttempts`

integer or null

How many times a response that never arrived is asked for; 1 asks once.

- range 1 to 10

### `agents[].config.tools`

array of objects

The tools the agent may call.

- size at most 64 items
- default `[]`

One of 12 kinds, told apart by `type`.

### `agents[].config.tools[].builtin` — `type: "builtin"`

A tool built into the app, which needs no setup.

### `agents[].config.tools[].builtin.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- one of `"current_date_time"` `"fetch_image"`

### `agents[].config.tools[].memory` — `type: "memory"`

A memory tool of the bundle's `tools`, and what the agent may do with its store.

### `agents[].config.tools[].memory.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- length 1 to 57 characters
- pattern `^(?:[a-zA-Z0-9_-]+)$`

### `agents[].config.tools[].memory.allowWrite`

boolean

The agent may write notes to the store.

- default `true`

### `agents[].config.tools[].memory.allowDelete`

boolean

The agent may delete notes from the store.

- default `true`

### `agents[].config.tools[].fetchUrl` — `type: "fetchUrl"`

A fetch tool of the bundle's `tools`.

### `agents[].config.tools[].fetchUrl.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- length 1 to 54 characters
- pattern `^(?:[a-zA-Z0-9_-]+)$`

### `agents[].config.tools[].subAgent` — `type: "subAgent"`

Hands a message to one of the listed agents and returns its reply.

### `agents[].config.tools[].subAgent.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- always `"call"`

### `agents[].config.tools[].subAgent.agents`

array of objects

The agents the call may reach, each by its name. The calling agent is shown each one's name and short description. An empty list reaches none.

- size at most 64 items
- default `[]`

### `agents[].config.tools[].subAgent.agents[].name`

string · required

The name of an agent in the bundle's `agents`.

- length 1 to 80 characters

### `agents[].config.tools[].elicitation` — `type: "elicitation"`

Asks the person in the conversation and waits for the answer.

### `agents[].config.tools[].elicitation.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- one of `"ask_text"` `"ask_choice"` `"confirm"`

### `agents[].config.tools[].tools` — `type: "tools"`

Lets the agent list, test, save and delete the app's tools.

### `agents[].config.tools[].tools.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- one of `"list"` `"get"` `"test"` `"delete"` `"request_secret"` `"copy_secret"` `"link_secret"` `"unlink_secret"` `"link_constant"` `"unlink_constant"` `"set_icon"` `"save_fetch_url"` `"save_conversation_title"`

### `agents[].config.tools[].agents` — `type: "agents"`

Lets the agent list, save, test and delete the app's agents.

### `agents[].config.tools[].agents.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- one of `"list"` `"get"` `"save"` `"delete"` `"test"` `"follow_up"` `"answer"`

### `agents[].config.tools[].providers` — `type: "providers"`

Lets the agent list the app's providers and their models, and save a provider.

### `agents[].config.tools[].providers.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- one of `"list"` `"models"` `"model"` `"set_icon"` `"save"` `"link_secret"` `"unlink_secret"` `"link_constant"` `"unlink_constant"`

### `agents[].config.tools[].icons` — `type: "icons"`

Lets the agent find icons.

### `agents[].config.tools[].icons.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- always `"discover"`

### `agents[].config.tools[].secrets` — `type: "secrets"`

Lets the agent list the saved keys by name, never their values.

### `agents[].config.tools[].secrets.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- one of `"list"` `"save"` `"delete"`

### `agents[].config.tools[].constants` — `type: "constants"`

Lets the agent list the constants, save one, link one to a tool or a provider, and set an agent's own text for one.

### `agents[].config.tools[].constants.name`

string · required

Which tool of its kind: the memory store's or the fetch tool's name, or one of the tools the app has of that kind.

- one of `"list"` `"save"` `"override_for_agent"`

### `agents[].config.tools[].conversationTitle` — `type: "conversationTitle"`

A conversation title tool of the bundle's `tools`, through which the model sets the conversation's title and summary.

### `agents[].config.tools[].conversationTitle.name`

string · required

The name of a conversation title tool of the bundle's `tools`.

- length 1 to 45 characters
- pattern `^(?:[a-zA-Z0-9_-]+)$`

## Shared parts

Parts of a bundle used in more than one place, under their names in the schema.

### `Origin`

object

Where an entity was imported from: the namespace and the id it had in the bundle it came in, and the marketplace version when it came from the marketplace.

### `Origin.namespaceId`

string · required

The namespace of the bundle the entity came in.

- length 36 characters
- pattern `^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`

### `Origin.id`

string · required

The id the entity had in that bundle.

- length 36 characters
- pattern `^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`

### `Origin.version`

integer or null

The marketplace version the entity was imported at, when it came from the marketplace.

- range 1 to 1000000000

### `SecretBindings`

map of names to strings

Each slot of an item, named by its key, beside the id of the secret it uses.

- size at most 8 entries
- entry length at most 128 characters

### `ConstantBindings`

map of names to strings

Each {constants.\<slot>} an item places, named by its slot, beside the id of the constant it places.

- size at most 16 entries
- entry length at most 128 characters

### `Endpoint`

object

A server a provider sends requests to.

### `Endpoint.baseUrl`

string · required

The address requests are sent to, which each request's path is added to.

- length 1 to 2048 characters

### `Endpoint.auth`

object or null

How requests authenticate. Absent leaves it to the connection kind's own default.

One of 2 kinds, told apart by `type`.

### `Endpoint.auth.none` — `type: "none"`

Requests carry no key.

No other fields.

### `Endpoint.auth.apiKey` — `type: "apiKey"`

Requests carry a key in a header. The key itself is kept apart from the provider and never travels in a bundle.

### `Endpoint.auth.apiKey.header`

string or null

The header the key is sent in.

- length 1 to 200 characters
- default `"Authorization"`

### `Endpoint.auth.apiKey.prefix`

string or null

What comes before the key in the header's value, such as `Bearer`.

- length at most 200 characters
- default `"Bearer"`

### `Endpoint.customHeaders`

array of objects

Headers sent with every request, in the order written. They travel with the file, so none of them may carry a key.

- size at most 32 items
- default `[]`

### `Endpoint.customHeaders[].name`

string · required

The header's name.

- length 1 to 128 characters

### `Endpoint.customHeaders[].value`

string · required

The header's value.

- length at most 1024 characters

### `ModelSettings`

object

Which model runs, and with what generation settings. A setting left out is the app's or the provider's own.

### `ModelSettings.binding`

object or null

The provider and model that run. Absent from a bundle whose exporter left the choice to the importer.

One of 3 kinds, told apart by `type`.

### `ModelSettings.binding.provider` — `type: "provider"`

A model a provider lists, named by its id there.

### `ModelSettings.binding.provider.providerId`

string · required

The id of the provider the model is reached through, an item of the bundle's `providers`.

- length 1 to 128 characters

### `ModelSettings.binding.provider.modelId`

string · required

The model's id, as the provider names it.

- length 1 to 200 characters

### `ModelSettings.binding.provider.allowUnlistedModel`

boolean

Lets a model the provider's catalogue does not list be chosen.

### `ModelSettings.binding.files` — `type: "files"`

A model that runs on the device, named by its files, which the importer looks for among its own. Only a bundle names a model this way.

### `ModelSettings.binding.files.base`

object · required

The model's weights.

Fields as in [`WeightsFile`](#weightsfile), under Shared parts.

### `ModelSettings.binding.files.projector`

object or null

The multimodal projector paired with the weights.

Fields as in [`WeightsFile`](#weightsfile), under Shared parts.

### `ModelSettings.binding.named` — `type: "named"`

A model named by its id alone: the provider it ran on was left out of the bundle, so the importer chooses one and reads which model it was. Only a bundle names a model this way.

### `ModelSettings.binding.named.modelId`

string · required

The model's id, as the provider left out named it.

- length 1 to 200 characters

### `ModelSettings.requires`

object

What the model must be able to do, which an importer's choice of model is held to.

### `ModelSettings.requires.imageInput`

boolean

The model reads images: the agent is meant to be shown them.

### `ModelSettings.requires.reasoning`

boolean

The model reasons before it answers.

### `ModelSettings.maxTokens`

integer or null

The most tokens an answer may take.

### `ModelSettings.temperature`

number or null

How random the answer is: lower is more predictable.

- range 0 to 2

### `ModelSettings.frequencyPenalty`

number or null

Discourages a token the more often it has already appeared.

- range -2 to 2

### `ModelSettings.presencePenalty`

number or null

Discourages any token that has already appeared, encouraging new topics.

- range -2 to 2

### `ModelSettings.repetitionPenalty`

number or null

Discourages repeating text; 1 leaves it as it is.

- range 0 to 2

### `ModelSettings.reasoningEffort`

string or null

How much a reasoning model thinks before it answers.

- one of `"none"` `"minimal"` `"low"` `"medium"` `"high"` `"xhigh"` `"max"`

### `ModelSettings.reasoningBudgetTokens`

integer or null

How many tokens a reasoning model may think with, for a model that takes a budget instead of an effort.

### `WeightsFile`

object

One GGUF file, known by a digest of its contents and described by what its header says, for a person to recognise it by.

### `WeightsFile.digest`

string · required

The digest of the file's contents, in lowercase hexadecimal.

- length 1 to 128 characters
- pattern `^(?:[0-9a-f]+)$`

### `WeightsFile.digestAlgorithm`

string · required

The hash that produced the digest.

- always `"xxh3-64"`

### `WeightsFile.sizeBytes`

integer · required

The file's size in bytes.

- range 1 to 9223372036854775807

### `WeightsFile.name`

string or null

The model's name, as its header gives it.

- length 1 to 200 characters

### `WeightsFile.architecture`

string or null

The model's architecture, as its header gives it.

- length 1 to 64 characters

### `WeightsFile.quantization`

string or null

How the weights are quantized, such as `Q4_K_M`.

- length 1 to 64 characters

### `WeightsFile.source`

string or null

A page where the file can be downloaded, opened in a browser and never requested by the app.

- length 1 to 2048 characters

---

[Bring Your LM](/) · [Docs](/docs/features) · [Marketplace](/marketplace)

[Articles](/articles) · [Releases](/releases) · [Bundle Format](/marketplace/bundle-format) · [Marketplace Terms](/terms) · [App Privacy](/privacy) · [Website Privacy](/website-privacy) · [Open Source](/open-source) · [llms.txt](/llms.txt)
