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:
- 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. - Behavioural instructions — an optional
behavior.mdper pack is embedded verbatim into the agent’s system prompt, so a pack can tell the agent how to use its own contents. - Capabilities — the pack’s
capabilitiesfield 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.
Anatomy
Section titled “Anatomy”The on-disk layout is a small closed set of top-level entries. The build pipeline rejects anything else:
| Entry | What it’s for |
|---|---|
pack.yaml | Author-provided metadata: name, description, version, category, optional capabilities[], optional suggested_packs[]. Parsed at upload to populate the row + verify the slug. |
behavior.md | Optional. 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.
Pack fields
Section titled “Pack fields”The customer-visible fields on a pack row:
| Field | Purpose |
|---|---|
id | Stable pack identifier. This is the value a role’s pack_ids[] references; role_packs.pack_id is the foreign key. |
org_id | Owning 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. |
name | Slug. (org_id, name) is unique. Used for name-based lookups — suggested_packs resolution and the community-catalog name dedupe. |
display_name, description, version | From pack.yaml. Surfaced on the pack card and in the agent’s pack-listing prompt. |
tier | recommended / verified / community — see Tiers. |
visibility | org / community — see Visibility. |
category | Closed-enum slug — see Categories. |
capabilities | YAML of capability suggestions the pack ships — see Capabilities. |
suggested_packs | Comma-separated names of other packs this one pairs well with. Surfaced as suggestions in the editor. |
image_origin | upload (platform built + pushed the image) or registry (operator supplied an external image ref) — see OCI packaging. |
image_ref | Full OCI image reference, including tag. |
image_digest | The digest the workspace pod actually mounts — see Digest pinning. |
size_bytes | Total uncompressed pack size, captured at build time. |
created_at, updated_at | Bookkeeping. |
A pack’s tier is a curation signal — which orgs can see it is
a separate concern, handled by visibility.
| Tier | Meaning |
|---|---|
recommended | Platform-recommended. Auto-selected as a starting point for new roles. Today only progress-tracking carries this tier. |
verified | Reviewed by the platform team. All other standard packs ship at this tier. |
community | The 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
Section titled “Visibility”Visibility decides which orgs see a pack.
| Visibility | Who sees it |
|---|---|
org (default) | Only the owning org. Hidden from everyone else. |
community | Visible 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.
Categories
Section titled “Categories”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.
OCI packaging
Section titled “OCI packaging”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.gzof 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 aFROM scratchimage with a single tar layer, and pushes it to{registry}/{org-id}/{pack-name}:{version}(plus a:latesttag for convenience). The upload form’s metadata fields override what’s in the suppliedpack.yaml, so the operator can fix the description or category at upload time without re-bundling. If noversionis 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.
Digest pinning
Section titled “Digest pinning”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.
Stale-pack signal
Section titled “Stale-pack signal”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’spack_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.
How packs influence the agent
Section titled “How packs influence the agent”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.
Capabilities
Section titled “Capabilities”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.
Bidirectional discovery
Section titled “Bidirectional discovery”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.
Pack-to-pack suggestions
Section titled “Pack-to-pack suggestions”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.
Standard packs
Section titled “Standard packs”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:
| Category | Packs |
|---|---|
| General | progress-tracking, change-tracking |
| Cloud | azure, gcs |
| Communication | slack, sendgrid, discord |
| CRM | hubspot |
| Data warehouses | bigquery, databricks |
| Databases | elasticsearch, airtable |
| Developer tools | gitlab, gitlab-git, github, github-git, jira, linear, go-install, node-install, pip-install |
| Monitoring | pagerduty, grafana, splunk, newrelic |
| Productivity | notion, confluence, gmail, google-drive, google-sheets, google-calendar, microsoft-graph |
| Security | nvd, virustotal |
| Scenarios | secops-triage, incident-response, devops-monitoring |
| News | nrk-news |
| Web fetch & search | web-fetch, duckduckgo-search, google-search, arxiv-search |
| Documents | pdf-reader, powerpoint, excel-finance |
| Media | image-gen, image-reader, elevenlabs, openai-tts |
| Social | reddit, 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.mdtelling 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.jsonand 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:
- The agent reads the guide, plans its steps, and writes step
files into
/workspace/.progress/plus acurrent.jsonthat names the active step. - The agent service’s progress watcher tails
/workspace/.progress/and emits astep_statusactivity event per step file change and aprogress_statusactivity event percurrent.jsonchange. (See Audit events — agent events for the wider activity-event picture.) - 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.
Change tracking — an example end-to-end
Section titled “Change tracking — an example end-to-end”The change-tracking standard pack mirrors the
progress-tracking recipe but for reviewable file edits.
What it ships:
- A
behavior.mdtelling 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
kindenum, when to writesummaryvsdiff, the staging directory for proposals, thebefore/snapshot directory for revert support). - A template
change.jsonso 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:
- The agent reads the guide and writes a JSON record to
/workspace/.changes/<N>_<slug>.jsonafter every file edit.kindis one ofcreate,modify,delete,rename, orproposal. Proposals stage the new content under/workspace/.changes/staged/instead of touching the target file directly. - The agent service’s change watcher tails
/workspace/.changes/and emits afile_changeactivity event per record. (See Audit events — agent events for the wider activity-event picture.) - The console’s activity panel renders each
file_changeevent 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).
Cloning, sharing, and re-upload
Section titled “Cloning, sharing, and re-upload”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=organdtier=communityon the new pack, regardless of what the source carried. So a clonedslackpack is private to your org and editable as a community-tier pack. - Accepts an optional
namebody field to rename on clone (so you can fork a community pack without shadowing the original).
For a pack you own:
- Toggle visibility between
organdcommunity(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_digestupdates,versionbumps to whatever the newpack.yamlsays (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 drift vs. running workspaces
Section titled “Pack drift vs. running workspaces”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.
Editing pack contents
Section titled “Editing pack contents”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/verifiedtier 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.