The Problem: Intent Sprawl
A year ago, AiFiler's codebase had a problem. Every new user action—create a document, search by date, batch-move files, generate a summary—lived in its own corner. A button press here triggered one handler. A keyboard shortcut there triggered another. A voice command somewhere else triggered a third. The code worked, but it was fragmented.
When we wanted to add a new intent (say, "export as PDF"), we'd have to wire it up in multiple places: the UI layer, the API endpoint, the batch processor, the mobile client. Each integration point was a chance to introduce bugs. Each new feature meant touching five different files. We were scaling linearly with features, not logarithmically.
Then we asked ourselves: what if we treated user intent as a first-class concept?
The Architecture: Universal Router
The solution is the Universal Router (lib/intelligence/universalRouter.ts)—a single dispatcher that sits between user input and action execution. Here's how it works:
Data flow:
User Input (UI, API, Mobile, Voice)
↓
Universal Router (intent classification)
↓
Intent Handlers (87 handlers, organized by domain)
↓
Action Executor (unified execution layer)
↓
State Management & Side Effects
Every user action—whether it comes from a keyboard shortcut, a menu click, an API call, or a voice command—gets normalized into an intent object:
{
type: "MOVE_DOCUMENTS",
payload: {
documentIds: ["doc-123", "doc-456"],
targetFolderId: "folder-789",
context: "batch-operation"
},
metadata: {
source: "keyboard-shortcut", // or "ui-menu", "api", "voice"
userId: "user-456",
timestamp: 1715000000
}
}
The router doesn't care where the intent came from. It just routes it to the right handler.
Intent Classification: The Heuristics Layer
Before the router can dispatch, it needs to figure out what the user wants. This is where intent heuristics (lib/intentHeuristics.ts) come in.
When a user types a query like "move these contracts to client folder," the system doesn't just do a keyword match. It runs a multi-stage classifier:
- Structural heuristics: Does the query mention specific entities (documents, folders, dates)? What's the grammatical structure?
- Domain heuristics: Is this a document operation, a knowledge operation, a workspace operation, or an intelligence operation?
- Confidence scoring: If the query could mean two things, which is more likely given context?
For example, the query "find contracts from Q4" triggers:
- A date range extraction (Q4 → specific month range)
- A document search intent (not a folder operation)
- A knowledge retrieval domain classification
The heuristics engine assigns a confidence score. If it's below a threshold, the system asks for clarification. If it's high enough, the intent moves to the router.
This is why Universal Command (Ctrl+Shift+A) can handle natural language. You don't have to say "SEARCH documents WHERE type=contract AND date>Q4-start." You can just type what you'd say to a colleague.
The Intent Handler Registry: 87 Organized Functions
The router doesn't execute intents directly. It delegates to intent handlers (lib/intelligence/intentHandlers.ts), organized into five domains:
Document Operations (23 handlers)
MOVE_DOCUMENTS,COPY_DOCUMENTS,DELETE_DOCUMENTSRENAME_DOCUMENT,CHANGE_DOCUMENT_TYPEEXPORT_DOCUMENT,SHARE_DOCUMENT,ARCHIVE_DOCUMENT- And 15 more
Knowledge Operations (18 handlers)
SEARCH_KNOWLEDGE,CREATE_KNOWLEDGE_ENTRYUPDATE_KNOWLEDGE_ENTRY,DELETE_KNOWLEDGE_ENTRYLINK_KNOWLEDGE_TO_DOCUMENT,EXTRACT_KNOWLEDGE- And 12 more
Workspace Operations (16 handlers)
CREATE_WORKSPACE,DELETE_WORKSPACE,RENAME_WORKSPACEADD_MEMBER,REMOVE_MEMBER,CHANGE_ROLEBULK_IMPORT_DOCUMENTS,BULK_EXPORT_WORKSPACE- And 9 more
Intelligence Operations (18 handlers)
GENERATE_SUMMARY,GENERATE_DOCUMENT,ANSWER_QUESTIONANALYZE_SENTIMENT,EXTRACT_ENTITIES,CLASSIFY_DOCUMENTSUGGEST_NEXT_STEPS,FIND_SIMILAR_DOCUMENTS- And 10 more
System Operations (12 handlers)
UNDO_ACTION,REDO_ACTION,SAVE_WORKSPACEEXPORT_AUDIT_LOG,CONFIGURE_SETTINGS- And 7 more
Each handler is a pure function that takes an intent object and returns a result:
const moveDocumentsHandler = async (intent) => {
const { documentIds, targetFolderId } = intent.payload;
// Validate
if (!documentIds.length) throw new Error("No documents selected");
if (!targetFolderId) throw new Error("No target folder");
// Execute
const moved = await moveDocsToFolder(documentIds, targetFolderId);
// Return standardized result
return {
success: true,
affectedCount: moved.length,
action: "MOVE_DOCUMENTS",
undo: () => moveDocsToFolder(documentIds, previousFolderId)
};
};
Handlers are composable. If you want to "move documents and notify team members," the router chains two handlers: MOVE_DOCUMENTS → NOTIFY_TEAM. The output of one becomes input context for the next.
The Action Executor: Unified Dispatch
The Action Executor (lib/intelligence/actionExecutor.ts) is the glue. It takes a classified intent, finds the right handler, executes it, and manages side effects:
async function executeIntent(intent) {
// 1. Find handler
const handler = handlerRegistry.get(intent.type);
if (!handler) throw new Error(`Unknown intent: ${intent.type}`);
// 2. Check permissions
const allowed = await checkPermissions(intent, currentUser);
if (!allowed) throw new Error("Permission denied");
// 3. Execute
const result = await handler(intent);
// 4. Log audit trail
await logAction(intent, result, currentUser);
// 5. Trigger side effects (UI updates, webhooks, etc.)
await triggerSideEffects(result);
// 6. Return result
return result;
}
This single execution path means:
- Audit logging is automatic. Every intent goes through the same logger.
- Permissions are centralized. We check them once, not in 87 different places.
- Undo/redo works everywhere. Because every handler returns an undo function, the system can undo any action.
- Rate limiting is uniform. We throttle at the dispatcher, not at each handler.
Why This Matters for You
If you use AiFiler, this architecture is invisible. But it changes what you can do:
Faster workflows: Universal Command (Ctrl+Shift+A) works because intent classification is fast. Type "move Q4 contracts to archive" and it's done in one command, not three menu clicks.
Consistent behavior: Whether you batch-move documents via the UI, the API, or a scheduled task, the behavior is identical. No surprises.
Reliable undo: Every action—document move, knowledge entry creation, batch export—can be undone because undo is built into the executor, not bolted on afterward.
Extensibility: When we add a new intent (say, "generate executive summary"), we add one handler function. It automatically works in Universal Command, in the API, in batch operations, everywhere. No rewiring needed.
Auditability: Every action is logged consistently, with the same metadata structure. If you need to know who moved which documents when, that data is there.
The Real Win: Thinking in Intents
The deeper benefit is architectural. By treating intent as a first-class concept, we've decoupled what the user wants from how they ask for it.
A user can ask via:
- Keyboard shortcut (Universal Command)
- UI menu click
- API call
- Voice command (coming soon)
- Scheduled automation
But the intent is the same. The handler is the same. The audit trail is the same.
This is why adding a new feature no longer means touching five different files. It means adding one handler to the registry and one heuristic rule. The rest of the system adapts automatically.
That's not just cleaner code. That's a system that scales with features, not against them.
Enjoyed this article?
Get more articles like this delivered to your inbox. No spam, unsubscribe anytime.



