Skip to content

Content packs

A content pack is a versioned bundle of files and metadata an operator attaches to one or more agent roles. When a workspace starts under a role with packs attached, each pack is mounted read-only at /workspace/packs/<name>/, and what the pack ships influences the agent in three ways:

  1. Files — guides, scripts, data, and templates the agent can read or execute during a task. The agent discovers them via ls /workspace/packs/<name>/ rather than through an index; the system prompt tells the agent each pack is mounted and what kinds of files to look for.
  2. Behavioural instructions — an optional behavior.md per pack is embedded verbatim into the agent’s system prompt, so a pack can tell the agent how to use its own contents.
  3. Capabilities — the pack’s capabilities field is parsed at upload time. When the operator attaches the pack in the role editor, the pack’s capabilities are auto-copied into the role’s inline capability YAML — see Capabilities below for the full story.

Packs are the unit of reuse on this platform. A slack pack drops Slack-shaped capabilities, agent prompts, and helper scripts into any role that attaches it.

The on-disk layout is a small closed set of top-level entries. The build pipeline rejects anything else:

EntryWhat it’s for
pack.yamlAuthor-provided metadata: name, description, version, category, optional capabilities[], optional suggested_packs[]. Parsed at upload to populate the row + verify the slug.
behavior.mdOptional. Markdown the agent’s system prompt picks up verbatim. Use it to tell the agent how to use the pack — preferred APIs, calling conventions, output formats.
guides/Optional. Markdown documents the agent reads on demand. The system prompt explicitly suggests fetching guides before calling unfamiliar APIs.
scripts/Optional. Ready-to-run scripts the agent can execute via its bash tool.
data/Optional. Static data files — configs, schemas, seed data.
templates/Optional. Reusable templates the agent fills in — request bodies, config files, structured-output skeletons.

A pack’s total uncompressed size is capped at 100 MB. The upload pipeline strips OS-generated metadata files automatically and rejects symlinks that point outside the pack directory.

The customer-visible fields on a pack row:

FieldPurpose
idStable pack identifier. This is the value a role’s pack_ids[] references; role_packs.pack_id is the foreign key.
org_idOwning org. NULL for platform-shipped standard packs. API responses redact this to null for any pack the caller doesn’t own — an owned boolean on the response is the single signal the UI uses for the my-org / other-orgs split.
nameSlug. (org_id, name) is unique. Used for name-based lookups — suggested_packs resolution and the community-catalog name dedupe.
display_name, description, versionFrom pack.yaml. Surfaced on the pack card and in the agent’s pack-listing prompt.
tierrecommended / verified / community — see Tiers.
visibilityorg / community — see Visibility.
categoryClosed-enum slug — see Categories.
capabilitiesYAML of capability suggestions the pack ships — see Capabilities.
suggested_packsComma-separated names of other packs this one pairs well with. Surfaced as suggestions in the editor.
image_originupload (platform built + pushed the image) or registry (operator supplied an external image ref) — see OCI packaging.
image_refFull OCI image reference, including tag.
image_digestThe digest the workspace pod actually mounts — see Digest pinning.
size_bytesTotal uncompressed pack size, captured at build time.
created_at, updated_atBookkeeping.

A pack’s tier is a curation signal — which orgs can see it is a separate concern, handled by visibility.

TierMeaning
recommendedPlatform-recommended. Auto-selected as a starting point for new roles. Today only progress-tracking carries this tier.
verifiedReviewed by the platform team. All other standard packs ship at this tier.
communityThe default for customer-authored packs. No platform review.

Customer-authored packs always start at community. Tier is not customer-adjustable, and recommended / verified are reserved for platform-shipped packs — your org never owns a pack in those tiers (cloning a standard pack lands the copy back at community).

The tier also gates editability: per-file editing in the console is allowed only on community-tier packs, which is every pack your org can own.

Visibility decides which orgs see a pack.

VisibilityWho sees it
org (default)Only the owning org. Hidden from everyone else.
communityVisible across all orgs in the platform’s community pool.

Standard packs ship at visibility=community so every org sees them in their catalog.

A pack the org owns can be flipped between org and community from the console (the people / building icon on the pack card). Flipping to community is blocked if another org already shares a community pack with the same name — community names are unique platform-wide because the catalog renders by name.

The closed-enum category slugs:

communication, monitoring, security, cloud, databases, data-warehouses, developer-tools, crm, productivity, storage, tools, scenarios, general.

Author-declared in pack.yaml; unknown values rejected at save time. Defaults to general when pack.yaml doesn’t set one. Drives the category-chip filter on the Content Packs page.

Packs are stored as OCI images in the platform’s image registry. Two paths get a pack into the registry:

  • upload: the operator hands the platform a .zip / .tar / .tar.gz of the pack contents (or builds it from the in-console editor). The platform validates the structure (allowed top-level entries, size cap, symlink safety), builds a FROM scratch image with a single tar layer, and pushes it to {registry}/{org-id}/{pack-name}:{version} (plus a :latest tag for convenience). The upload form’s metadata fields override what’s in the supplied pack.yaml, so the operator can fix the description or category at upload time without re-bundling. If no version is provided, the platform fingerprints the contents and synthesises one (sha-<hash>).
  • registry: the operator supplies an existing OCI image reference they pushed themselves. The platform doesn’t build; it reads the image manifest and records its metadata. Useful for CI pipelines that build pack images independently.

A pack’s repo path accumulates a tag per version on re-upload. Delete cleans up every version under the path so a deleted pack doesn’t leave behind storage in the registry.

Every pack has a tag ({repo}:v1) and a digest — a SHA256 fingerprint of the exact bytes that were pushed ({repo}@sha256:...). The tag is a label that can be re-pointed; the digest names one specific build, forever.

When the platform builds an agent pod, it mounts each pack by its digest, not by its tag. The digest it uses is whatever the platform recorded the last time that pack was uploaded.

Two things follow from that.

Re-uploading a pack moves the recorded digest. When you re-upload (or the platform re-pushes a standard pack at a new version), new bytes go to the registry, a new digest gets captured, and the pack row updates. From that point on, new pods — pool refills, fork claims, replacements after a restart — pull the new bytes. Pods that were already running keep their previous mount; Kubernetes never swaps a mounted volume under a running pod regardless of how the reference was written. They keep serving the old bytes until they’re restarted.

Registry-side tag tampering doesn’t reach new pods. If someone re-pushed bytes under the same tag without going through the platform’s upload flow, the pack row would still hold the previous digest, so new pods would still pull the previous bytes. Tag re-pushes that don’t update the platform’s record have no effect on what gets mounted.

To make drift visible, every pod is stamped at creation with an annotation listing the digests it actually mounted. When the console reads a workspace row, the platform compares that annotation against the role’s current pack digest set:

  • current — annotation matches the current digest set.
  • stale — annotation differs from the current digest set. Something changed (a pack was re-uploaded, the role’s pack_ids[] was edited, etc.) and the running pod is on the previous content. The console renders a Stale packs badge on the workspace card and detail page; restart the workspace to pick up the new digests.
  • unknown — no live pod to read an annotation from (workspace stopped, provisioning, just-failed). The signal is meaningless without a pod, so the field is suppressed rather than guessed.

The pool also enforces this end of things: a pool pod whose annotation doesn’t match the role’s current digest set is rejected at claim and deleted by a periodic sweep, so a new workspace can’t be born stale.

Three streams flow from a pack into the agent’s runtime context.

The pack listing. The agent’s system prompt names every mounted pack, with its mount path under /workspace/packs/ and its description from pack.yaml. The agent always knows the full set of mounted packs without having to discover them.

behavior.md, if present, is included in the system prompt verbatim and attributed to its pack, so the agent can tell whose instructions are whose. This is how a pack tells the agent how to use what it ships — preferred APIs, calling conventions, error-handling guidance.

The pack’s files are not pre-indexed or summarised. The system prompt tells the agent each pack may contain guides/, scripts/, data/, templates/ and to explore them via bash (ls, cat, etc.). The agent discovers what’s useful at task time rather than carrying the index in the prompt.

A pack’s pack.yaml can declare a capabilities[] block — full capabilities with allow rules, transforms, and budget counters, just like a role’s inline YAML. The platform parses and validates them at upload using the same validator that runs against role-inline YAML, so anything that’s legal on a role is legal in a pack.

What happens when an operator attaches the pack in the role editor: the pack’s capabilities are auto-copied into the role’s in-memory capability list. Capabilities that aren’t already on the role (by name) get added; the editor opens the Templates panel and the matching capability suggestions glow briefly so the operator sees what just landed. From there, the operator can edit, refine, or remove the capabilities before saving the role.

The model is worth being precise about:

  • Pack capabilities aren’t a separate runtime merge tier. At workspace activate-time, the proxy compiles the provider’s capabilities merged with the role’s, with role caps overriding provider caps by name. Pack capabilities end up in the role’s own YAML at editor-save time; from then on they look like any other role capability to the runtime.
  • Detaching a pack doesn’t undo the auto-copy. When the operator toggles a pack off, the pack’s mount disappears from future workspaces, but the capabilities that were copied into the role stay until the operator removes them explicitly. If you tweaked a pack’s capability after attaching, detaching shouldn’t throw away your edits.

Alongside the auto-copy of the attached pack’s own capabilities described above, the role editor also cross-suggests neighbouring things in both directions:

  • Attach a pack → the editor opens the Templates panel and briefly glows any capabilities whose domains overlap with the pack’s. So if you attach a Slack pack, related capabilities (other packs’ Slack caps, built-in Slack templates) light up so you can see what else is available against the same domain.
  • Add a capability with a new domain → the editor opens the Content Packs panel and briefly glows any packs whose capabilities overlap that domain. So if you start authoring a capability against slack.com, the Slack pack glows in the panel as a “you might want to attach this whole pack instead of writing one capability at a time” hint.

The cross-suggestions don’t auto-apply — they’re nudges.

The suggested_packs field on pack.yaml is a comma-separated list of other pack names the author thinks pair well with this one. When such a pack is attached to a role, the Content Packs panel highlights any of its suggested_packs that aren’t yet attached, with a small suggested badge and a “Suggested by {pack}” tooltip. Standard packs use it sparingly — e.g. the search packs (google-search, duckduckgo-search, arxiv-search) suggest web-fetch so the agent can both find and read pages; image-gen suggests image-reader. These are also nudges, not auto-attach.

The platform ships a set of standard packs as platform-owned, community-visible rows that every org sees in its catalog. When the platform releases a new version of one, your org picks it up automatically — no re-upload needed.

The current catalog, grouped by category:

CategoryPacks
Generalprogress-tracking, change-tracking
Cloudazure, gcs
Communicationslack, sendgrid, discord
CRMhubspot
Data warehousesbigquery, databricks
Databaseselasticsearch, airtable
Developer toolsgitlab, gitlab-git, github, github-git, jira, linear, go-install, node-install, pip-install
Monitoringpagerduty, grafana, splunk, newrelic
Productivitynotion, confluence, gmail, google-drive, google-sheets, google-calendar, microsoft-graph
Securitynvd, virustotal
Scenariossecops-triage, incident-response, devops-monitoring
Newsnrk-news
Web fetch & searchweb-fetch, duckduckgo-search, google-search, arxiv-search
Documentspdf-reader, powerpoint, excel-finance
Mediaimage-gen, image-reader, elevenlabs, openai-tts
Socialreddit, social-media-content

Every standard pack ships at verified tier except progress-tracking, which is recommended and auto-attaches to new roles. Both progress-tracking and change-tracking get end-to-end walkthroughs below — they’re the two general-category packs that ship with the platform and they show how a pack’s guide + watcher + activity event line up into a console feature.

Progress tracking — an example end-to-end

Section titled “Progress tracking — an example end-to-end”

The progress-tracking standard pack is the most useful pack to look at if you want to see how all three streams fit together.

What it ships:

  • A behavior.md telling the agent to plan its task as a series of named steps under /workspace/.progress/ before doing anything else.
  • A guide describing the step-file convention the agent should follow (file naming, the shape of current.json, when to update it).
  • Templates for current.json and individual step files so the agent fills in pre-shaped files instead of inventing them.
  • No capabilities and no credentials — this pack is purely about how the agent organises its own work.

What this produces at runtime:

  1. The agent reads the guide, plans its steps, and writes step files into /workspace/.progress/ plus a current.json that names the active step.
  2. The agent service’s progress watcher tails /workspace/.progress/ and emits a step_status activity event per step file change and a progress_status activity event per current.json change. (See Audit events — agent events for the wider activity-event picture.)
  3. The console’s progress tracker renders those events as a live step list and a “what’s happening right now” line in the workspace’s activity view.

That’s why progress-tracking is the recommended-tier default for new roles: it costs nothing (no external API access, no credentials) and unlocks the live progress visualisation that the rest of the console builds on. New roles created from the template picker attach it automatically.

The change-tracking standard pack mirrors the progress-tracking recipe but for reviewable file edits.

What it ships:

  • A behavior.md telling the agent to record every file edit as a JSON entry under /workspace/.changes/ so the activity panel can render it as an inline diff.
  • A guide describing the change-record convention (file naming, the kind enum, when to write summary vs diff, the staging directory for proposals, the before/ snapshot directory for revert support).
  • A template change.json so the agent fills in pre-shaped records instead of inventing them.
  • No capabilities and no credentials — like progress-tracking, this pack is purely about how the agent organises its own reviewable output.

What this produces at runtime:

  1. The agent reads the guide and writes a JSON record to /workspace/.changes/<N>_<slug>.json after every file edit. kind is one of create, modify, delete, rename, or proposal. Proposals stage the new content under /workspace/.changes/staged/ instead of touching the target file directly.
  2. The agent service’s change watcher tails /workspace/.changes/ and emits a file_change activity event per record. (See Audit events — agent events for the wider activity-event picture.)
  3. The console’s activity panel renders each file_change event as an inline diff — kind tag (create / modify / delete / rename / proposal), the path (clickable into Sandbox), the optional one-line summary, and the unified diff color-coded per line. The file path opens the file in the workspace’s Sandbox view for the wider context.

Pair change-tracking with progress-tracking for a role that’s both steerable (the user sees the plan unfold) and reviewable (every edit shows up as a diff in the activity log).

A pack the org doesn’t own (community pack from another org, or a standard pack) can be cloned into your org from the pack card’s Clone button. Clone:

  • Pulls the source image’s bytes, rebuilds them into a new OCI image owned by your org at {registry}/{your-org}/{name}:{version}.
  • Copies metadata across — display name, description, version, category, capabilities, suggested packs.
  • Forces visibility=org and tier=community on the new pack, regardless of what the source carried. So a cloned slack pack is private to your org and editable as a community-tier pack.
  • Accepts an optional name body field to rename on clone (so you can fork a community pack without shadowing the original).

For a pack you own:

  • Toggle visibility between org and community (the people / building icon on the pack card).
  • Upload a new version by re-running the upload flow. The new tar becomes the next :<version> tag on the registry repo, image_digest updates, version bumps to whatever the new pack.yaml says (or a content-hash fingerprint if empty).
  • Delete removes the row and cascades through role_packs (any role that referenced this pack loses the attachment in the same transaction — so a role’s pack list is always self-consistent), then cleans up every version of the image from the registry.

Pack deletion doesn’t show up as a “missing pack” chip on the role anywhere — the cascade above keeps the role’s pack list self-consistent, so there’s nothing dangling to flag. Other pack changes (re-upload, role pack-set edits) surface through the stale-pack signal — see Stale-pack signal above and Workspaces — lifecycle for the restart that picks up the new digests.

For community-tier upload-origin packs you own, the console exposes a per-file editor: click the pack card to expand its file tree, click the pencil icon on any file to edit, and Save triggers a re-build + push of the OCI image with the change folded in.

Editing is not available on:

  • recommended / verified tier packs (platform-shipped — not editable by any customer org).
  • registry-origin packs (the operator owns the source image externally; updates have to go through their own build pipeline).
  • Packs the org doesn’t own — clone first.