Skip to content

Workspace memory for AI clients (Enterprise) - #1152

Merged
helpcodeai merged 14 commits into
mainfrom
helpcodeai/workspace-memory
Oct 19, 2026
Merged

helpcodeai merged 14 commits into
mainfrom
helpcodeai/workspace-memory

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

Summary

Workspace memory, see #1151. Builds on #1146 and #1149: until those are merged this PR also shows their commits; the changes here are in the last commit, "Add workspace memory for AI clients (Enterprise)".

AI clients can save facts, preferences and notes and find them again later, in any client connected to an MCP server of the workspace. Each member has a personal memory that only they can read, and the workspace has a company memory. Admins decide per member what their AI clients may do with it, and whether company memory proposed by AI clients needs approval.

Memory is available with an Enterprise licence on Cloud and self-hosted. It is gated like single sign-on: the code is AGPL, only the licence check (EditionService.hasMemory / assertMemory) lives in ee/licensing. That ee/ change was decided by the maintainer for this feature.

Changes

  • Data: memory_items (organization, scope personal | organization, owner, kind fact | preference | note, content up to 2,000 characters, tags, status active | pending | rejected, source, author, approver) with a generated tsvector column (simple configuration, no stemming, so German, Italian and English behave alike) and a GIN index. organization_members.memory_access: inherit (default) | none | read | personal | contribute. Workspace settings in org_settings: default access (initially personal) and the write mode for company memory from AI clients (approval by default, or auto).
  • Licence: EditionService.hasMemory(organizationId): an active, unexpired licence with plan enterprise. Cloud reads the workspace's licence; self-hosted reads the workspace's or the instance's key. The Business plan, the trial and the transition period do not include it. Cached 60 s per workspace because the MCP endpoint checks it on every request. Without it the routes answer edition_required and no memory tool is offered; stored data is kept.
  • MCP (/mcp/:serverId only; the shared /mcp toolset is unchanged), offered by the caller's level in the server's organization:
    • read: memory_search(query?, scope?, limit?), full text ranked by relevance, falling back to a substring match for partial words and codes.
    • personal: also memory_save (personal) and memory_forget (own personal items only).
    • contribute: memory_save also offers scope: "organization", pending or active depending on the workspace switch.
    • Results are labelled as information written by people, not instructions. Callers without a member identity, deactivated members and unlicensed workspaces get no tools.
  • Safety: content that looks like a password, token or key (common key formats, JWTs, password: / token= patterns, private keys) is refused. Limits: 10 tags per item, 1,000 personal items per member and workspace, 20,000 items per workspace.
  • Privacy: a personal item can be read, edited or deleted only by its owner; to anyone else, admins included, it answers like a missing item. Admins can erase a member's personal memory without reading it. Removing a membership deletes that member's personal memory in the workspace; deleting the account cascades.
  • Audit: MEMORY_ACCESS_CHANGED (one row per member), MEMORY_SETTINGS_CHANGED, MEMORY_ERASED.
  • API (/api/memory): status, items (list, create, edit, delete), items/:id/approve and items/:id/reject (admin, editor), settings (admin), members/access (admin, up to 500), members/:userId/personal (admin, erase). All DTO-validated.
  • UI: new Memory page (sidebar, Intelligence) with My memory, Company memory and To review tabs, search, add/edit/delete, approve/reject and workspace settings, or an Enterprise notice without the licence. Settings > Users gets a Memory column, bulk access changes and "Erase personal memory".
  • Docs: docs/memory.md, linked from docs/guides.md.

Cloud and self-hosted behave the same; the only difference is where the licence is read (workspace licence on Cloud, workspace or instance key on self-hosted).

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 were reverted
  • packages/backend: npx jest: 677 suites passed, 4 skipped; 20464 tests passed, 1496 skipped
  • packages/frontend: npx tsc --noEmit -p tsconfig.json: no errors; eslint on the changed files: no new findings
  • Migration: prisma migrate deploy on an empty scratch database, then prisma migrate diff against the schema: empty (the generated column and GIN index are declared in the schema, so later migrations do not try to drop them).

New unit tests: licence rules (Enterprise yes; Business, Team, inactive, expired, trial, transition no; per-workspace lookup), access resolution (no licence, no user, non-member, deactivated, inherit, explicit), save rules (levels, approval vs automatic, curator, secrets, limits, tags), search scope (organization filter, personal only for the owner, read level, full text then fallback, rank order), ownership (forget, someone else's personal item is not found even for an admin, curators only for company items), erase, access changes, membership removal, and the MCP endpoint (tools per level, organization scope only from contribute, arguments scoped to the server's organization, data labelling).

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

  • Without a licence: /api/memory/status reports licensed: false, item routes return 403 edition_required, no memory tool is listed.
  • With an Enterprise licence row for the workspace: level personal lists search, save and forget; memory_save has no scope; a preference was saved; a token-like text was refused.
  • Level set to contribute: a company item was saved as pending, did not appear in search, was approved via the API, then found by full text ("stammkunde"), by a partial word ("framew", fallback) and by English words ("Italian tables").
  • Second member (viewer): sees active company memory, gets 403 for pending items and for adding company memory, saves a personal item. The admin's personal list shows only their own items; editing, deleting or memory_forget of the member's item returns not found; the member still sees it. A viewer cannot change access (403). The admin erased the member's personal memory (1 item) without reading it.
  • Write mode set to automatic: a company item from the AI client was active right away. An invalid access level returns 400.
  • Security events: MEMORY_ACCESS_CHANGED, MEMORY_ERASED, MEMORY_SETTINGS_CHANGED with from/to.
  • A second workspace without a licence lists no memory tools; after adding its own Enterprise licence (and the 60 s cache), its search does not see the first workspace's items.
  • UI checked in the browser: Memory page with review queue and settings (an approval from the UI set the item to active), Users page Memory column and bulk bar.

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 #1151
Depends on #1146, #1149

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.
@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:03
@helpcodeai
helpcodeai merged commit 7f58be5 into main Oct 19, 2026
14 checks passed
@helpcodeai
helpcodeai deleted the helpcodeai/workspace-memory branch October 19, 2026 18:08
@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.

Workspace memory: personal and organization memory for AI clients

1 participant