packages/builtin-agents/src/agents/page-agent/README.md
This document explains how the Page Agent works, including context tracking, injection, and document manipulation.
The Page Agent is a specialized AI agent that helps users create, read, update, and edit documents in the PageEditor. It has access to the current page's content and structure through context injection.
index.ts)lobe-page-agent (Document manipulation tools)src/tools/document/)lobe-page-agentinitPage - Initialize document from MarkdowneditTitle - Edit document titlepackages/context-engine/)PageEditorContextInjector┌─────────────────────────────────────────────────────────────┐
│ Page Editor │
│ - User edits document with Lexical editor │
│ - Document saved in database as JSON (editorData) │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Store Updater (StoreUpdater.tsx) │
│ - Watches currentDocId changes │
│ - Calls: useChatStore.internal_updateActivePageId() │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Chat Store (store/chat/slices/message/) │
│ - Stores activePageId globally │
│ - Available to all services via getChatStoreState() │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ User sends message to Page Agent │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Chat Service (services/chat/index.ts) │
│ 1. Gets activePageId from chat store │
│ 2. Fetches document from file store │
│ 3. Builds pageEditorContext: │
│ - document metadata (id, title, etc.) │
│ - content (markdown text) │
│ - editorData (Lexical JSON structure) │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Context Engineering (mecha/contextEngineering.ts) │
│ - Receives pageEditorContext │
│ - Passes to context pipeline │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PageEditorContextInjector (context-engine/providers/) │
│ 1. Converts Lexical JSON → XML with node IDs │
│ 2. Formats as structured context │
│ 3. Injects before first user message │
└────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Model receives: │
│ <current_page_context> │
│ <document_metadata> │
│ <id>docs_abc123</id> │
│ <title>My Document</title> │
│ </document_metadata> │
│ <document_structure> │
│ <root> │
│ <h1 id="node_1"> │
│ <span id="node_2">Heading Text</span> │
│ </h1> │
│ <p id="node_3"> │
│ <span id="node_4">Paragraph content</span> │
│ </p> │
│ </root> │
│ </document_structure> │
│ </current_page_context> │
└─────────────────────────────────────────────────────────────┘
Location: src/features/PageEditor/StoreUpdater.tsx:145-157
useEffect(() => {
const activeId = currentDocId || pageId;
updateActivePageId(activeId);
return () => {
updateActivePageId(undefined); // Cleanup on unmount
};
}, [currentDocId, pageId, updateActivePageId]);
State: src/store/chat/slices/message/initialState.ts:17
interface ChatMessageState {
activePageId?: string; // Current page being edited
// ... other fields
}
Location: src/services/chat/index.ts:217-259
const isPageAgentEnabled = enabledToolIds.includes(PAGE_AGENT_TOOL_ID);
if (isPageAgentEnabled) {
const activePageId = getChatStoreState().activePageId;
if (activePageId) {
const document = documentSelectors.getDocumentById(activePageId)(fileState);
pageEditorContext = {
document: {
id: document.id,
title: document.title,
fileType: document.fileType,
totalCharCount: document.totalCharCount,
totalLineCount: document.totalLineCount,
},
content: document.content || undefined,
editorData: document.editorData || undefined,
};
}
}
Location: packages/context-engine/src/providers/PageEditorContextInjector.ts:32-116
Input (Lexical JSON):
{
"root": {
"type": "root",
"children": [
{
"type": "heading",
"tag": "h1",
"children": [{ "type": "text", "text": "Hello World" }]
}
]
}
}
Output (XML with IDs):
<root>
<h1 id="node_1">
<span id="node_2">Hello World</span>
</h1>
</root>
Supported Node Types:
text → <span id="...">heading → <h1-h6 id="...">paragraph → <p id="...">list → <ul/ol id="...">listitem → <li id="...">quote → <blockquote id="...">code → <pre id="..."><code>link → <a id="..." href="...">image → ``Location: packages/context-engine/src/providers/PageEditorContextInjector.ts:217-237
Process:
<current_page_context>{ injectType: 'page-editor-context', systemInjection: true }The model can use these tools to manipulate the document:
editTitle({ title: 'New Title' });
initPage({
markdown: '# Heading\n\nParagraph content',
});
Currently disabled for MVP, but will support:
updateNode({ nodeId: "node_3", content: "New text" })deleteNode({ nodeId: "node_5" })createNode({ type: "p", content: "New paragraph", position: "after", referenceNodeId: "node_3" })Page ID tracking:
[StoreUpdater] Updating activePageId in chat store: docs_abc123
[ChatStore] Updating activePageId: docs_abc123
Context building:
[ChatService] isPageAgentEnabled: true
[ChatService] activePageId: docs_abc123
[ChatService] Built page editor context: { documentId: '...', hasEditorData: true }
XML conversion:
[PageEditorContextInjector] Converted Lexical to XML, node count: 15
[PageEditorContextInjector] XML preview: <root>...
Injection:
[PageEditorContextInjector] Page Editor context injected successfully
[PageEditorContextInjector] Total messages after injection: 2
initPage and editTitle are currently enabledpackages/builtin-agents/src/agents/page-agent/src/tools/document/packages/context-engine/src/providers/PageEditorContextInjector.tssrc/services/chat/index.tssrc/features/PageEditor/StoreUpdater.tsxsrc/store/chat/slices/message/src/store/file/slices/document/