Rensei docs
Memory

Context Injection

Start-of-session memory injection.

Context injection is the mechanism by which the platform enriches a just-started agent session with relevant past observations. When a worker claims a session, buildSessionMemoryBlock retrieves observations from the store, applies feedback ranking, and formats them into a markdown block that is delivered to the agent over the durable inject rail.

How injection works

Delivery

The rendered block is delivered through the durable session_memory_injects queue, not by literally rewriting the agent's system prompt: injectContextForClaimedSession (called after a worker claims the session) enqueues the block via enqueueMemoryInject, and the owning worker pulls + applies it through the daemon's handle.Inject on its next lock-refresh beat. The queue is the same at-least-once rail described in In-Session Injection → Durable inject queue.

Delivery is gated by the project's runtimeInjectEnabled memory sub-toggle - when it is off, the block is still computed and the context_injection_logs row is still written (so feedback weighting keeps learning), but nothing is pushed to the agent. Enqueue is idempotent per (sessionId, contentHash) and fire-and-forget: a failure never affects the session.

Retrieval query text

In production the queryText is composed from the work item rather than the raw issue UUID: issue identifier + cached issue title + the first line of the description (via the platform's Linear issue cache, populated on session creation), degrading stepwise to identifier-only → issue UUID → session id when fields are missing (retrieval-query.ts).

Token budgets

Each workType has a configurable token budget. The platform uses a 4-chars-per-token heuristic (no tiktoken dependency) to stay under the budget.

Default budgets

workTypeDefault (tokens)
bug_fix750
feature400
refactor600
chore300
(unknown)500 (fallback)

These are the DEFAULT_CONTEXT_BUDGET values from context-injection.ts. Override them per org:

const budgetConfig: ContextBudgetConfig = {
  defaults: {
    bug_fix: 750,
    feature: 400,
    refactor: 600,
    chore: 300,
  },
  orgOverrides: {
    'org_abc': {
      feature: 600,   // this org wants more feature context
    },
  },
}

Org overrides take precedence over the defaults map. The budget controls how many observations are included - observations are added in ranked order until the token estimate would be exceeded; the exceeding observation is skipped and the loop continues (a shorter observation may still fit).

Observation block format

Included observations are rendered as:

## Relevant Past Observations
- [<id>] <content excerpt up to 300 chars> (weight: <weight>)
- [<id>] ...

The excerpt is truncated at 300 characters to prevent a single verbose observation from consuming the entire budget.

Graph context merging

When a Knowledge Graph is configured and the project has Knowledge Graph memory enabled (per-project; see Knowledge Graph enablement), the injection also runs a graph search and merges architectural triplets into the block:

## Knowledge Graph Triplets
- AuthService → depends_on → PostgresDB
- UserController → calls → AuthService

Cross-org graph triplets are filtered by the Cedar PEP (enforceGraphAccess) before they reach the prompt. Graph search is per-workType gated in addition to the per-project enablement check:

workTypeGraph enabled by default
bug_fixYes
refactorYes
featureYes
choreNo

The injection log

Every injection is persisted to context_injection_logs by recordContextInjection. This row powers the Feedback Impact and Context Budget Pareto analytics views:

interface ContextInjectionLog {
  id?: string
  sessionId: string
  workType: string
  budgetTokens: number    // configured budget
  actualTokens: number    // tokens actually consumed
  observationIds: string[]
  sessionSummaryIds: string[]
  graphNodeIds?: string[]    // graph node UUIDs surfaced to agent
  graphEdgeKeys?: Array<{
    sourceId: string
    targetId: string
    relationshipName: string
  }>
  queryText: string
  orgId?: string
  projectId?: string
  timestamp?: string      // ISO-8601, from DB row
}

graphNodeIds and graphEdgeKeys are also used at session completion by GraphFeedbackService.applyFeedback to propagate EMA weight updates back to the graph nodes that were helpful or misleading.

Cross-project transfer

When crossProjectTransfer is passed to buildSessionMemoryBlock, the store is transparently wrapped with createCrossProjectTransferStore. This surfaces Cedar-authorized observations from peer projects with overlapping tech stacks. See Cross-Project Transfer for the transfer rules and the 70% Jaccard similarity threshold.

Calling the API

import {
  buildSessionMemoryBlock,
  recordContextInjection,
} from '@/lib/memory/context-injection'

const block = await buildSessionMemoryBlock({
  sessionId: 'ses_abc',
  workType: 'bug_fix',
  orgId: 'org_123',
  projectId: 'proj_xyz',
  queryText: 'fix null pointer in auth middleware',
  store: myMemoryStore,
  crossProjectTransfer: { /* optional */ },
  graph: {
    engine: graphSearchEngine,
    identity: { orgId: 'org_123', agentId: 'agent_dev' },
    // gated by isGraphEnabledForProject(orgId, projectId) - per-project, not an org flag
    graphEnabled: true,
  },
})

// block.block - the markdown string to prepend to the system prompt
// block.observationIds
// block.graphNodeIds
// block.graphEdgeKeys

const logId = await recordContextInjection({
  sessionId: 'ses_abc',
  workType: 'bug_fix',
  budgetTokens: 750,
  actualTokens: block.actualTokens,
  observationIds: block.observationIds,
  sessionSummaryIds: [],
  graphNodeIds: block.graphNodeIds,
  graphEdgeKeys: block.graphEdgeKeys,
  queryText: 'fix null pointer in auth middleware',
  orgId: 'org_123',
  projectId: 'proj_xyz',
})

On this page