Rensei docs
Memory

Cross-Project Transfer

Cedar-authorized cross-project memory transfer.

Cross-project knowledge transfer lets agents working on project B retrieve observations originally captured in project A, provided the two projects share a sufficiently similar tech stack and an administrator has explicitly authorized the transfer. Transfer is read-only - agents cannot write back to a source project's observation store.

Transfer rules

Three conditions must all be satisfied for a transfer to occur:

Tech-stack similarity. The source and destination project must have a Jaccard similarity score ≥ 0.70 on their tech-stack fingerprints. The fingerprint is built from manifest files (package.json, go.mod, pyproject.toml, Cargo.toml, requirements.txt, Gemfile).

Explicit admin grant. An org administrator must enable transfer for the (sourceProjectId, destProjectId) pair. Transfer is deny-by-default - the grant must be created deliberately via the Cedar policy cross_project_transfer_grants or equivalent admin config.

Classification gate. Only observations with classification = 'public' or classification = 'internal' are eligible. confidential and restricted observations are blocked by the Cedar PEP (enforceCrossProjectTransfer) regardless of admin grants.

Transferred observation handling

Transferred observations are tagged in their metadata and receive a weight penalty:

PropertyValue
metadata.source_projectThe source project ID
metadata.transferredtrue
weight (and score)Multiplied by transferredWeightMultiplier (default 0.5)

The 0.5 weight multiplier reflects the design intent: transferred knowledge is useful signal but has lower confidence than observations from the same project.

Tech-stack fingerprinting

fingerprintProject parses supported manifest files and builds a TechStackFingerprint:

interface TechStackFingerprint {
  languages: Set<string>      // 'typescript', 'python', 'go', etc.
  frameworks: Set<string>     // 'react', 'nextjs', 'fastapi', etc.
  databases: Set<string>      // 'postgres', 'redis', etc.
  tools: Set<string>          // 'vitest', 'eslint', 'docker', etc.
}

Jaccard similarity compares the union of all identifiers across both fingerprints:

// From tech-stack.ts
export const DEFAULT_TRANSFER_SIMILARITY_THRESHOLD = 0.7

const similarity = stackSimilarity(fingerprintA, fingerprintB)
// similarity is 0..1; >= 0.7 makes the project eligible as a transfer source

Fingerprints are evaluated lazily on each retrieve() call via the FingerprintProvider callback. This satisfies the "tech-stack change re-evaluation" requirement - there is no long-lived in-process cache to invalidate.

Using the transfer store wrapper

createCrossProjectTransferStore wraps any MemoryStore implementation:

import {
  createCrossProjectTransferStore,
} from '@/lib/memory/cross-project-transfer'

const transferStore = createCrossProjectTransferStore({
  base: myMemoryStore,
  destProjectId: 'proj_target',
  destOrgId: 'org_123',
  fingerprintProvider: async (projectId) => {
    // Return a TechStackFingerprint or ProjectManifestFiles or null
    return await loadProjectFingerprint(projectId)
  },
  grantProvider: async (sourceId, destId, orgId) => {
    return await checkTransferGrant(sourceId, destId, orgId)
  },
  candidateSourceProvider: async (destProjectId, orgId) => {
    return await listSiblingProjects(destProjectId, orgId)
  },
  // Optional tuning
  transferredWeightMultiplier: 0.5,
  similarityThreshold: 0.7,
})

// retrievals now transparently include eligible cross-project observations
const observations = await transferStore.retrieve(scope, query)

Integration via context injection

The simpler path is to pass crossProjectTransfer directly to buildSessionMemoryBlock:

const block = await buildSessionMemoryBlock({
  sessionId: 'ses_abc',
  workType: 'feature',
  orgId: 'org_123',
  projectId: 'proj_target',
  queryText: 'implement OAuth flow',
  store: myStore,
  crossProjectTransfer: {
    fingerprintProvider: myFingerprintProvider,
    grantProvider: myGrantProvider,
    candidateSourceProvider: myCandidateProvider,
  },
})

The injection builder fills in base, destProjectId, and destOrgId from the session scope automatically.

Enabling transfer (operators)

Transfer is disabled by default for all project pairs. To enable for a pair:

  1. Navigate to Project Settings → Memory → Cross-Project Transfer.
  2. Select the source project from the list of tech-stack-similar candidates (the UI shows Jaccard similarity scores).
  3. Confirm the grant. The system writes a Cedar policy entity for the pair.

Only observations classified public or internal will flow through. Operators can inspect which observations were transferred in the Observation Store list view filtered by metadata.transferred = true.

Write isolation

The transfer wrapper enforces read-only access:

  • store() (write) is always directed at the destination project's own store - the source project is never written to.
  • deleteById() on a transferred observation ID is rejected with an error.

This prevents transferred knowledge from being modified or deleted through the destination project's context.

On this page