Skip to main content

Context Assembly

Context Assembly orchestrates the read path for memory in Codebolt. It evaluates rules, executes multiple persistent memory pipelines in parallel, merges the results, enforces token budgets, and returns assembled context ready for the LLM.

Context Assembly

The system comprises two sub-systems:

  • Context Rule Engine — determines which memories to activate based on the current scope and conditions.
  • Context Assembly Service — executes the resolved memory pipelines and merges their output.

Storage

ComponentLocation
Rule engine definitions{projectPath}/.codebolt/context-rule-engines/{id}.json
Assembly serviceNo dedicated storage — reads from Persistent Memory definitions and storage layers at runtime

Context Assembly Request

Every assembly call begins with a request object:

{
scope_variables: ScopeVariables, // REQUIRED — all scope vars
additional_variables?: AdditionalVariables, // phase, query, custom
input?: { text?: string }, // semantic search text
explicit_memory?: string[], // specific memory type IDs to include
constraints?: { max_tokens?: number },
rule_engine_ids?: string[] // which rule engines to evaluate
}

Scope Variables (8 Scopes, Hierarchical)

Scope variables describe the current execution context. Eight scopes are supported:

ScopeFields
SwarmScopeVarsswarm_id, swarm_name
ProjectScopeVarsproject_id, project_path
AgentScopeVarsagent_id, agent_name, agent_type
ThreadScopeVarsthread_id
TaskScopeVarstask_id, task_type
UserScopeVarsuser_id
OrgScopeVarsorg_id
OrchestratorScopeVarsorchestrator_id

The internal helper flattenVariables() merges all scope and additional variables into a flat Record<string, any> for use by the rule engine and pipeline execution.


Assembly Execution Flow

The assembly process runs through seven sequential stages:

1. validateRequest(request)
├─ Check scope_variables present
├─ Verify explicit memories exist
├─ Validate constraints
└─ Check rule engines exist

2. resolveMemories(request)
├─ Start with explicit_memory list
├─ Load enabled rule engines (or specific rule_engine_ids)
├─ evaluateRules() against flattened variables
├─ Merge: explicit memories + rule-matched memories (deduplicated)
└─ Build trace: which memories from explicit vs rules

3. Execute pipelines IN PARALLEL
├─ For each resolved memory: load PersistentMemory definition
├─ Call persistentMemoryPipelineService.executePipeline(memoryId, intent, threadId)
├─ Collect ContextItem[] from each pipeline
└─ Emit 'contextAssembly:pipelineComplete' per pipeline

4. mergeResults(allItems)
├─ Deduplicate by content hash (MD5 or string equality)
└─ Return unique ContextItem[]

5. applyContributionRules(items)
├─ Group items by their memory's contribution.section
├─ Within each section: sort by priority weight
├─ Apply max_items limit per memory type
└─ Return Record<string, ContextItem[]> keyed by section

6. enforceBudget(sections, constraints)
├─ If max_tokens set: estimate tokens per section
├─ Truncate lowest-priority sections first until under budget
└─ Return { sections, truncated: boolean, tokenEstimate }

7. Return AssembledContext
├─ context: Record<string, ContextItem[]>
├─ trace: { memory_calls, rules_applied, variables_used, missing_variables }
└─ meta: { token_estimate, total_items, truncated, duration_ms }

Context Sections and Ordering

Sections appear in this fixed order in assembled context:

OrderSection
1state
2warnings
3constraints
4knowledge
5history
6suggestions
7working_memory

Within each section, items are sorted by priority weight:

PriorityWeight
critical4
high3
medium2
low1

Token Budget Enforcement

  • Estimation: estimateTokens() uses a simple heuristic of string length / 4.
  • Truncation: When a max_tokens constraint is set and the assembled context exceeds it, items are removed starting from the lowest-priority sections first.
  • Metadata: When truncation occurs, the response meta object sets truncated: true.

Context Rule Engine

The rule engine decides which persistent memories to activate for a given assembly request.

Rule Engine Definition

{
id: string,
name: string,
description: string,
enabled: boolean,
rules: ContextRule[]
}

Rule Structure

{
id: string,
name: string,
priority: number, // higher = evaluated first
when: ContextRuleCondition[], // ALL must match (AND logic)
then: { add_memories: string[] } // memory type IDs to include
}

Each rule has a set of when conditions that use AND logic — all conditions must match for the rule to fire. When a rule matches, its then.add_memories list is added to the set of memories to assemble.

Rule Evaluation

evaluateRules(request, ruleEngines) proceeds as follows:

  1. Build variable lookup: a flat map with prefixes scope., addVar., input..
  2. For each engine (if enabled):
  3. Sort rules by priority (descending).
  4. For each rule: evaluate ALL conditions.
  5. If all conditions match: add the rule's memories to the matched set.
  6. Collect: matchedMemories[], engineResults[], variablesUsed[].

Condition Operators

The rule engine supports 14 condition operators:

OperatorDescription
existsField has a value (not null/undefined)
not_existsField is null/undefined
eqStrict equality
neqNot equal
inValue is in array
not_inValue is not in array
containsString contains substring
starts_withString starts with prefix
ends_withString ends with suffix
gtGreater than (numeric)
gteGreater than or equal
ltLess than
lteLess than or equal
regexRegular expression match

Variable Lookup Building

buildVariableLookup(request) creates a flat map from the request:

  • From scope_variables: scope.swarm.swarm_id, scope.agent.agent_name, etc.
  • From additional_variables: addVar.phase, addVar.query, addVar.{custom}
  • From input: input.text

Variable Extraction

Two helpers support UI integration and validation:

extractPossibleVariables(ruleEngines, memories)

  • Scans all rule conditions for referenced variable fields.
  • Scans all memory definitions for inputs_scope and additional_variables.
  • Returns PossibleVariable[] for populating UI dropdowns.

getRequiredVariables(memories)

  • Extracts required scope variables from memory inputs_scope.
  • Extracts required additional variables.
  • Returns what the caller must provide in the request.

Rule Engine CRUD

OperationBehavior
CreateGenerates IDs for rules without IDs, saves JSON to disk
UpdateMerges updates, generates IDs for new rules
DeleteRemoves the JSON file
AddRuleAdds a rule to an existing engine
UpdateRuleUpdates a specific rule by ID
DeleteRuleRemoves a specific rule by ID

WebSocket Events

Assembly progress is reported via WebSocket events:

EventDescription
contextAssembly:startedAssembly begins
contextAssembly:rulesEvaluatedRules evaluated, memories resolved
contextAssembly:pipelineCompleteOne pipeline finished
contextAssembly:completeFull assembly done with result
contextAssembly:errorAssembly failed

REST API

Context Assembly Endpoints

MethodEndpointDescription
POST/context-assembly/assembleMain assembly endpoint (accepts x-thread-id header)
POST/context-assembly/validateValidate a request without executing
GET/context-assembly/memory-typesReturns simplified list of available memory types
POST/context-assembly/evaluate-rulesEvaluate rules without performing full assembly
POST/context-assembly/required-variablesGet required variables for a set of memories

Context Rule Engine Endpoints

MethodEndpointDescription
GET/context-rule-engineList all rule engines
POST/context-rule-engineCreate a new rule engine
GET/context-rule-engine/:idGet a specific rule engine
PUT/context-rule-engine/:idUpdate a rule engine
DELETE/context-rule-engine/:idDelete a rule engine
POST/context-rule-engine/:id/evaluateEvaluate a specific engine
POST/context-rule-engine/evaluateEvaluate all enabled engines
GET/context-rule-engine/variablesGet possible variables for UI

SDK Reference

Context Assembly

// Assemble context for the current scope
const result = await codebolt.contextAssembly.getContext({
scope_variables: { /* ... */ },
constraints: { max_tokens: 8000 }
});

// Validate a request before executing
const validation = await codebolt.contextAssembly.validate(request);

// List available memory types
const types = await codebolt.contextAssembly.listMemoryTypes();

// Evaluate rules without full assembly
const ruleResults = await codebolt.contextAssembly.evaluateRules(request);

// Get required variables for a set of memories
const required = await codebolt.contextAssembly.getRequiredVariables(memoryIds);

Context Rule Engine

// Create a rule engine
const engine = await codebolt.contextRuleEngine.create({
name: "Planning Phase Rules",
description: "Activate architecture memories during planning",
enabled: true,
rules: [
{
name: "Include architecture docs in planning",
priority: 10,
when: [
{ field: "addVar.phase", operator: "eq", value: "planning" }
],
then: { add_memories: ["architecture-docs", "project-conventions"] }
}
]
});

// Update an existing engine
await codebolt.contextRuleEngine.update(engineId, { enabled: false });

// Evaluate a rule engine against current variables
const evaluation = await codebolt.contextRuleEngine.evaluate({
scope_variables: { /* ... */ },
additional_variables: { phase: "planning" }
});

// Get all possible variables for UI dropdowns
const variables = await codebolt.contextRuleEngine.getPossibleVariables();