@barry-rocks/sdk/bags
Types for bags as Barry sees them: the parsed manifest (Bag and the shapes
of its apps, hooks, dependencies, stores, and more), plus parseManifest and
findManifest. Use it when code needs to read a bag.yaml; the
configuration reference documents each key.
import { … } from "@barry-rocks/sdk/bags";Interfaces
Section titled “Interfaces”ActionRunSpec
Section titled “ActionRunSpec”A run: block, normalized. Paths are still relative to the action directory.
Properties
Section titled “Properties”| Property | Type |
|---|---|
args |
string[] |
command |
string |
entry? |
string |
workingDirectory? |
string |
A fully loaded bag with resolved paths and capabilities
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
actionsDirs |
string[] |
Dirs holding this bag’s actions (<bagDir>/actions). Twin of skillsDirs. |
apps |
BagApp & object[] |
GUI applications declared by this bag |
builtin |
boolean |
- |
dependencies |
BagDependency[] |
- |
description |
string |
- |
hooks |
ResolvedBagHook[] |
Agent hooks declared by this bag, resolved to runnable commands |
infra? |
BagInfra |
Infrastructure declared by this bag |
instructionsDirs |
string[] |
Dirs holding this bag’s instructions (<bagDir>/instructions). |
jobs? |
WorkContract & object & object[] |
Jobs declared by this bag. dir is the bag directory their paths resolve in. |
manifest |
BagManifest | null |
- |
mcpServers |
Record<string, BagMcpServer> |
- |
name |
string |
- |
requires |
BagRequirement & object[] |
Capability requirements, tagged with the required bag’s name |
schedules |
BagSchedule & object[] |
Schedules declared by this bag. dir is the bag directory inline actions run in. |
services |
BagService & object[] |
Long-running services declared by this bag |
skillsDirs |
string[] |
- |
slashCommands |
BagSlashCommand[] |
- |
source |
BagSource |
- |
tools |
BagToolMeta[] |
- |
toolsError? |
string |
Why this bag’s tool code could not be built or imported, when it could not. Set instead of throwing, so one broken bag degrades itself rather than every load it is part of. Everything the manifest declares (services, schedules, skills, composition) is still present; only tools is unknown. Unknown, not empty: trait derivation must not guess namespaces for this bag, and nothing may prune grants on the strength of its absence. |
traits |
BagTrait[] |
- |
BagApp
Section titled “BagApp”A GUI application declared by a bag (apps: in bag.yaml).
Distinct from a service: an app is built, installed as a bundle, and launched by the user, who may quit it. A service that exits has failed; an app that exits has not.
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
build |
BagAppBuild |
- |
bundleId? |
string |
- |
capabilities? |
BagAppCapabilities |
- |
description |
string |
- |
device? |
boolean |
iOS only: whether this app can be signed and installed onto a device. |
displayName? |
string |
- |
id? |
string |
- |
keepAlive? |
boolean |
- |
loginItem? |
boolean |
- |
name |
string |
- |
output |
string |
- |
platform |
"macos" | "ios" |
- |
protocolVersion? |
number |
- |
urlSchemes? |
string[] |
- |
BagAppCapabilities
Section titled “BagAppCapabilities”Properties
Section titled “Properties”BagAuthCommand
Section titled “BagAuthCommand”CLI-delegated auth for a bag (auth: in bag.yaml).
Some vendors ship their own OAuth in their CLI (e.g. temporal cloud login).
barry bag auth <name> runs check first (exit 0 → already authenticated),
then runs command interactively so the user can complete the browser flow.
Credentials stay in the vendor CLI’s own store — no vault/barry env plumbing.
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
args? |
string[] |
- |
check? |
object |
Optional command that exits 0 when already authenticated |
check.args? |
string[] |
- |
check.command |
string |
- |
command |
string |
- |
BagBoundDefinition
Section titled “BagBoundDefinition”A named bound as a manifest defines it.
Properties
Section titled “Properties”| Property | Type |
|---|---|
bound |
Bound |
description |
string |
BagChildSource
Section titled “BagChildSource”Where a composed sub-bag’s code comes from.
Exactly one of repo or path — enforced by the schema, because an entry
with neither is a declaration that names no source and would otherwise parse
into “resolve from the registry”, silently behaving like a bare name.
Properties
Section titled “Properties”BagCliConfig
Section titled “BagCliConfig”CLI configuration for a bag.
Optional — only needed when the bag wants a different CLI command name
than its manifest name. Which bags actually appear in barry -h is
controlled by the user’s ~/.config/barry/cli.yaml, not the manifest.
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
alias? |
string |
Alternate CLI command group name (default: bag name). Used as: barry <alias> <tool> |
BagCredentialDeclaration
Section titled “BagCredentialDeclaration”One harness credential’s default source. Vault-only — see
BagManifest.credentials.
Properties
Section titled “Properties”| Property | Type |
|---|---|
field |
"username" | "password" | "notes" | "uri" |
item |
string |
source |
"vault" |
BagDefaultNotifier
Section titled “BagDefaultNotifier”A bag’s fallback notifier — see default_notifier on the manifest.
Properties
Section titled “Properties”BagDependency
Section titled “BagDependency”Properties
Section titled “Properties”BagHook
Section titled “BagHook”An agent hook declared in a bag manifest (hooks: in bag.yaml)
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
args? |
string[] |
Extra argv appended to the command — carries the hook’s mode (e.g. session-tracker’s start) |
cursorEvents? |
string[] |
Override the default neutral→Cursor event mapping for this hook |
cursorMatcher? |
string |
Cursor matcher (substring semantics, e.g. "npm " on beforeShellExecution) |
event |
BagHookEvent |
- |
matcher? |
string |
Claude tool-name matcher (PreToolUse/PostToolUse); omitted = match all |
run |
string |
Script path relative to the bag directory |
timeout? |
number |
Per-invocation timeout in seconds |
BagHostingCapability
Section titled “BagHostingCapability”What a hosting provider bag can do (provides: in bag.yaml), in
vendor-neutral words: give services local names, and/or carry tunnel routes
at the listed reaches behind the listed gates.
Properties
Section titled “Properties”| Property | Type |
|---|---|
local? |
Record<string, never> |
tunnel? |
object |
tunnel.gates |
"access"[] |
tunnel.reach |
("private" | "public")[] |
BagInfra
Section titled “BagInfra”Infrastructure-as-code a bag owns (infra: in bag.yaml).
Today only Terraform is supported. barry bag infra <name> plan|apply
runs terraform in the declared root with the declared variables set.
Properties
Section titled “Properties”BagLaunch
Section titled “BagLaunch”What a bag declares when it wants something RUN.
Shared by services, scripts and app builds, which had repeated these three
fields over and over. Everything else about them differs — a service carries
launchd lifecycle and network identity, a script nothing at all — so only the
genuinely common part lives here. (Scheduled work is an action on a
schedule, which has its own run: shape.)
Services normalize literals into named values; scripts retain KEY=VALUE entries. Their representation belongs to each launch consumer.
Distinct from the agents’ LaunchPlan for the same kind of reason: that one
carries resolved values, temp dirs to clean up, and a reserved session id,
none of which exist in a manifest.
Extended by
Section titled “Extended by”Properties
Section titled “Properties”BagManifest
Section titled “BagManifest”Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
apps |
Record<string, BagApp> |
GUI applications this bag ships |
auth? |
BagAuthCommand |
- |
bags? |
string[] |
Sub-bags this bag includes — auto-enabled when this bag is enabled |
bagSources? |
Record<string, BagChildSource> |
Where sub-bags named in bags come from, for those that say. Keyed by the same name that appears in bags, so composition stays a single list and this only answers “where do I fetch it from”. A name in bags with no entry here resolves from the registry as it always has. The distinction is load-bearing at install time: a name alone is best-effort widening (the bag may not exist yet, so it is skipped with a warning), while an entry here is an unambiguous claim that the child exists at that source — so failing to fetch it fails the install. |
barryHooks? |
BarryHook[] |
Record hooks this bag declares (hooks.barry) |
bounds? |
Record<string, BagBoundDefinition> |
Named bounds this bag defines, keyed by name (bounds: at the manifest’s top level). A trait refers to one by name in its own bounds: list. Distinct from a trait’s inline bound:, which restricts that one trait and has no name for anything else to reuse. |
cli? |
BagCliConfig |
CLI exposure configuration — how this bag’s tools appear as barry <group> <tool> commands |
constants? |
Record<string, unknown> |
- |
credentials? |
Record<string, BagCredentialDeclaration> |
Credentials a HARNESS bag’s sessions authenticate with, as a resolution plan: env var name -> where the value comes from when the identity does not define the variable itself. The identity’s own definition wins; this map is the default. Declared here because a claim a picker or the gate can read without loading adapter code is the one that holds. Vault-only: a manifest is shared code, and an inline constant in shared code is either a placeholder (vendor rows have placeholder for that) or a secret committed to the repo. |
default_notifier? |
BagDefaultNotifier |
A notifier this bag offers when the barry has not configured one. Progress notifications resolve in precedence order: an explicit notify_tool argument, then the barry’s own status_notify, then a bag default, then nothing. The last two exist for different reasons — the barry’s setting is a user’s deliberate choice and always wins, while this is a bag saying “if you have not picked anything, I can carry it.” The tool named here must be one this bag actually provides, and must work without a target — a default that needs configuring is not a default. |
dependencies |
BagDependency[] |
- |
description |
string |
- |
extends? |
string |
The plain name of the bag this extension adds to. |
hooks? |
BagHook[] |
Agent hooks this bag declares (hooks.agent, or the legacy bare array) |
infra? |
BagInfra |
Infrastructure-as-code this bag owns (terraform root + variables) |
instructions? |
string |
Guidance injected into the session’s system prompt when this bag’s tools are active. Use it to steer the agent toward the bag’s structured tools — e.g. the git bag tells the agent to use git_* tools, not shell git. |
jobs? |
Record<string, BagJob> |
Work this bag ships for Barry to run (jobs:). Absent when it declares none. |
mcpServers |
Record<string, BagMcpServer> |
- |
name |
string |
- |
permissions? |
BagPermission[] |
macOS privacy permissions its code needs. |
promptsEntry? |
BagPromptsEntry |
- |
provides? |
BagHostingCapability |
Hosting this bag can provide (provides:), if it is a hosting provider |
requires? |
Record<string, BagRequirement> |
Capabilities of other bags this bag calls (requires: in bag.yaml) |
schedules |
Record<string, BagSchedule> |
Actions this bag runs on a trigger (schedules:), run by the scheduler service |
scripts |
Record<string, BagScript> |
On-demand commands this bag ships (barry <bag> <script>) |
services |
Record<string, BagService> |
Long-running services this bag declares (launchd-managed) |
slashCommands? |
BagSlashCommandsEntry |
Slash commands this bag provides for the Slack server |
storeRestores? |
Record<string, { entry: string; }> |
- |
stores? |
Record<string, { engine: "sqlite"; migrations: string; owned_by: "identity" | "node"; }> |
- |
storeSnapshots? |
Record<string, { entry: string; }> |
- |
tools |
BagToolMeta[] |
- |
toolsEntry? |
BagToolsEntry |
- |
toolVisibility? |
Record<string, "enabled" | "deferred" | "disabled"> |
- |
traits |
Record<string, Omit<BagTrait, "name">> |
- |
vendors? |
Vendor[] |
- |
version? |
string |
The bag’s own release (e.g. “v0.1”), if it declares one. |
BagMcpServer
Section titled “BagMcpServer”MCP server definition within a bag
Properties
Section titled “Properties”BagPermission
Section titled “BagPermission”A macOS privacy permission the bag’s code needs, granted by a person in
System Settings › Privacy & Security. Declared so barry bag doctor can say
what to grant and to which program before a tool fails on a silent denial.
Properties
Section titled “Properties”BagPromptsEntry
Section titled “BagPromptsEntry”A bag’s computed prompt contributor (prompts: { entry }).
Properties
Section titled “Properties”| Property | Type |
|---|---|
entry |
string |
BagRegistrySnapshot
Section titled “BagRegistrySnapshot”Immutable view of one registry resolution, shared by runtime consumers.
Properties
Section titled “Properties”| Property | Type |
|---|---|
bags |
readonly Bag[] |
byName |
ReadonlyMap<string, Bag> |
registry |
Readonly<BagRegistry> |
BagRequirement
Section titled “BagRequirement”A capability requirement declared in a bag manifest (requires: in bag.yaml)
Properties
Section titled “Properties”BagSchedule
Section titled “BagSchedule”A schedule, normalized.
Properties
Section titled “Properties”| Property | Type |
|---|---|
bounds? |
string[] |
catchUp |
"none" | "once" |
description? |
string |
enabled |
boolean |
identity? |
string |
inputs? |
Record<string, unknown> |
overlap |
"queue" | "skip" |
payload |
SchedulePayload |
traits? |
string[] |
trigger |
ScheduleTrigger |
BagScript
Section titled “BagScript”A command a bag ships, run on demand as barry <bag> <script>
(scripts: in bag.yaml). Nothing installs it, so it carries no launchd
bookkeeping; to run one on a timer, make it a run: action on a schedule.
Extends
Section titled “Extends”Properties
Section titled “Properties”| Property | Type | Description | Inherited from |
|---|---|---|---|
args? |
string[] |
- | BagLaunch.args |
command |
string |
- | BagLaunch.command |
description |
string |
- | BagLaunch.description |
entry? |
string |
Bag-relative entry; command is the normalized interpreter placeholder until launch. | BagLaunch.entry |
env? |
string[] |
Non-secret KEY=VALUE literals. Credentials belong under secrets. |
- |
secrets? |
string[] |
- | - |
workingDirectory? |
string |
Relative to the bag directory; defaults to the bag dir. | BagLaunch.workingDirectory |
BagService
Section titled “BagService”What a bag declares when it wants something RUN.
Shared by services, scripts and app builds, which had repeated these three
fields over and over. Everything else about them differs — a service carries
launchd lifecycle and network identity, a script nothing at all — so only the
genuinely common part lives here. (Scheduled work is an action on a
schedule, which has its own run: shape.)
Services normalize literals into named values; scripts retain KEY=VALUE entries. Their representation belongs to each launch consumer.
Distinct from the agents’ LaunchPlan for the same kind of reason: that one
carries resolved values, temp dirs to clean up, and a reserved session id,
none of which exist in a manifest.
Extends
Section titled “Extends”Properties
Section titled “Properties”| Property | Type | Description | Inherited from |
|---|---|---|---|
args? |
string[] |
- | BagLaunch.args |
autostart? |
"login" | "manual" |
Who starts this: the machine at login (the default), or something else. A service RUNS CONTINUOUSLY by definition, so this is the only part of its lifecycle a bag still chooses. manual covers both a service another process bootstraps (coffee’s supervisor, started by its own tools) and one that is simply parked until needed (file-stream) — the same case, since neither wants the machine starting it. |
- |
command |
string |
- | BagLaunch.command |
description |
string |
- | BagLaunch.description |
entry? |
string |
Bag-relative entry; command is the normalized interpreter placeholder until launch. | BagLaunch.entry |
env? |
LiteralEnvValue[] |
Authored non-secret process literals. | - |
health? |
string |
Readiness probe path, e.g. “/health”. | - |
localOnly? |
boolean |
Its security assumes callers on this Mac, so no tunnel may route to it. Only a local name may. |
- |
optionalSecrets? |
string[] |
Secrets the service runs without (optional-secrets:). |
- |
secrets? |
string[] |
- | - |
serves? |
"http" |
The protocol this service speaks. http: it listens on the loopback port the service management assigns (injected as PORT), or on its socket. |
- |
socket? |
string |
Unix socket path, relative to the state directory for user: services and to the bag directory otherwise, instead of an assigned port: kernel-enforced socket permissions are the point, so a TCP listener alongside would reopen what the socket closes. |
- |
user? |
string |
macOS service account this runs as (e.g. “_barryvault”). Absent means the invoking user, installed as a LaunchAgent. Prepare with barry setup, then install with sudo barry setup --system. The privileged pass creates the account and installs root-owned built code as a LaunchDaemon. Its socket lives under the service’s state directory. Ordinary setup and refresh never run it as the invoking user. |
- |
workingDirectory? |
string |
Relative to the bag directory; defaults to the bag dir. | BagLaunch.workingDirectory |
BagSlashCommand
Section titled “BagSlashCommand”A SLACK slash command declared in a bag manifest — /oncall typed into
Slack, dispatched to a handler exported from the entry module.
NOT a coding-agent slash command. Those come from actions declaring
slash_command: true and are generated per session (see
@barry-rocks/sdk/internal/actions action-commands). The two are unrelated despite the
shared word, and the resemblance has already misled one reader into calling
this dead scaffolding.
It is live: the slack-app bag imports each declaring bag’s entry module at
startup (slack-app/src/handlers/bag-commands.ts), registers one route per
command name (routes/commands.ts), and publishes them to Slack via
generate-manifest.ts. ~/repos/bags/vantage-slack ships /oncall and
/sentry this way. Nothing in THIS repo reads slashCommands — the consumer
is a bag, which is why a monorepo-only grep makes it look unused.
Properties
Section titled “Properties”| Property | Type |
|---|---|
description |
string |
name |
string |
BagSlashCommandsEntry
Section titled “BagSlashCommandsEntry”Slack slash commands entry in a bag manifest (slash-commands: in bag.yaml)
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
commands |
BagSlashCommand[] |
- |
entry |
string |
Module exporting one handler per declared command name. |
BagToolMeta
Section titled “BagToolMeta”Tool metadata declared in a bag manifest
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
access |
BagAccess |
- |
contractHash? |
string |
Read from the tool’s code (toolContractHash); absent for manifest and remote tools. |
description? |
string |
What the tool does, from its definition; absent for manifest and remote tools. |
namespace |
string |
- |
toolName |
string |
- |
BagToolsEntry
Section titled “BagToolsEntry”Conventional local tool module discovered from the bag directory.
Properties
Section titled “Properties”| Property | Type |
|---|---|
entry |
string |
BagTrait
Section titled “BagTrait”Trait declared in a bag manifest
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
access |
BagAccess |
- |
bags |
string[] |
Bag/namespace names this trait grants. Resolved to namespaces at the session-scope boundary. |
bound? |
Bound |
Restriction declared inline on the trait rather than referenced by name. Lets a bag outside this repo ship its own bound without a row in the framework’s bounds table. Merged with any named bounds when the session’s effective bound is built. |
boundNames |
string[] |
Named bounds whose restrictions this trait carries, resolved against the bounds table when the session’s effective bound is built. Empty for auto-traits: a generated trait grants everything its bag provides, and narrowing it is an authoring decision, never a derived one. |
description |
string |
- |
harnessTraits? |
Record<string, string> |
Select one included trait by the running harness. |
includes? |
string[] |
- |
instructions |
string[] |
Instruction names this trait points at. Resolved through the instruction catalog at session start; the prose itself lives in an instructions/ file. |
name |
string |
- |
nativeTools? |
Record<string, string[]> |
- |
skills |
string[] |
Skill names (directory basenames under the bag’s skills/ dirs) granted by this trait |
BarryHook
Section titled “BarryHook”A hook on a Barry record, declared under hooks.barry in bag.yaml.
Properties
Section titled “Properties”LiteralEnvValue
Section titled “LiteralEnvValue”Properties
Section titled “Properties”| Property | Type |
|---|---|
name |
string |
source |
"value" |
value |
string |
LocalBagSource
Section titled “LocalBagSource”Registry entry for a local bag
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
access? |
BagAccessLevel |
- |
disabled? |
boolean |
Deprecated use access: "disabled" — kept for back-compat |
git? |
object |
Git remote that path was cloned from, when Barry fetched it. Provenance, exactly like npm above: the bag is still an ordinary local checkout — same loading, building and traits — and this records where it came from so a vanished path can be re-cloned and bag doctor can check the checkout still matches what the manifest declared. rev is the pinned commit when one was requested; ref the branch or tag otherwise. Both are recorded as resolved, not as written, so drift from the manifest is detectable. |
git.ref? |
string |
- |
git.repo |
string |
- |
git.rev? |
string |
- |
npm? |
string |
npm package specifier that owns path (e.g. @acme/my-bag). Stored so the registry can re-resolve a bag whose absolute path vanished after pnpm install reshuffled node_modules. |
path |
string |
- |
type |
"local" |
- |
RemoteBagSource
Section titled “RemoteBagSource”Registry entry for a remote bag (MCP server or command)
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
access? |
BagAccessLevel |
- |
args? |
string[] |
- |
command? |
string |
- |
disabled? |
boolean |
Deprecated use access: "disabled" — kept for back-compat |
env? |
string[] |
- |
resources? |
boolean |
When true, connect to MCP server and discover barry:// resources |
session-scoped? |
boolean |
Connect per session instead of once at startup. Mirrors the manifest’s session-scoped key (BagMcpServer.sessionScoped) for registry entries, which have no manifest to declare it in. Without this a credentialless remote bag is connected eagerly and its process lives as long as the MCP server, whether or not any session uses it. |
tools? |
BagToolMeta[] |
Inline tool metadata for trait-based access control |
type |
"remote" |
- |
url? |
string |
- |
ResolvedBagHook
Section titled “ResolvedBagHook”A bag hook resolved to a runnable command (absolute script path + runner)
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
bag |
string |
Owning bag, for diagnostics |
command |
string |
Full command string, e.g. tsx /abs/path/index.ts start |
cursorEvents? |
string[] |
- |
cursorMatcher? |
string |
- |
event |
BagHookEvent |
- |
matcher? |
string |
- |
timeout? |
number |
- |
WorkContract
Section titled “WorkContract”The contract as the rest of Barry reads it.
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
inputSchema? |
Record<string, unknown> |
- |
outputSchema? |
Record<string, unknown> |
- |
preflight? |
ContractPreflight |
- |
requires |
object |
- |
requires.bins |
string[] |
- |
requires.labels? |
Record<string, string> |
- |
requires.secrets |
string[] |
- |
requires.tools |
string[] |
- |
timeout? |
number |
Run timeout in seconds, when declared. |
validate? |
ContractValidate |
- |
Type Aliases
Section titled “Type Aliases”ActionAgent
Section titled “ActionAgent”ActionAgent =
object
The manifest’s agent: block — which coding agent and model a detached run
prefers. Both optional and resolved independently; see the schema for why
there is no model-provider field.
Properties
Section titled “Properties”agent?
Section titled “agent?”
optionalagent?:HarnessId
model?
Section titled “model?”
optionalmodel?:string
ActionBody
Section titled “ActionBody”ActionBody = {
job?:undefined;prompt:string;run?:undefined; } | {env?:LiteralEnvValue[];job?:undefined;prompt?:undefined;run:ActionRunSpec;secrets?:string[]; } | {job:string;prompt?:undefined;run?:undefined; }
How an action’s work is done: by an agent following prompt, or by Barry
running run. A union so every consumer of prompt has to say what it does
with an action that has none, instead of rendering an empty string.
Union Members
Section titled “Union Members”Type Literal
Section titled “Type Literal”{ job?: undefined; prompt: string; run?: undefined; }
| Name | Type | Description |
|---|---|---|
job? |
undefined |
- |
prompt |
string |
Prompt body, already resolved from the file form when that was used. |
run? |
undefined |
- |
Type Literal
Section titled “Type Literal”{ env?: LiteralEnvValue[]; job?: undefined; prompt?: undefined; run: ActionRunSpec; secrets?: string[]; }
| Name | Type | Description |
|---|---|---|
env? |
LiteralEnvValue[] |
Declared env for the process, resolved at run time. |
job? |
undefined |
- |
prompt? |
undefined |
- |
run |
ActionRunSpec |
- |
secrets? |
string[] |
- |
Type Literal
Section titled “Type Literal”{ job: string; prompt?: undefined; run?: undefined; }
| Name | Type | Description |
|---|---|---|
job |
string |
The job this action runs, as written: name or bag:name[@version]. |
prompt? |
undefined |
- |
run? |
undefined |
- |
ActionContext
Section titled “ActionContext”ActionContext =
"session"|"none"
ActionManifest
Section titled “ActionManifest”ActionManifest =
ActionBody&object
Type Declaration
Section titled “Type Declaration”| Name | Type | Description |
|---|---|---|
agent? |
ActionAgent |
- |
commandDescription? |
string |
Menu subtitle for the slash command. Falls back to description. |
commandHint? |
string |
Argument hint shown during / autocomplete. |
context |
ActionContext |
Caller-context requirement; session unless the manifest says none. |
description |
string |
- |
inputSchema? |
Record<string, unknown> |
JSON Schema for caller-supplied inputs (object root, named properties). Absent = the action takes no declared inputs. |
instructions |
string[] |
Names of shared instructions to prepend, in declared order. |
name |
string |
- |
outputSchema? |
Record<string, unknown> |
JSON Schema for the declared output. Absent = the action declares none. |
preflight? |
ActionPreflight |
Pre-run reachability probe. Absent = the action claims nothing. |
requires |
object |
- |
requires.bins |
string[] |
- |
requires.secrets |
string[] |
- |
requires.tools |
string[] |
- |
slashCommand |
boolean |
Expose as a /name slash command where the provider supports one. |
steps |
ActionStep[] |
Child actions, in declared order. Empty for an ordinary action — a non-empty array is what makes this one a composite, so callers test length rather than needing a separate flag that could disagree with it. |
timeout? |
number |
Run timeout in seconds, when declared. |
validate? |
ActionValidate |
- |
version? |
string |
- |
ActionPreflight
Section titled “ActionPreflight”ActionPreflight =
ContractPreflight
ActionStep
Section titled “ActionStep”ActionStep =
object
One child of a composite action.
An object rather than a bare string so a step can grow fields — input
mapping, a run-only-if condition — without a breaking format change. Today
action is the only one, and every step runs.
Properties
Section titled “Properties”action
Section titled “action”action:
string
Child action, by bare name or bag:name. Resolved by the catalog.
ActionValidate
Section titled “ActionValidate”ActionValidate =
ContractValidate
BagAccess
Section titled “BagAccess”BagAccess =
"read"|"readwrite"
BagAccessLevel
Section titled “BagAccessLevel”BagAccessLevel =
"enabled"|"deferred"|"disabled"
A bag’s visibility in a session, set on its registry entry:
enabled— tools appear in the MCP tools/list normally (default)deferred— tools are hidden from tools/list but stay callable and discoverable via thetool_searchtooldisabled— the bag is off entirely (supersedes the legacydisabledbool)
BagAppBuild
Section titled “BagAppBuild”BagAppBuild =
string| {configuration:string;project:string;scheme:string; }
Package script name or an explicit Xcode build.
BagHookEvent
Section titled “BagHookEvent”BagHookEvent =
"session-start"|"session-end"|"user-prompt"|"stop"|"pre-tool"|"post-tool"
Neutral hook event vocabulary — mapped per provider in sdk/src/hooks/hooks.ts
BagJob
Section titled “BagJob”BagJob =
WorkContract&object
A job, normalized: its contract in the camelCase every reader uses.
Type Declaration
Section titled “Type Declaration”| Name | Type |
|---|---|
bounds? |
string[] |
description? |
string |
runs |
JobRuns |
version? |
string |
BagRegistry
Section titled “BagRegistry”BagRegistry =
Record<string,BagSource>
The bag registry: every installed bag by name (config.db, as its snapshot projects it)
BagResource
Section titled “BagResource”BagResource =
ServiceEntry
One registered resource: how a client reaches it.
BagResourceKind
Section titled “BagResourceKind”BagResourceKind =
ServiceEntry["kind"]
BagSource
Section titled “BagSource”BagSource =
LocalBagSource|RemoteBagSource
BarryHookEvent
Section titled “BarryHookEvent”BarryHookEvent =
"event-created"
Records a bag can hook. Only events, because events have one write path that already announces after commit — see sdk/src/tools/hooks-schema.ts for why errors reach hooks as events rather than directly.
ContractPreflight
Section titled “ContractPreflight”ContractPreflight =
object
Properties
Section titled “Properties”command
Section titled “command”command:
string
optionalhint?:string
timeout?
Section titled “timeout?”
optionaltimeout?:number
ContractValidate
Section titled “ContractValidate”ContractValidate =
object
Properties
Section titled “Properties”command?
Section titled “command?”
optionalcommand?:string
files_exist?
Section titled “files_exist?”
optionalfiles_exist?:string[]
timeout?
Section titled “timeout?”
optionaltimeout?:number
JobRuns
Section titled “JobRuns”JobRuns = {
env?:LiteralEnvValue[];main:string;secrets?:string[];using:"node";workingDirectory?:string; } | {command:string[];env?:LiteralEnvValue[];secrets?:string[];using:"process";workingDirectory?:string; } | {action:string;using:"action"; }
SchedulePayload
Section titled “SchedulePayload”SchedulePayload = {
kind:"ref";ref:string; } | {kind:"inline";manifest:ActionManifest; } | {kind:"job";ref:string; }
What a schedule runs: an action by reference, an inline action, or a job by reference.
ScheduleTrigger
Section titled “ScheduleTrigger”ScheduleTrigger = {
expression:string;kind:"cron";timezone?:string; } | {kind:"at";time:string; } | {kind:"login"; }
Union Members
Section titled “Union Members”Type Literal
Section titled “Type Literal”{ expression: string; kind: "cron"; timezone?: string; }
Type Literal
Section titled “Type Literal”{ kind: "at"; time: string; }
Type Literal
Section titled “Type Literal”{ kind: "login"; }
Once per verified operating-system login event.
SchemaValidationResult
Section titled “SchemaValidationResult”SchemaValidationResult = {
ok:true; } | {errors:string[];ok:false; }
Variables
Section titled “Variables”BoundDefinitionSchema
Section titled “BoundDefinitionSchema”
constBoundDefinitionSchema:ZodObject<{bound:ZodObject<{bash:ZodOptional<ZodObject<{deny:ZodOptional<ZodArray<ZodString>>;denyPrograms:ZodOptional<ZodArray<ZodString>>; },$strict>>;deniedAccess:ZodOptional<ZodArray<ZodString>>;deniedTools:ZodOptional<ZodArray<ZodString>>;description:ZodOptional<ZodString>;files:ZodOptional<ZodObject<{allowOnly:ZodOptional<ZodArray<ZodString>>;deny:ZodOptional<ZodArray<ZodString>>; },$strict>>;name:ZodOptional<ZodString>;nativeTools:ZodOptional<ZodRecord<ZodString,ZodArray<ZodString>>>;network:ZodOptional<ZodObject<{actions:ZodOptional<ZodArray<ZodEnum<{all: …;dns: …;git:fetch: …;git:push: …;http:read: …;http:write: …;read: …;ssh:read: …;ssh:write: …;write: …; }>>>;allowDomains:ZodOptional<ZodArray<ZodString>>;domains:ZodOptional<ZodArray<ZodString>>;enforce:ZodOptional<ZodEnum<{guard:"guard";sandbox:"sandbox"; }>>; },$strict>>;nodes:ZodOptional<ZodObject<{allow:ZodOptional<ZodArray<ZodString>>;deny:ZodOptional<ZodArray<ZodString>>; },$strict>>; },$strict>;description:ZodString; },$strict>
A named bound a manifest defines at its top level. Strict for the same reason as TraitSchema: a misspelled key would otherwise parse clean and restrict nothing. Identity files define bounds with the same fields.
TraitSchema
Section titled “TraitSchema”
constTraitSchema:ZodObject<{access:ZodOptional<ZodEnum<{read:"read";readwrite:"readwrite";write:"write"; }>>;bags:ZodOptional<ZodArray<ZodString>>;bound:ZodOptional<ZodObject<{bash:ZodOptional<ZodObject<{deny:ZodOptional<ZodArray<ZodString>>;denyPrograms:ZodOptional<ZodArray<ZodString>>; },$strict>>;deniedAccess:ZodOptional<ZodArray<ZodString>>;deniedTools:ZodOptional<ZodArray<ZodString>>;description:ZodOptional<ZodString>;files:ZodOptional<ZodObject<{allowOnly:ZodOptional<ZodArray<ZodString>>;deny:ZodOptional<ZodArray<ZodString>>; },$strict>>;name:ZodOptional<ZodString>;nativeTools:ZodOptional<ZodRecord<ZodString,ZodArray<ZodString>>>;network:ZodOptional<ZodObject<{actions:ZodOptional<ZodArray<ZodEnum<…>>>;allowDomains:ZodOptional<ZodArray<ZodString>>;domains:ZodOptional<ZodArray<ZodString>>;enforce:ZodOptional<ZodEnum<{guard: …;sandbox: …; }>>; },$strict>>;nodes:ZodOptional<ZodObject<{allow:ZodOptional<ZodArray<ZodString>>;deny:ZodOptional<ZodArray<ZodString>>; },$strict>>; },$strict>>;bounds:ZodOptional<ZodArray<ZodString>>;description:ZodString;harnessTraits:ZodOptional<ZodRecord<ZodString,ZodString>>;includes:ZodOptional<ZodArray<ZodString>>;instructions:ZodOptional<ZodArray<ZodString>>;namespaces:ZodOptional<ZodArray<ZodString>>;nativeTools:ZodOptional<ZodRecord<ZodString,ZodArray<ZodString>>>;skills:ZodOptional<ZodArray<ZodString>>; },$strict>
Unknown keys are rejected, like every sibling schema here. Until now this one
alone was permissive despite comments claiming otherwise, so a trait that
misspelled a restriction key — bound for bounds, or the reverse — parsed
clean and silently unrestricted.
Functions
Section titled “Functions”assertCompilableSchema()
Section titled “assertCompilableSchema()”assertCompilableSchema(
schema):void
Throws if the schema itself does not compile. Callers that accept schemas from manifests should invoke this at parse time so a broken schema fails the manifest’s own validation instead of the first live run.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
schema |
Record<string, unknown> |
Returns
Section titled “Returns”void
bagServiceUrl()
Section titled “bagServiceUrl()”bagServiceUrl(
bag,name,path?):string
Require this instance’s registered service address, with no fallback; see docs/decisions/0017-sdk-discovery-and-wire-contracts.md.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bag |
string |
name |
string |
path? |
string |
Returns
Section titled “Returns”string
clearManifestCache()
Section titled “clearManifestCache()”clearManifestCache():
void
Returns
Section titled “Returns”void
extensionName()
Section titled “extensionName()”extensionName(
parent,name):string
The registered name of an add-on to one bag.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
parent |
string |
name |
string |
Returns
Section titled “Returns”string
findBagResource()
Section titled “findBagResource()”findBagResource(
bag,name,path?):ServiceEntry|null
The resource, or null when it is not registered.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bag |
string |
name |
string |
path? |
string |
Returns
Section titled “Returns”ServiceEntry | null
findBagResourceUrl()
Section titled “findBagResourceUrl()”findBagResourceUrl(
bag,name,path?):string|null
The resource’s URL, or null when unregistered or not HTTP-reachable.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bag |
string |
name |
string |
path? |
string |
Returns
Section titled “Returns”string | null
findManifest()
Section titled “findManifest()”findManifest(
bagDir):string|null
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bagDir |
string |
Returns
Section titled “Returns”string | null
getBagResourceRegistryPath()
Section titled “getBagResourceRegistryPath()”getBagResourceRegistryPath():
string
Returns
Section titled “Returns”string
getSkillsDirs()
Section titled “getSkillsDirs()”getSkillsDirs(
bagDir):string[]
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bagDir |
string |
Returns
Section titled “Returns”string[]
normalizeTrait()
Section titled “normalizeTrait()”normalizeTrait(
d):Omit<BagTrait,"name">
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
d |
{ access?: "read" | "write" | "readwrite"; bags?: string[]; bound?: { bash?: { deny?: string[]; denyPrograms?: string[]; }; deniedAccess?: string[]; deniedTools?: string[]; description?: string; files?: { allowOnly?: string[]; deny?: string[]; }; name?: string; nativeTools?: Record<string, string[]>; network?: { actions?: ("read" | "write" | "all" | "git:push" | "git:fetch" | "http:write" | "http:read" | "ssh:write" | "ssh:read" | "dns")[]; allowDomains?: string[]; domains?: string[]; enforce?: "guard" | "sandbox"; }; nodes?: { allow?: string[]; deny?: string[]; }; }; bounds?: string[]; description: string; harnessTraits?: Record<string, string>; includes?: string[]; instructions?: string[]; namespaces?: string[]; nativeTools?: Record<string, string[]>; skills?: string[]; } |
- |
d.access? |
"read" | "write" | "readwrite" |
- |
d.bags? |
string[] |
Bag/namespace names — accepts both bags and legacy namespaces. |
d.bound? |
{ bash?: { deny?: string[]; denyPrograms?: string[]; }; deniedAccess?: string[]; deniedTools?: string[]; description?: string; files?: { allowOnly?: string[]; deny?: string[]; }; name?: string; nativeTools?: Record<string, string[]>; network?: { actions?: ("read" | "write" | "all" | "git:push" | "git:fetch" | "http:write" | "http:read" | "ssh:write" | "ssh:read" | "dns")[]; allowDomains?: string[]; domains?: string[]; enforce?: "guard" | "sandbox"; }; nodes?: { allow?: string[]; deny?: string[]; }; } |
Restriction declared inline, rather than referenced by name, so an external bag can ship an opinionated bound without a row in this framework’s config. Validated against the real bound shape — a permissive z.record() would let a misspelled denyPrograms parse into a bound that denies nothing and looks, from every angle, exactly like a working one. A restriction that fails open is worse than one that fails to parse. |
d.bound.bash? |
{ deny?: string[]; denyPrograms?: string[]; } |
- |
d.bound.bash.deny? |
string[] |
- |
d.bound.bash.denyPrograms? |
string[] |
- |
d.bound.deniedAccess? |
string[] |
- |
d.bound.deniedTools? |
string[] |
- |
d.bound.description? |
string |
- |
d.bound.files? |
{ allowOnly?: string[]; deny?: string[]; } |
- |
d.bound.files.allowOnly? |
string[] |
- |
d.bound.files.deny? |
string[] |
- |
d.bound.name? |
string |
- |
d.bound.nativeTools? |
Record<string, string[]> |
- |
d.bound.network? |
{ actions?: ("read" | "write" | "all" | "git:push" | "git:fetch" | "http:write" | "http:read" | "ssh:write" | "ssh:read" | "dns")[]; allowDomains?: string[]; domains?: string[]; enforce?: "guard" | "sandbox"; } |
- |
d.bound.network.actions? |
("read" | "write" | "all" | "git:push" | "git:fetch" | "http:write" | "http:read" | "ssh:write" | "ssh:read" | "dns")[] |
- |
d.bound.network.allowDomains? |
string[] |
- |
d.bound.network.domains? |
string[] |
- |
d.bound.network.enforce? |
"guard" | "sandbox" |
- |
d.bound.nodes? |
{ allow?: string[]; deny?: string[]; } |
- |
d.bound.nodes.allow? |
string[] |
- |
d.bound.nodes.deny? |
string[] |
- |
d.bounds? |
string[] |
Named bounds whose restrictions this trait carries. Read-only is composed this way — the grant plus bounds: [readonly] — which is exactly what the generated <bag>:read twin is. A manifest trait named read replaces that twin, for a bag whose read-only variant needs to differ. The legacy scopes: spelling was dropped here. It was documented as permanent on the premise that external bag.yaml files are unreachable and unsweepable, so no moment could exist when every bag was known converted. The premise was wrong: every bag.yaml on disk was swept and exactly one still said scopes: (the slack bag, since converted). The sweep is also repeatable — registered-manifests.test.ts parses every registered local bag, so a straggler fails the suite instead of failing silently in a running MCP server. config/*.yaml keeps its legacy readers, because its gitignored *.local.yaml twins genuinely cannot be swept. |
d.description |
string |
- |
d.harnessTraits? |
Record<string, string> |
- |
d.includes? |
string[] |
- |
d.instructions? |
string[] |
Instruction names this trait points at. Names, not prose — the body lives in an instructions/ file and resolves through the instruction catalog, so the text is authored once and stays under the prompt budgets. |
d.namespaces? |
string[] |
- |
d.nativeTools? |
Record<string, string[]> |
- |
d.skills? |
string[] |
- |
Returns
Section titled “Returns”Omit<BagTrait, "name">
parentOf()
Section titled “parentOf()”parentOf(
bag):string|null
The parent of an extension, or null for a plain bag.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bag |
string |
Returns
Section titled “Returns”string | null
parseManifest()
Section titled “parseManifest()”parseManifest(
bagDir):BagManifest|null
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bagDir |
string |
Returns
Section titled “Returns”BagManifest | null
parseManifestObject()
Section titled “parseManifestObject()”parseManifestObject(
raw,label?,bagDir?,options?):BagManifest
bagDir is where inline schedule actions resolve their relative paths.
Omitted for a manifest with no directory, where an inline
action naming a file cannot resolve and fails as a missing file.
Parameters
Section titled “Parameters”| Parameter | Type | Default value |
|---|---|---|
raw |
unknown |
undefined |
label |
string |
"manifest" |
bagDir |
string |
"" |
options |
{ source?: "local" | "remote"; } |
{} |
options.source? |
"local" | "remote" |
undefined |
Returns
Section titled “Returns”parseManifestObjectSafe()
Section titled “parseManifestObjectSafe()”parseManifestObjectSafe(
raw,label?,options?):object
Non-throwing variant for untrusted input.
Remote manifests must not be able to break discovery: a third-party server running a newer (or simply wrong) manifest shape should degrade to “no manifest” with a reportable reason, not abort the whole bag load.
Parameters
Section titled “Parameters”| Parameter | Type | Default value |
|---|---|---|
raw |
unknown |
undefined |
label |
string |
"manifest" |
options |
{ source?: "local" | "remote"; } |
{} |
options.source? |
"local" | "remote" |
undefined |
Returns
Section titled “Returns”object
| Name | Type |
|---|---|
error |
string | null |
manifest |
BagManifest | null |
readBagResourceRegistry()
Section titled “readBagResourceRegistry()”readBagResourceRegistry(
path?):Record<string,BagResource>
Every registered resource, or none when there is no readable registry.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
path? |
string |
Returns
Section titled “Returns”Record<string, BagResource>
readRawManifest()
Section titled “readRawManifest()”readRawManifest(
bagDir):Record<string,unknown> |null
The raw manifest object as written on disk, before normalization.
Used when serving a bag over MCP: the wire format is the author’s
bag.yaml as JSON, so it round-trips through the same strict schema on
the other side. Re-emitting the normalized form would not, because
normalization renames keys (mcp-servers -> mcpServers).
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bagDir |
string |
Returns
Section titled “Returns”Record<string, unknown> | null
registeredName()
Section titled “registeredName()”registeredName(
manifest):string
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
manifest |
{ extends?: string; name: string; } |
manifest.extends? |
string |
manifest.name |
string |
Returns
Section titled “Returns”string
resolveBagAccess()
Section titled “resolveBagAccess()”resolveBagAccess(
source):BagAccessLevel
Resolve a bag’s effective access level, honoring the legacy disabled
boolean for back-compat. An explicit access field wins.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
source |
BagSource |
Returns
Section titled “Returns”resourceKey()
Section titled “resourceKey()”resourceKey(
bag,name):string
<bag>.<name> — the key consumers look up.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
bag |
string |
name |
string |
Returns
Section titled “Returns”string
validateAgainstSchema()
Section titled “validateAgainstSchema()”validateAgainstSchema(
schema,value):SchemaValidationResult
Validate a value against a JSON Schema.
Errors are pre-formatted strings rather than Ajv’s error objects: every consumer here feeds them back to an agent or an HTTP 422 body, and Ajv’s internal shape is neither stable API nor legible prose.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
schema |
Record<string, unknown> |
value |
unknown |