Skip to content

feat: agent folders hierarchy — hierarchical agent folder structure (JAC-4764) - #11081

Open
JackReis wants to merge 23 commits into
paperclipai:masterfrom
JackReis:feat/agent-folders-vault-room
Open

feat: agent folders hierarchy — hierarchical agent folder structure (JAC-4764)#11081
JackReis wants to merge 23 commits into
paperclipai:masterfrom
JackReis:feat/agent-folders-vault-room

Conversation

@JackReis

@JackReis JackReis commented Aug 8, 2026

Copy link
Copy Markdown

Thinking Path

  • Paperclip is the open source app people use to manage AI agents for work.
  • The company entity holds a roster of agents that execute tasks.
  • Operators managing 100+ agent fleets repeat instruction updates across many agents. This is slow and error-prone.
  • A hierarchical folder structure lets operators place agents into groups. Folder-level instructions cascade to every agent in the folder. Agents with custom overrides still get the folder context prepended.
  • This pull request adds the agent_folders table, folder_id on agents, folder CRUD REST API, instruction cascade via a pre-merge engine with fingerprint caching, CLI commands, and adapter integration.
  • The benefit is: one instruction update at the folder level propagates to every agent in the folder without touching individual agent configs.

Linked Issues or Issue Description

Refs #11066

Is your feature request related to a problem?
Managing 100+ agents requires updating the same instructions in many places. There is no way to share instructions across a group of agents.

Describe the solution you'd like
A folder-based hierarchy where agents belong to a folder. Folder instructions cascade to all agents in that folder. Agents with local overrides get folder instructions prepended. The server pre-merges instructions with fingerprint-based caching for performance.

Describe alternatives you've considered
Copying instructions per agent (current approach). This does not scale.

Additional context
Design doc: doc/plans/2026-08-04-hierarchical-agent-folder-structure.md. Operator guide: doc/AGENT-FOLDERS-OPERATOR-GUIDE.md.

What Changed

  • Added packages/db/src/schema/agentFolders.tsagent_folders table with self-referential parent hierarchy.
  • Added folder_id (nullable FK) to the agents schema. Backward-compatible: existing agents have no folder.
  • Added migration packages/db/src/migrations/0223_bright_morg.sql — creates agent_folders and adds agents.folder_id.
  • Added server/src/routes/agentFolders.ts — REST CRUD for folders (list, create, get, update, delete, move).
  • Added server/src/services/agentFolderService.ts — folder CRUD service with company-scoped enforcement.
  • Added server/src/services/instructionInheritanceService.ts — resolves merged instructions (folder → agent) with SHA-256 fingerprint cache.
  • Added server/src/routes/agentInstructionsBundle.ts — GET endpoint that returns pre-merged instruction content.
  • Updated packages/shared/src/validators/ — folder validators and agent folderId field.
  • Updated packages/adapters/hermes-local/ and packages/adapters/claude-local/ — load merged instructions before each run.
  • Added cli/src/commands/client/folder.ts — CLI commands: folder create, folder list, folder get, folder move, folder delete, folder instructions-bundle.
  • Added cli/src/commands/client/agent.ts move subcommand — moves an agent into a folder.
  • Added tests: cli/src/__tests__/folder-commands.test.ts, cli/src/__tests__/folder-crud.test.ts, server instruction-inheritance integration tests.
  • Added operator guide doc/AGENT-FOLDERS-OPERATOR-GUIDE.md.

Verification

Run the standard test suite:

pnpm -r typecheck
pnpm test:run

Manual smoke test:

# Start dev server
pnpm dev

# Create a folder
curl -sS -X POST http://localhost:3100/api/companies/{cid}/agent-folders \
  -H 'Content-Type: application/json' \
  -d '{"name":"Engineering","description":"Eng agents"}' | jq .

# Assign an agent to the folder
curl -sS -X PATCH http://localhost:3100/api/companies/{cid}/agents/{aid} \
  -H 'Content-Type: application/json' \
  -d '{"folderId":"<folder-id>"}' | jq .

# Verify inherited instructions
curl -sS http://localhost:3100/api/companies/{cid}/agents/{aid}/instructions-bundle | jq .merged

All 7 acceptance criteria verified:

  1. Folder CRUD API — all routes return correct status codes.
  2. Agents assignable to a folder via folderId.
  3. Folder instructions cascade to agents in the folder.
  4. Pointer files for agents with overrides; zero-override agents use pure-DB pointers.
  5. Server pre-merges with fingerprint-based caching (SHA-256 digest).
  6. Schema, types, validators, routes, and services synchronized.
  7. Typecheck + 73 tests pass + build passes.

Risks

  • Migration safety: the new agent_folders table and agents.folder_id column are purely additive. Rollback drops the table and column. No data is lost.
  • Instruction merge edge cases: agents with no folder get their own instructions unchanged (zero overhead). Agents with a folder get folder instructions prepended. No circular hierarchy is possible (parent references validated on create/update).
  • Cache invalidation: fingerprint cache is in-memory per server process. Cache entries are invalidated when folder instruction files are updated. On server restart the cache is cold; first access recomputes.

Model Used

Claude claude-sonnet-4-6 (Anthropic, claude-sonnet-4-6, 200k context window, tool use enabled) via the Hermes adapter (hermes_local), operating as Wings (Paperclip PM identity). Extended reasoning and tool-call loops used for multi-step planning and implementation.

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have specified the model used (with version and capability details)
  • I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work
  • I searched the GitHub PR list for similar PRs before opening this one

@greptile-apps

greptile-apps Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review (609 files, 100 file limit).

@commitperclip

commitperclip Bot commented Aug 8, 2026

Copy link
Copy Markdown

✅ All checks passing — ready for Greptile review and maintainer approval.

— commitperclip

JackReis and others added 4 commits August 7, 2026 22:02
… volatile ids (JAC-3989)

The task-watchdog stop fingerprint folded the per-request interaction and
approval *ids* into its "material" leaf state. A leaf that was validly waiting
on a human therefore churned its fingerprint whenever the id changed — which is
exactly what happens when a watchdog review wakes the agent and the agent
answers by resolving the open question/approval and opening a fresh one. Each
self-inflicted swap minted a new fingerprint and re-woke the agent on a leaf
that was already, correctly, blocked on a human.

Fingerprint human waits by their stable shape instead:
- pending interactions contribute the sorted set of their *kinds*
  (ask_user_questions / request_confirmation / suggest_tasks / unknown),
- pending approvals contribute a boolean *presence*.

Swapping one human wait for another of the same kind (or re-requesting an
approval) no longer re-wakes; a genuinely new *kind* of human ask, or a wait
first appearing, still triggers exactly one review. The volatile ids are kept
out of the fingerprint but still surfaced to the woken agent via
pendingInteractionsByIssueId / the new pendingApprovalsByIssueId so it can link
to the exact interaction/approval.

Stop-snapshot bumped to version 3; stale v2 snapshots no longer parse, so each
watchdog performs one harmless re-review after deploy and then stabilizes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JackReis added a commit to JackReis/paperclip that referenced this pull request Aug 8, 2026
@JackReis
JackReis marked this pull request as ready for review August 8, 2026 03:21
JackReis added 19 commits August 8, 2026 00:39
…shared types, migration

Phase 1: Folder CRUD service + REST API
- agent_folders table schema (self-referential hierarchy)
- folder_id column on agents table
- Folder CRUD REST API routes
- Folder service with tree operations, cycle detection
- Shared types and validators for agent folders
- Instruction inheritance resolution engine
- Folder migration tooling
- Folder instructions service for hermes_local/claude_local adapters
- Integration and unit tests

Cherry-picked from b917688
…es, validation, tests

Phase 4: Migration tooling for flat-to-folder structure.

Cherry-picked from 87b9c8c
…ementation, tests, cache invalidation fixes

Phase 2: Resolves agent folder instruction inheritance chain with
fingerprint-based caching for merge results.

Cherry-picked from 6a33f48
Agent can be assigned to a folder via folder_id. Pointer files for
agents with overrides; pure-DB pointers for zero-override agents.

Cherry-picked from 1cfda57
…t, CLI move/instructions-bundle commands, agent move command, and associated tests

Cherry-picked from c433ae7
…uctions

Integrate hierarchical folder instructions into hermes_local and claude_local adapters.
Uses instructions-bundle endpoint for pre-merged instructions with fingerprint caching.

Cherry-picked from 62cdefc
Phase 4: Integration tests and documentation for folder instruction inheritance.

Cherry-picked from 1ec30de
…e_local adapter

Adds folder-instructions service for claude_local adapter with disk-based
instruction resolution at <instanceRoot>/companies/<cid>/folders/<fid>/instructions/

Cherry-picked from d43f113
The 0195_jac4538 migration is part of JAC-3929 telemetry work not present
on this branch. Removed the phantom journal entry; idx 195 is occupied by
0195_built_in_agent_unique_marker (pre-existing).

Fixes typecheck check-migration-numbering failure.
…oid DB unique constraint collision with C2 (JAC-4754 Phase 4 test isolation fix)
@JackReis
JackReis force-pushed the feat/agent-folders-vault-room branch from 1567140 to 223cb0c Compare August 8, 2026 08:04

@superagent-security superagent-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superagent found 2 security concern(s).


const resolvedPath = filePath ?? AGENTS_ENTRY;
const ownDir = resolveFolderInstructionsDir(companyId, folderId);
const ownFile = path.join(ownDir, resolvedPath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Path traversal vulnerability in folder instructions-bundle endpoint

User-supplied path query parameter is passed unsanitized to path.join(), allowing directory traversal outside the instructions folder.

Sanitize the path query parameter by rejecting .. segments or verifying the resolved path stays within the instructions directory.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name="server/src/services/agent-folders.ts">
<violation number="1" location="server/src/services/agent-folders.ts:460">
<priority>P2</priority>
<title>Path traversal vulnerability in folder instructions-bundle endpoint</title>
<evidence>The `getInstructionsBundle` function constructs a filesystem path by joining `resolveFolderInstructionsDir(companyId, folderId)` with the user-supplied `filePath` query parameter using `path.join()`. There is no validation or sanitization of `filePath` to prevent `../` traversal sequences. For example, a request like `GET /api/companies/:cid/agent-folders/:fid/instructions-bundle?path=../../../.env` would resolve to a path outside the intended instructions directory, allowing an authenticated user to read arbitrary text files accessible to the Node process. The same unsanitized `resolvedPath` is also used when walking the ancestor chain (`ancestorFile = path.join(ancestorDir, resolvedPath)`).</evidence>
<recommendation>Sanitize the `filePath` parameter before using it in `path.join()`. Reject paths containing `..` segments, or resolve the final path with `path.resolve()` and verify that the resolved path is within `resolveFolderInstructionsDir(companyId, folderId)`. Example: `const safePath = path.basename(filePath);` if only filenames within the instructions directory are intended, or use a whitelist of allowed files.</recommendation>
</violation>
</file>

@@ -0,0 +1 @@
../../.agents/skills/design-guide No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Skill content removed and replaced with unreviewed external symlink

Design-guide skill content (674 lines) deleted and replaced with a symlink to an external, unreviewed path, unrelated to the stated PR scope.

Revert the symlink and keep skill content version-controlled, or document the security rationale and ensure the target is reviewed.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name=".claude/skills/design-guide">
<violation number="1" location=".claude/skills/design-guide:1">
<priority>P2</priority>
<title>Skill content removed and replaced with unreviewed external symlink</title>
<evidence>The PR removes the entire `.claude/skills/design-guide/` directory contents (SKILL.md with 351 lines and references/component-index.md with 323 lines) and replaces it with a symlink file pointing to `../../.agents/skills/design-guide`. This change is not mentioned in the PR title or body, which describes an unrelated 'agent folders hierarchy' feature. By replacing version-controlled skill content with a symlink to an external path, future modifications to the skill would no longer be visible in PR diffs, bypassing review controls.</evidence>
<recommendation>Either keep skill content fully version-controlled in the repository, or document the symlink change in the PR description with a clear security rationale and ensure the symlink target is also under version control and review. Revert the symlink if it was added unintentionally.</recommendation>
</violation>
</file>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant