# Packets

A **packet** is a read-only bundle of reference files attached to a mission. Use it when you want agents to be able to read documentation, knowledge bases, examples, prompt libraries, or any other text-only material that should never change during the run.

Packets are the read-only counterpart to [memory and scratchpad slots](/missions/folders). Memory slots are writable workspaces with Squadron-managed paths; packets point at *user-controlled* directories and are immutable to agents.

| Capability | Packet | Memory / Scratchpad |
|------------|--------|----------------------|
| Agents can read files | yes | yes |
| Agents can write/delete | **no** | yes |
| Path | user-supplied | Squadron-derived |
| Allowed file types | UTF-8 text only | any |
| HCL parsed inside the path | **no** (excluded) | n/a |
| Tool slot name | `packet.<name>` | `<name>` / `"memory"` / `"scratchpad"` |

## Declaring a packet

Declare packets at the top level of your HCL, alongside `memory` blocks:

```hcl
packet "knowledge_base" {
  path        = "./reference/kb"
  description = "Internal product documentation"
}

packet "examples" {
  path        = "./reference/examples"
  description = "Worked examples agents can imitate"
}
```

| Attribute | Type | Description |
|-----------|------|-------------|
| `path` | string (required) | Directory path. Resolved by the project-wide rule (see below). Must already exist — packets are reference data, so Squadron never creates the folder for you. |
| `description` | string | Human-readable description shown to agents in the system prompt. |

### Path resolution

The same rule applies to every path attribute in Squadron config (currently `packet.path`, `plugin.source` local sources, and the `load()` function):

| Form | Resolves to |
|------|-------------|
| `./foo`, `../foo`, bare `foo` | The directory of the HCL file declaring the attribute |
| `@/foo` | The **project root** — the directory you pass to `squadron -c <dir>` |
| `/foo`, absolute | **Rejected** (`absolute paths are not allowed`) |

After resolution, the path **must remain inside the project root** AND must not be the project root itself. Three failure modes are rejected at parse time:

| Bad input | Error |
|-----------|-------|
| `path = "/etc"` | `absolute paths are not allowed — use a path relative to the HCL file or prefix with "@/" for the project root` |
| `path = "../../escaped"` | `escapes the project root "<project>"` |
| `path = "@/"` | `resolves to the project root "<project>" itself — config path attributes must point at a specific file or subdirectory, not the whole project` |

This means `path = "reference/kb"` works the same regardless of where you `cd`'d to before running squadron — there's no CWD dependence anywhere in the resolution. The same rule is shared with [`plugin.source`](../config/plugins#path-resolution) and [`load()`](../config/functions#path-resolution).

> **Why no `path = "@/"`?** The whole point of a packet is to bundle a *specific subset* of the project — and the HCL-exclusion filter drops any `.hcl` files inside the packet path. If `path = "@/"` were allowed, every `.hcl` file in your project would be filtered out and squadron would silently load with zero missions. The explicit error tells you exactly what went wrong.

Names must not collide with the reserved `memory` / `scratchpad` slot names and follow the same character rules as memory names (no path separators, no leading dot).

## Attaching packets to a mission

A mission opts in by listing the packets it cares about. The same namespace works at the task level when a particular task wants to declare its own packet dependencies:

```hcl
mission "answer_questions" {
  commander { model = models.anthropic.claude_sonnet_4 }
  agents    = [agents.researcher]

  # Mission-wide packet — available to every task
  packets = [packets.knowledge_base]

  task "draft" {
    objective = "Draft an answer using the KB."
    # Task-level packet — declared per task
    packets   = [packets.examples]
  }
}
```

Both mission-level and task-level references are validated at load time; an unknown packet name fails the parse.

> **Task-level declarations are not runtime access boundaries.** Every task in a mission shares the same mission-wide slot store, so a packet referenced by *any* task is accessible to *every* task in that mission. Use task-level `packets = [...]` to document intent, not for isolation.

## Reading from a packet

Once a packet is attached, agents access it through the same file tools as memory slots. The `slot` parameter takes the **prefixed name** `packet.<name>` so packets cannot be confused with writable slots:

```json
{
  "slot": "packet.knowledge_base",
  "path": "billing/refunds.md"
}
```

The six file tools apply the following policy for packet slots:

| Tool | Behavior in a packet slot |
|------|---------------------------|
| `file_list` | Lists files normally |
| `file_read` | Returns text content. Rejects files that look binary (NUL byte in the first 8 KB) |
| `file_search` | Filename regex search, recursive |
| `file_grep` | Content regex search; binary files are silently skipped |
| `file_create` | Always returns `Error: slot is read-only (packet bundles are immutable)` |
| `file_delete` | Always returns the same read-only error |

## File types

Packets are designed for UTF-8 text: Markdown, source code, JSON/YAML, prose, transcripts, anything an LLM can read directly. Binary payloads (images, audio, video, archives, compiled binaries) and non-UTF-8 encodings (e.g. UTF-16) are not supported — `file_read` rejects them up front so agents don't get a wall of garbled bytes.

If you need to expose mixed-type material, split it: keep the text in a packet and put any binaries in a writable memory slot that a pre-processor can stage on demand.

## HCL exclusion

A packet folder is treated as opaque reference data, *not* as part of the squadron config. Any `.hcl` files that happen to live inside a packet path are ignored at config load time — Squadron will not parse them, will not surface their syntax errors, and will not pick up `model`/`agent`/`mission`/etc. blocks from them.

This lets you point a packet at a folder that contains arbitrary user files without worrying about accidental collisions with the HCL loader.

## Full example

```hcl
# Two equivalent forms: "./..." anchors to this HCL file's directory,
# "@/..." anchors to the project root (the `-c` argument). For a flat
# project layout where the HCL lives at the root, the two are
# interchangeable; for a layered config (sub-HCL files in subdirectories)
# "@/" is the clearer choice for paths that should mean "from the top."
packet "playbooks" {
  path        = "@/reference/playbooks"
  description = "Customer-support playbooks (read-only)"
}

packet "tone_guide" {
  path        = "./reference/voice"
  description = "Brand voice and tone guidance"
}

mission "support_reply" {
  commander { model = models.anthropic.claude_sonnet_4 }
  agents    = [agents.support_writer]
  packets   = [packets.playbooks, packets.tone_guide]

  scratchpad = true

  task "draft" {
    objective = "Read the relevant playbook from packet.playbooks and draft a reply in the scratchpad."
  }

  task "polish" {
    depends_on = [tasks.draft]
    objective  = "Refine the draft against packet.tone_guide and save the final reply to the scratchpad."
    packets    = [packets.tone_guide]
  }
}
```
