MemoryProxy Injection Pipeline: Understanding ContextContentProvider and InjectionHook Abstractions

The MemoryProxy injection pipeline in TencentDB-Agent-Memory uses the InjectionHook interface as its core abstraction, enabling protocol-agnostic context injection at specific InjectionPoint locations, while provider utilities in ContextContentProvider (via createInjectionHook) simplify the creation of cache-aware injectors that return ContextBlock arrays.

The TencentDB-Agent-Memory repository implements a sophisticated MemoryProxy injection pipeline that enriches LLM requests before they reach the model. This system relies on a strictly typed hierarchy of abstractions—centered on the InjectionHook contract and supporting provider utilities—to inject contextual blocks at precise conversation boundaries. The architecture supports priority-ordered execution, semantic anchoring, and cache-aware prewarming across different agent protocols.

The Core Data Model: ContextBlock and AgentContext

The pipeline operates on immutable data structures defined in MemoryProxy/src/injection/types.ts. Understanding these foundation types is essential before implementing custom hooks.

ContextBlock: The Atomic Content Unit

Every piece of injectable content is encapsulated as a ContextBlock. This union type supports text, tool calls, images, and custom payloads, making the pipeline protocol-agnostic.

export type ContextBlockType =
  | "text" | "tool_use" | "tool_result" | "thinking" | "image" | "custom";

export interface ContextBlock {
  type: ContextBlockType;
  content: string;
  metadata?: Record<string, unknown>;
}

AgentContext: The Pipeline Payload

The AgentContext interface (lines 128–146 in types.ts) represents the complete request state traversing the pipeline. It aggregates messages, available tools, request parameters, and metadata.

export interface AgentContext {
  messages: ContextMessage[];
  tools?: AgentTool[];
  requestParams: Record<string, unknown>;
  metadata: AgentContextMetadata;
}

The AgentContextMetadata sub-interface carries trace identifiers, user/session context, and protocol information required by injectors to make contextual decisions.

Injection Points and Semantic Anchoring

The MemoryProxy injection pipeline supports nine distinct InjectionPoint values (lines 148–164), defining exactly where content modification occurs:

  • System message boundaries: system.prefix, system.suffix, system.before_tools, system.after_tools
  • User message boundaries: user.before, user.after, user.first_turn
  • Tool list modifications: tools.append, tools.prepend

Portable Anchoring with SemanticSlot

For agents requiring precise structural placement, the AnchorTarget interface combines with SemanticSlot types to declare which conceptual region (e.g., "persona", "memory", "knowledge") receives the injection, independent of the underlying protocol representation.

export type SemanticSlot = "persona" | "tools" | "skills" | "memory" | "knowledge" | "rules" | "task_context" | (string & {});

export interface AnchorTarget {
  slot?: SemanticSlot;
  rawKey?: string;
  relation: AnchorRelation;
}

The InjectionHook Interface: Core Abstraction

The InjectionHook interface (lines 122–162 in types.ts) is the primary contract that all context injectors must implement. This abstraction decouples content generation from application logic.

export interface InjectionHook {
  id: string;
  point: InjectionPoint;
  anchor?: AnchorTarget;
  priority: HookPriority;
  description: string;
  cacheStrategy?: CacheStrategy;
  prewarm?(input: PrewarmInput): Promise<ContextBlock[]> | ContextBlock[];
  execute(ctx: AgentContext): Promise<ContextBlock[]> | ContextBlock[];
}

Key properties include:

  • point: The specific InjectionPoint where execution triggers
  • priority: Numeric execution order (lower values execute first)
  • cacheStrategy: Controls precomputation via "none", "session_init", or "hybrid" modes
  • prewarm: Optional initialization function receiving PrewarmInput (containing keyId, userId, sessionInfo)
  • execute: The main method receiving AgentContext and returning ContextBlock[] arrays

Hook Priority and Registration

Execution Ordering with HookPriority

The pipeline respects explicit numerical priorities. The HOOK_PRIORITY constant (lines 332–353) provides sensible defaults:

export const HOOK_PRIORITY = {
  SYSTEM: 0,
  MEMORY: 100,
  SKILL: 200,
  WIKI: 300,
  CUSTOM: 1000,
} as const;

The HookRegistry Contract

The HookRegistry interface (lines 64–78) manages hook lifecycle and retrieval. Implementations guarantee that getHooks(point) returns injectors sorted by priority for deterministic execution order.

export interface HookRegistry {
  register(hook: InjectionHook): void;
  unregister(hookId: string): void;
  getHooks(point: InjectionPoint): InjectionHook[];
  getAll(): InjectionHook[];
}

The concrete implementation resides in MemoryProxy/src/injection/registry.ts, while the factory function createInjectionHook in MemoryProxy/src/injection/provider.ts simplifies instantiation of compliant objects.

Cache-Aware Execution Strategies

The pipeline optimizes performance through the CacheStrategy union type ("none" | "session_init" | "hybrid"). When set to "session_init" or "hybrid", the prewarm method executes once during session establishment, allowing expensive data fetching (e.g., loading conversation history from TencentDB) to occur before the first turn.

The PrewarmInput interface supplies session context including keyId, agentSource, spaceId, and sessionInfo, enabling injectors to preload user-specific data.

Pipeline Orchestration

The InjectionPipeline implementation in MemoryProxy/src/injection/pipeline.ts coordinates the complete flow:

  1. Warm-up Phase: Invokes prewarm on hooks with applicable cache strategies
  2. Execution Phase: Collects ContextBlock arrays from hooks at each InjectionPoint in priority order
  3. Application Phase: Merges injected blocks into the AgentContext according to semantic or positional rules
  4. Observation Phase: Optional InjectionObserver integrations (defined in observer.ts) emit telemetry to systems like Langfuse

Practical Implementation Example

Implementing a custom injector requires implementing the InjectionHook contract and registering via the provider utilities:

import { createInjectionHook } from "./injection/provider.js";
import { HOOK_PRIORITY, InjectionPoint } from "./injection/types.js";

const memoryInjector = createInjectionHook({
  id: "mem-recent-conversation",
  point: "user.before",
  priority: HOOK_PRIORITY.MEMORY,
  description: "Inject recent conversation snippets",
  cacheStrategy: "hybrid",
  async prewarm(input) {
    // Called once per session during initialization
    const recent = await fetchRecentMessages(input.keyId);
    return recent.map(txt => ({ type: "text", content: txt }));
  },
  async execute(ctx) {
    // Called per-turn for dynamic context
    return [{ type: "text", content: "Remember to check the latest logs." }];
  },
});

// Register with the global registry
registry.register(memoryInjector);

// Execute via the pipeline
const pipeline = getInjectionPipeline({});
const enrichedCtx = await pipeline.run(originalAgentContext);

This example demonstrates the ContextContentProvider pattern: the createInjectionHook factory transforms a configuration object into a fully compliant InjectionHook, handling interface conformance automatically.

Summary

  • The MemoryProxy injection pipeline centers on the InjectionHook interface defined in MemoryProxy/src/injection/types.ts, which standardizes how contextual content enters the LLM request flow.
  • Nine distinct InjectionPoint values allow precise placement of injected content at system, user, or tool boundaries.
  • Priority-based execution (via HookPriority and HOOK_PRIORITY constants) ensures system-level hooks execute before skill or memory hooks.
  • Cache strategies ("session_init", "hybrid") enable expensive data loading during session initialization via the prewarm lifecycle method.
  • The createInjectionHook factory in MemoryProxy/src/injection/provider.ts implements the ContextContentProvider pattern, simplifying the creation of type-safe injectors.
  • The Pipeline orchestrator in MemoryProxy/src/injection/pipeline.ts coordinates hook execution and block application across the conversation lifecycle.

Frequently Asked Questions

What is the difference between InjectionHook and ContextContentProvider?

InjectionHook is the strict TypeScript interface defining the contract for all injectors (id, point, priority, execute/prewarm methods), while ContextContentProvider refers to the factory utilities (particularly createInjectionHook in provider.ts) that instantiate these hooks. The provider abstracts boilerplate, allowing developers to supply only the configuration and logic functions while ensuring type safety.

How does hook priority affect injection order?

The priority property (typed as HookPriority, which is a number) determines execution sequence within each InjectionPoint. Lower numeric values execute first. The system provides predefined constants in HOOK_PRIORITY: SYSTEM (0), MEMORY (100), SKILL (200), WIKI (300), and CUSTOM (1000). The HookRegistry implementation automatically sorts hooks by priority when retrieved for a specific point.

What are the available cache strategies for injection hooks?

The CacheStrategy type supports three modes: "none" (execute execute() every turn), "session_init" (execute prewarm() once at session start and cache results), and "hybrid" (combine both—use prewarm for static data and execute for dynamic per-turn context). The PrewarmInput interface supplies session metadata including keyId, userId, and agentSource during the warm-up phase.

Where are the concrete implementations of the injection pipeline located?

The core type definitions reside in MemoryProxy/src/injection/types.ts. The pipeline orchestrator is implemented in MemoryProxy/src/injection/pipeline.ts, while hook registration logic lives in MemoryProxy/src/injection/registry.ts. Factory functions for creating hooks are found in MemoryProxy/src/injection/provider.ts, and real-world injector examples (such as tool injectors) are located in MemoryProxy/src/injection/injectors/.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →