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:
| Property | Value |
|---|---|
metadata.source_project | The source project ID |
metadata.transferred | true |
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 sourceFingerprints 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:
- Navigate to Project Settings → Memory → Cross-Project Transfer.
- Select the source project from the list of tech-stack-similar candidates (the UI shows Jaccard similarity scores).
- 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.
Related pages
- Observation Store - the base store that this wrapper enriches
- Memory Classification - the classification gate for
public/internal - Context Injection - where
crossProjectTransferis wired into the injection pipeline - Security: Cedar Policies - the policy engine that enforces transfer grants