Skip to content

Folders for skills and company memory, with role limits - #1154

Merged
helpcodeai merged 21 commits into
mainfrom
helpcodeai/knowledge-folders
Oct 19, 2026
Merged

helpcodeai merged 21 commits into
mainfrom
helpcodeai/knowledge-folders

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

Summary

Folders for skills and company memory, with role limits. See #1153. Builds on #1146, #1149 and #1152: until those are merged this PR also shows their commits; the changes here are in the last commit, "Add folders for skills and company memory, with role limits".

With dozens of skills and hundreds of memory items, flat lists stop working, for people and for AI clients. Companies also want some knowledge visible only to some teams. This adds one folder tree per workspace, shared by skills and the company memory (a topic like "Sales / Quotes" holds both its procedures and its facts), and lets admins limit a folder to roles.

Changes

  • Data: knowledge_folders (organization, parent, name, description, role_ids), nullable folder_id on kg_skill_suggestions and memory_items (SET NULL on folder removal). Up to 4 levels; sibling names unique (case-insensitive, checked by the service); no / in names.
  • Folders API (/api/knowledge-folders): list with counts (what the caller may see), create / rename / describe / move / delete (admin, editor), PUT :id/roles (admin, Enterprise), POST move for skills or company memory items. Deleting moves subfolders and items to the parent (a subfolder whose name is taken there gets a suffix).
  • MCP (/mcp/:serverId):
    • skills_list(query?, folder?): the folder argument (path or id) appears when folders exist; the result lists the subfolders with counts and each skill's folder.
    • Skill index: grouped by folder; when it does not fit, the root skills plus one line per top-level folder (description and count) for skills_list. Full text shows the folder next to the title. Workspaces without folders get exactly the text they get today.
    • memory_search(folder?) (company items in the folder and subfolders), memory_save(folder?) for company items (existing folders only; the description lists them). Results show each item's folder.
  • Role limits (team scope, Enterprise): a folder limited to custom roles (including roles synced from the identity provider), and everything below it, is hidden from callers without one of those roles: instructions, skills_list / skill_get, prompts, memory_search, kg_how_to_obtain, the knowledge-graph resource, the shared /mcp guide and the dashboard lists and counts. Each limited ancestor must be satisfied. Workspace admins see everything; callers without a member identity see only unlimited folders. Setting limits needs Enterprise; limits already set keep applying without it.
  • Never widening by accident (found while testing): moving a folder or items out from under a limited folder is admin-only; a limited folder that still holds items cannot be deleted; when a limited folder is deleted its subfolders inherit the limit (an already limited subfolder keeps only the roles that satisfied both, or the delete is refused).
  • Licence: EditionService.hasEnterprise / assertEnterprise(org, feature) generalise the memory gate; hasMemory / assertMemory delegate to them. Same rule: active, unexpired licence with plan enterprise, no trial or transition.
  • Audit: KNOWLEDGE_FOLDER_ACCESS_CHANGED with folder, path, from and to.
  • UI: shared folder tree (select, create, edit name / description / parent, delete, "Visible to" role editor for admins with Enterprise, lock icon for limited folders) on the AI Skills page and on the Memory page (Company memory and To review tabs); folder field when creating or editing skills and company memory.
  • Docs: docs/memory.md (Folders, Limiting folders to roles), docs/knowledge-graph.md (folders for skills).

Cloud and self-hosted behave the same; only the licence source differs.

Type

  • New feature
  • Documentation

Testing

  • Added/updated unit tests
  • Tested manually

Commands and results:

  • packages/backend: npx prisma generate, npx tsc --noEmit -p tsconfig.json: no errors
  • packages/backend: npm run lint: 0 errors (existing warnings only); eslint --fix rewrites of untouched files reverted
  • packages/backend: npx jest: 678 suites passed, 4 skipped; 20495 tests passed, 1496 skipped
  • packages/frontend: npx tsc --noEmit -p tsconfig.json: no errors; eslint on the changed files: no findings
  • Migration: prisma migrate deploy on an empty scratch database, prisma migrate diff against the schema: empty

New unit tests: tree helpers (paths, resolve by id or path, subtree, visibility filter), access (no limits, admin, inherited limits, workspace and global role grants only, no identity / non-member / deactivated), create / move / delete rules, role limits (Enterprise, known roles, audit), item moves, the widening protections, skill visibility and grouped index, skills_list by folder, memory search / save / edit with folders, and the MCP endpoint (folder access scoped to the server organization, folder argument and result).

Manual end-to-end on a local backend (scratch database, Enterprise licence row, MCP API keys, JSON-RPC over /mcp/:serverId):

  • Folders Sales, Sales / Quotes, HR; skills and company memory filed in them; duplicate sibling name refused (409); counts per folder correct.
  • Instructions: full text shows [Sales / Quotes]; index mode groups by folder. skills_list(folder: "sales") returns the subfolder and its skill; memory_search(folder: "Sales / Quotes") returns its item with the folder path.
  • Sales limited to a custom role. An editor without the role: instructions, skills_list, prompts and kg_how_to_obtain omit the Sales skill; skill_get by id or title answers not found; skills_list(folder: "Sales") is empty; memory_search does not find the Sales item; dashboard folders, skills (and counts) and company memory omit it; editing the hidden item answers 404; setting roles as editor is 403.
  • After assigning the role, the same editor sees the Sales skill, folder and memory item.
  • Widening: deleting a limited folder that holds an item → 409; the editor moving the item or the subfolder out → 403; the admin moving the item into the subfolder works; deleting the limited folder then hands its role to the subfolder.
  • Without the licence: setting roles → 403 edition_required, existing limits unchanged.
  • UI checked in the browser: folder tree with role editor on AI Skills, tree and folder field on the Memory page.

Checklist

  • My code follows the existing code style
  • I have tested my changes locally
  • I have updated documentation (if applicable)
  • All existing tests pass (backend npx jest)
  • This PR has a descriptive title

Related Issues

Fixes #1153
Depends on #1146, #1149, #1152

Generating skills deleted every pending skill in its scope, including
Agent Skills Finder suggestions awaiting approval and manual pending ones.
Skills now record their source (manual, llm, finder) and generation only
replaces its own pending llm suggestions. activeSkillsText also filters by
organization, and the skill endpoints validate their bodies with DTOs.

Intent capture was one switch for the whole workspace. Admins can now set
each member to inherit, on or off (also in bulk, optionally erasing the
intents already captured), and members can opt themselves out. Only
contributing callers get the _intent parameter; callers without a member
identity never contribute. Changes are logged as LEARNING_CHANGED.
Every applied skill was pasted into the initialize instructions, a skill
could only hold a 2,000-character rule, and edits overwrote it.

- skills_list / skill_get on /mcp/:serverId when the caller sees an
  applied skill, scoped to the server's organization and the caller's
  visible connectors; each skill is also offered as an MCP prompt.
- Per-server skills_delivery: auto (full text up to 6,000 characters,
  else an index), inline or index. The migration pins servers already
  above the threshold to inline so nothing changes until an admin opts in.
- Optional body (markdown, up to 20,000 characters) served by skill_get
  only; the Skills Finder can file one.
- Edits keep the previous content in kg_skill_versions; skill_get and
  prompts count uses. Consolidate leaves skills with a procedure alone.
- AI Skills page shows procedure, version, usage and history; the MCP
  server page gets the delivery setting.
AI clients can save facts, preferences and notes and find them again in
any client connected to a server of the workspace: personal memory per
member (readable only by that member) and a company memory.

- memory_items with a generated tsvector ('simple') and GIN index;
  organization_members.memory_access (inherit, none, read, personal,
  contribute) with a workspace default.
- memory_search / memory_save / memory_forget on /mcp/:serverId only,
  offered by the caller's level; results are labelled as data, not
  instructions. Company memory from AI clients waits for approval unless
  the workspace switch is set to automatic.
- Secrets are refused; size limits per item, member and workspace.
  Admins can erase a member's personal memory without reading it;
  removing a membership deletes it. Access, settings and erasures are
  logged as security events.
- Licence gate in ee/licensing (EditionService.hasMemory): an active
  Enterprise licence of the workspace (Cloud) or instance (self-hosted);
  not the trial or transition period. The code itself is AGPL.
- Memory page (my memory, company memory, review, settings), Memory
  column and bulk actions on Settings > Users, docs/memory.md.
One folder tree per workspace, shared by skills and the company memory,
so a topic holds both its procedures and its facts.

- knowledge_folders (parent, name, description, role_ids) and folder_id
  on skills and memory items; up to 4 levels, unique sibling names.
  Deleting a folder moves its subfolders and items to the parent.
- MCP: skills_list(folder) lists a folder and its subfolders; the skill
  index is grouped by folder and, when too large, lists the top-level
  folders for skills_list. memory_search(folder) and memory_save(folder)
  for company items; AI clients never create folders.
- Role limits (Enterprise, set by admins): a folder limited to custom
  roles is hidden with its subtree from everyone else on every path
  (instructions, skills_list/skill_get, prompts, memory_search,
  kg_how_to_obtain, dashboard). Admins see everything. Limits keep
  applying without the licence. Moves and deletes never widen access:
  only an admin can move content out of a limited folder, a limited
  folder with items cannot be deleted, and its subfolders inherit the
  limit. Changes are logged as KNOWLEDGE_FOLDER_ACCESS_CHANGED.
- EditionService.hasEnterprise / assertEnterprise generalise the memory
  gate for both features.
- Folder tree with edit, move and role editor on the AI Skills and
  Memory pages; docs updated.
@helpcodeai

Copy link
Copy Markdown
Contributor Author

Update after review (19 Oct): verified with a real model (gpt-4o-mini driving the MCP server as a chat client) and two independent security reviews. Fixes are in the PRs they belong to:

Merge the stack in order (#1146 → #1149 → #1152 → #1154 → #1158) and release them together.

@helpcodeai
helpcodeai enabled auto-merge (squash) October 19, 2026 18:08
@helpcodeai
helpcodeai merged commit a5c76c4 into main Oct 19, 2026
14 checks passed
@helpcodeai
helpcodeai deleted the helpcodeai/knowledge-folders branch October 19, 2026 18:18
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Folders for skills and company memory, with role restrictions

1 participant