#16761: sessions_spawn: inline attachments + redaction + hardening
agents
size: L
Cluster:
Model Management Enhancements
Fixes #17214
Image-specific request remains tracked in #16344 as a special case of general file attachments.
## Summary
This Pull Request introduces robust support for passing inline attachments to sub-agents via `sessions_spawn`. This enables more complex workflows where agents require specific files, such as images for analysis (e.g., in response to a user's request to "analyze this image"), code snippets, or other structured data, making it a more comprehensive solution for inter-agent communication.
This feature moves beyond basic text exchange, addressing a crucial need for agents to operate on concrete file inputs.
### Core Idea
The goal is to allow a spawning agent (or user) to provide files directly with a `sessions_spawn` call. These files are then safely materialized into the target sub-agent's isolated workspace, making them accessible to the sub-agent's tools and thought process. This allows for scenarios like:
- An agent analyzing an image provided by a user.
- An agent receiving a configuration file for processing.
- An agent getting a dataset for a specific task.
### Implemented Details
- Adds `attachments[]` support to `sessions_spawn` (accepts inline content as `utf8` or `base64` encoded strings).
- Materializes attachments into the child sub-agent's isolated workspace at a deterministic relative path:
- `.openclaw/attachments/<uuid>/`
- Includes a `.manifest.json` file within this directory, containing metadata for all attached files (name, size, SHA256 hash).
- Returns an attachment receipt (`count`, `totalBytes`, `files`, `relDir`) in the `sessions_spawn` tool result, allowing the spawning agent to confirm successful transfer.
- Appends a system note to the child sub-agent's context, informing it of the canonical `relDir` where attachments can be found (the `mountPath` concept is explicitly reserved for future, more advanced mount-based implementations and is not used as a runtime path authority in this implementation).
### Security and Robustness Hardening
Extensive measures have been taken to ensure the security and integrity of attachment handling:
- **Strict Transcript Redaction**: `sessions_spawn` attachment `content` is strictly redacted from transcript persistence (both `arguments` and `input` tool-call shapes) to prevent accidental logging of sensitive data.
- **Robust Base64 Validation**: Implements strict base64 validation (checking alphabet, padding, and roundtrip consistency). Critically, it includes a **pre-decode encoded-size guard** to prevent memory exhaustion (DoS) from overly large base64 inputs before full decoding.
- **Atomic Materialization & Rollback**: Attachment staging includes atomic file writes and a comprehensive rollback mechanism: if any error occurs during materialization (e.g., validation failure, disk write issue), the partially created attachment directory is fully removed.
- **Spawn Failure Cleanup**: If the `agent.callGateway` (the actual sub-agent spawn) fails after attachments have been materialized, the staged attachment directory is removed according to the defined cleanup policy.
- **Lifecycle-Aware Cleanup**: The cleanup policy (`cleanup=delete` or `retainOnSessionKeep=false`) is fully wired into the sub-agent registry's `finalize` process, ensuring attachment directories are reliably removed after the sub-agent run completes or its session is archived.
- **Constrained Deletion**: Attachment directory deletion is strictly constrained to the canonical attachments root within the child sub-agent's workspace, preventing accidental deletion of files outside the intended scope (path traversal protection).
- **Tightened File Permissions**: File system permissions for materialized attachments are highly restricted: directories are set to `0700` and files/manifests to `0600`, ensuring only the owner (the sub-agent process) can access them.
### Tests Run
The following tests have been run and passed:
- `pnpm lint --silent` (static code analysis)
- `pnpm exec vitest run --config vitest.unit.config.ts src/agents/session-transcript-repair.attachments.test.ts` (unit tests for transcript redaction and base64 handling)
- `pnpm exec vitest run --config vitest.e2e.config.ts src/agents/openclaw-tools.sessions.e2e.test.ts` (end-to-end tests for `sessions_spawn` functionality)
---
## Reviewer Guide
**What to look for:**
1. **Security (Critical):**
* **`sessions_spawn-tool.ts`**:
* `decodeStrictBase64` logic: Confirm the `maxEncodedBytes` calculation and its enforcement *before* `Buffer.from` to prevent memory DoS.
* Attachment file/dir creation (`fs.mkdir`, `fs.writeFile`): Verify `mode` (0700/0600) and `flag: "wx"` (no overwrite).
* Error handling logic in the attachment materialization `try/catch` block: Ensure `fs.rm(absDir, { recursive: true, force: true })` is called on *any* failure to prevent residue.
* **`subagent-registry.ts`**:
* `safeRemoveAttachmentsDir`: Verify the `fs.realpath` and `startsWith(rootWithSep)` checks are robust against path traversal for deletion.
* `finalizeSubagentCleanup`: Ensure `shouldDeleteAttachments` logic correctly triggers `safeRemoveAttachmentsDir` based on `cleanup` and `retainAttachmentsOnKeep`.
* **`session-transcript-repair.ts`**:
* `sanitizeToolCallInputs`: Confirm `attachments[].content` is redacted for both `arguments` and `input` fields in `sessions_spawn` tool calls.
2. **Correctness (High):**
* **`sessions_spawn-tool.ts`**:
* `decodeStrictBase64`: Test with various valid, invalid, and edge-case base64 strings (e.g., malformed padding, non-base64 chars, empty, very long inputs).
* Attachment limits (`maxFiles`, `maxTotalBytes`, `maxFileBytes`): Ensure these are correctly enforced at various stages.
* Filename validation: Check edge cases for `.` `..` `/` `\` `\\^@` and ensure they are rejected.
* `relDir` generation: Confirm it is deterministic and reliably points to the attachment location in the child workspace.
* System prompt message: Verify it clearly states the `relDir` and the untrusted nature of attachments.
* **`subagent-registry.ts`**:
* `registerSubagentRun`: Ensure `attachmentsDir` and `retainAttachmentsOnKeep` are correctly recorded.
* `finalizeSubagentCleanup`: Verify the timing and conditions for `safeRemoveAttachmentsDir` are correct (i.e., it doesn't try to delete before creation, and doesn't leave residue).
3. **Architecture / Design (Medium):**
* **`sessions_spawn-tool.ts`**:
* Confirm that the `attachAs.mountPath` field is indeed *not* used for path resolution in the MVP, but retained as a placeholder (and documented as such in the code comments).
* Attachment lifecycle (creation, use, cleanup) is clear and robust.
* **Overall**: Ensure that this inline attachment mechanism is a solid foundation for future extensions (e.g., streaming, more complex storage).
**Suggested Manual Test Cases:**
1. **Successful Attachment**: Spawn a sub-agent with a small UTF8 text file. Verify the sub-agent can `read` the file at the reported `relDir`.
2. **Successful Base64 Image**: Spawn a sub-agent with a base64 encoded image (within limits). Verify the sub-agent can `read` the file (e.g., check its size or first few bytes).
3. **Attachment Limits**:
* Send a single file exceeding `maxFileBytes`. Expect rejection.
* Send multiple files whose `totalBytes` exceed `maxTotalBytes`. Expect rejection (even if individual files are fine).
* Send more than `maxFiles`. Expect rejection.
4. **Invalid Base64**: Send an attachment with malformed base64 `content`. Expect `attachments_invalid_base64_or_too_large` error.
5. **Invalid Filenames**: Send attachments with `../` `//` `\` `\\^@` or null bytes in the `name`. Expect `attachments_invalid_name` error.
6. **Duplicate Filenames**: Send multiple attachments with the same valid `name`. Expect `attachments_duplicate_name` error.
7. **Rollback on Materialization Error**: Simulate a disk full or permission error during file writing (manually block `fs.writeFile` in a test environment, if possible). Verify that no residual attachment directory or files are left.
8. **Spawn Failure Cleanup**: Trigger `sessions_spawn` with valid attachments, but ensure the actual sub-agent spawn (`callGateway<...>(method: "agent", ...)` fails (e.g., invalid `agentId`). Verify that the attachment directory is removed.
9. **`cleanup=delete`**: Spawn an agent with attachments and `cleanup="delete"`. Verify the attachment directory is removed after the sub-agent completes successfully.
10. **`cleanup=keep` (default)**: Spawn an agent with attachments and `cleanup="keep"`. Verify the attachment directory *is not* removed after the sub-agent completes.
Mentions: #16344
## AI Assistance & Contributing Transparency
- [x] AI-assisted: yes (Codex/Claude)
- [x] Testing level: lightly tested (unit/e2e targeted + CI)
- [x] Prompts/session logs: Session logs available on request
- [x] I understand what this code does and can maintain it
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds inline attachment support to `sessions_spawn`, enabling sub-agents to receive files (UTF-8 or base64) materialized into isolated workspace directories. Changes include comprehensive security hardening: pre-decode size validation, strict base64 verification with roundtrip checks, atomic file writes with rollback, transcript redaction of attachment content, and path-traversal-protected cleanup lifecycle.
- Attachments materialized to `.openclaw/attachments/<uuid>/` with deterministic paths and 0700/0600 permissions
- Base64 validation includes pre-allocation size guard (`maxEncodedBytes * 2` check before regex) and post-decode verification
- UTF-8 path now pre-checks `Buffer.byteLength(content, "utf8")` before allocation to prevent memory...
Most Similar PRs
#16247: feat(agents): declarative agent definitions for sessions_spawn
by zerone0x · 2026-02-14
69.5%
#20072: feat(sessions_spawn): add sessionKey param to reuse sub-agent sessions
by Be1Human · 2026-02-18
67.4%
#9049: fix: prevent subagent stuck loops and ensure user feedback
by maxtongwang · 2026-02-04
67.0%
#20480: feat(slack): extract and surface attachment notes in messages
by olyashok · 2026-02-19
66.7%
#13331: feat(sessions_spawn): add sessionKey param to reuse sub-agent sessions
by Be1Human · 2026-02-10
66.3%
#21035: feat: agent hardening — modifying after_tool_call hook, cron contex...
by roelven · 2026-02-19
65.1%
#10748: feat: Add sessions.spawn gateway method for direct subagent spawning
by fox-openclaw · 2026-02-06
65.0%
#6875: test: add coverage for sessions_spawn model + agentId (#6817)
by whoknowsmann · 2026-02-02
65.0%
#13167: feat(agents): dispatch Claude Code CLI runs as persistent, resumabl...
by gyant · 2026-02-10
64.7%
#23506: feat(spawn): per-spawn tool permissions for sessions_spawn
by dissaozw · 2026-02-22
64.3%