How Anchor-Based Landing Works in MemoryProxy: Configuration Guide
Anchor-based landing in MemoryProxy allows injection hooks to place generated content at precise semantic locations—such as before or after specific markdown headings—rather than using generic append/prepend injection points.
MemoryProxy, a component of the TencentCloud/TencentDB-Agent-Memory repository, enhances LLM requests by injecting tools, memories, and skills through a sophisticated pipeline. Understanding anchor-based landing is essential for developers who need surgical control over where injected content appears within the message structure, ensuring that dynamic content respects the original document organization.
What Is Anchor-Based Landing?
Without anchor-based landing, injection hooks in MemoryProxy rely on generic injection points that simply append or prepend content to the request. Anchor-based landing introduces a declarative positioning system where hooks specify a slot (semantic region) and a relation (position relative to that slot).
When a hook declares an anchor, MemoryProxy attempts to resolve the slot name to a concrete markdown heading or marker through the active AgentProfile. If resolution succeeds, the generated block lands exactly at the specified location—preserving document structure and semantic coherence. If resolution fails, the system gracefully falls back to standard injection point behavior.
The Anchor Resolution Pipeline
Hook Definition with AnchorTarget
Hooks define optional anchors through the AnchorTarget interface. In src/injection/types.ts at line 339, the type definition shows that hooks may include an anchor field containing a slot and a relation.
// src/injection/types.ts (line 339)
export interface InjectionHook {
// ... other properties ...
anchor?: AnchorTarget;
}
interface AnchorTarget {
slot: string; // e.g., "memory", "skills", "knowledge"
relation: "before" | "after" | "inside_append" | ...;
}
Common slot names vary by agent: WorkBuddy uses memory and skills, while Claude-Code uses knowledge and skill. These constants are defined in src/injection/agents/workbuddy/constants.ts and src/injection/agents/codebuddy/constants.ts.
Profile Resolution
Before anchor resolution, MemoryProxy identifies the active agent. In src/injection/pipeline.ts (lines 101-128), the process() function looks up the AgentProfile based on URL prefix or legacy detection and attaches it to the context. This profile contains the logic necessary to translate semantic slot names into literal markdown headings.
Anchor Resolution and Insertion
The core landing logic resides in src/injection/pipeline.ts at lines 351-369. During hook execution, the pipeline checks for the presence of an anchor property. If present, it invokes the profile's resolveSlot(slot: string): string | null method to convert the slot name into a heading key (e.g., "memory" resolves to "## Memory").
If the key exists in the document, the pipeline inserts the block according to the specified relation. If the slot cannot be resolved—meaning the target heading is missing—the hook falls back to generic point behavior, and a debug message is logged:
[injection] anchor slot "memory" ignored – no matching heading found
Configuring Anchor-Based Landing
Declaring Anchors in Injection Hooks
To enable anchor-based landing, add an anchor field to your hook definition. The relation parameter supports values like before, after, and inside_append, determining whether content precedes, follows, or nests within the target section.
// Example: Inserting before the Memory section
export const skillToolsInjector: InjectionHook = {
name: "skill-tools",
async execute(context) {
// ... generate content ...
},
anchor: { slot: "memory", relation: "before" },
};
Agent Profiles and Slot Mapping
Each AgentProfile implements the resolveSlot function to map abstract slot names to concrete markdown headings. The profile is automatically loaded based on the request URL path (/agent/:spaceId/...).
To add support for new slots, modify the corresponding agent profile:
// Conceptual example based on workbuddy profile structure
export const workbuddyProfile: AgentProfile = {
resolveSlot(slot: string): string | null {
const mapping = {
memory: "## Memory",
skills: "## Skills",
faq: "## FAQ"
};
return mapping[slot] ?? null;
}
};
Reference the agent-specific constants files—src/injection/agents/workbuddy/constants.ts and src/injection/agents/codebuddy/constants.ts—for the complete list of supported slots per agent.
Handling Missing Anchors
Anchor landing is opt-in per hook. If a hook omits the anchor field entirely, it follows legacy injection point behavior. If a hook declares an anchor but the profile returns null for the slot (heading not found), the pipeline logs the debug message shown above and falls back to the default injection point. Control log verbosity through the logLevel configuration in MemoryProxy settings.
Practical Implementation Examples
Inserting Before Memory Section
The tdaiToolsInjector in src/injection/injectors/tdai-tools-injector.ts demonstrates placing tool descriptions immediately before the memory section:
// src/injection/injectors/tdai-tools-injector.ts
export const tdaiToolsInjector: InjectionHook = {
// ... hook implementation ...
anchor: { slot: "memory", relation: "before" },
};
Result: Generated content appears immediately before the ## Memory heading.
Appending Inside Knowledge Section
For nested insertion within a section, use inside_append or after relations. The knowledge tools injector uses this pattern:
// src/injection/injectors/knowledge-tools-injector.ts
export const knowledgeToolsInjector: InjectionHook = {
// ... hook implementation ...
anchor: { slot: "knowledge", relation: "after" },
};
Result: The block is appended inside the ## Knowledge section, following existing content.
Legacy Fallback Behavior
Hooks without anchor definitions maintain backward compatibility:
export const legacyInjector: InjectionHook = {
// No anchor field
execute(context) {
// ... processing ...
}
};
Result: Output is injected at the default injection point (e.g., system.after_tools) regardless of document structure.
Summary
- Anchor-based landing provides semantic positioning within LLM requests, replacing generic append/prepend logic with precise markdown-based targeting.
- Configuration occurs at two levels: hooks declare anchors via
slotandrelationproperties, while AgentProfiles resolve slots to concrete headings through theresolveSlotfunction. - The resolution pipeline lives in
src/injection/pipeline.ts(lines 351-369), with type definitions insrc/injection/types.ts(line 339). - When anchors cannot be resolved, the system falls back to standard injection points and logs debug information.
- WorkBuddy, CodeBuddy, and Claude-Code agents each maintain distinct slot constants in their respective
constants.tsfiles.
Frequently Asked Questions
What happens if the anchor slot is missing from the document?
If the profile cannot resolve the slot to a heading, MemoryProxy logs a debug message ([injection] anchor slot "x" ignored...) and falls back to the hook's default injection point behavior. The request proceeds without the semantic positioning, ensuring robustness against malformed or variable input.
Can I create custom anchor slots?
Yes. To define custom slots, modify the target agent's AgentProfile implementation to include the new slot in the resolveSlot mapping, then reference that slot in your hook's anchor declaration. You must also update the corresponding constants.ts file to document the new slot for consistency.
How is anchor-based landing different from injection points?
Injection points are generic positional markers (e.g., system.after_tools) that append or prepend content relative to the message structure. Anchor-based landing uses semantic markers (e.g., ## Memory) understood by the specific agent, allowing content to land within or adjacent to named document sections regardless of their position in the raw message.
Which agents support anchor-based landing?
MemoryProxy supports anchor-based landing across multiple agents including WorkBuddy, CodeBuddy, and Claude-Code, with each agent defining specific slot names in their respective constants files (src/injection/agents/workbuddy/constants.ts, src/injection/agents/codebuddy/constants.ts). The specific slots available vary by agent implementation, though memory and skills are commonly supported across WorkBuddy and CodeBuddy configurations.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →