Skip to main content

Documentation Index

Fetch the complete documentation index at: https://grantmaster.dev/llms.txt

Use this file to discover all available pages before exploring further.

Agent Execution Architecture

StatusUpdatedCovered Files
🟢 Stable2026-02-21features/agents/types.ts, AgentExecutionService.ts, AgentToolRegistry.ts, agentDefinitions.ts, AgentQuotaService.ts

Overview

The Agent Execution Architecture enables autonomous, multi-step AI operations that combine existing services (Gemini AI, RAG, compliance) under a credit-based quota system. Agents inherit the triggering user’s RBAC permissions, enforce step/credit budgets per run, and support human-in-the-loop escalation.
User triggers agent run


┌──────────────────────┐
│ AgentExecutionService │  Orchestrates the entire lifecycle
│  ├─ startRun()       │  Validates → reserves credits → creates run doc
│  ├─ executeStep()    │  Invokes tools → consumes credits
│  ├─ pauseRun()       │  Creates escalation (human-in-the-loop)
│  ├─ resumeRun()      │  Resolves escalation → continues
│  ├─ completeRun()    │  Releases unused credits
│  └─ failRun()        │  Releases all reserved credits
└──────────────────────┘

        ├──▶ AgentToolRegistry      (tool lookup & permission check)
        ├──▶ CreditService          (reserve / consume / release)
        ├──▶ AgentQuotaService       (tier-based limits)
        └──▶ EventBus               (lifecycle events)

Key Components

AgentExecutionService

File: src/features/agents/services/AgentExecutionService.ts The central orchestrator. Each method is a Firestore transaction-backed state transition on the agent run document stored at organizations/{orgId}/agent_runs/{runId}.
MethodState TransitionActions
startRun()queuedrunningValidates definition, checks permissions, checks quota (concurrent + monthly limits), reserves credits, creates Firestore doc, emits AGENT_TASK_STARTED
executeStep()runningrunningValidates step budget, looks up tool in registry, invokes tool, consumes credits from reservation, emits AGENT_STEP_COMPLETED
pauseRun()runningawaiting_humanCreates AgentEscalation record, emits AGENT_ESCALATION_REQUIRED
resumeRun()awaiting_humanrunningRecords escalation resolution, emits AGENT_ESCALATION_RESOLVED
completeRun()runningcompletedReleases unused credits, computes final token/duration stats, emits AGENT_TASK_COMPLETED
failRun()runningfailedReleases all reserved credits, records error, emits AGENT_TASK_FAILED
cancelRun()any active → cancelledReleases all reserved credits, emits AGENT_TASK_CANCELLED

AgentToolRegistry

File: src/features/agents/services/AgentToolRegistry.ts A singleton registry mapping tool names to AgentTool implementations. Tools wrap existing service methods without modifying the original services.
// Register a tool
agentToolRegistry.register(createAgentTool({
  name: 'generate_journal',
  description: '...',
  requiredPermissions: [Permission.USE_AI_WIZARD, Permission.EDIT_OWN_ENTRIES],
  execute: async (input, context) => { /* calls geminiService */ },
}));

// Lookup at execution time
const tool = agentToolRegistry.getTool('generate_journal');
const result = await tool.execute(input, context);
Built-in tools (registered at module initialization):
ToolCreditsWrapsRequired Permissions
generate_journal5geminiService.generateJournalEntries()USE_AI_WIZARD, EDIT_OWN_ENTRIES
analyze_compliance8geminiService.analyzeCompliance()VIEW_JOURNALS, VIEW_PROJECTS
forecast_budget10geminiService.generateProjectForecast()VIEW_PROJECTS, VIEW_PROJECT_ANALYTICS
scan_expense3geminiService.analyzeReceipt()VIEW_EXPENSES
generate_report15geminiService.generateReportNarrative()GENERATE_REPORTS
query_documents2ragService.queryRAG()VIEW_PROJECTS
Each tool wrapper performs permission verification before execution and returns a standardized AgentToolResult with success, output, creditsUsed, and optional error.

AgentQuotaService

File: src/features/agents/services/AgentQuotaService.ts Enforces tier-based limits before agent runs start:
  • Monthly run limit: maxAgentRunsPerMonth from TierLimits
  • Concurrent agent limit: maxConcurrentAgents — counts runs with status running or awaiting_human
  • Hourly rate limit: agentRunsPerHour
  • Per-run step budget: maxAgentStepsPerRun
  • Per-run token budget: maxAgentTokenBudgetPerRun

Agent Definitions

File: src/features/agents/services/agentDefinitions.ts Static configuration for each agent type. The AGENT_DEFINITIONS array is looked up by ID during startRun() to validate the request and enforce budgets.
AgentIDFeature GateMax StepsCredit BudgetCategory
Compliance Checkercompliance_checkerAGENT_MULTI_STEP1030compliance
Report Generatorreport_generatorAGENT_MULTI_STEP1550reporting
Grant Proposal Writergrant_proposal_writerAGENT_AUTONOMOUS2080grants
Expense Auditorexpense_auditorAGENT_MULTI_STEP1540finance
Journal Assistantjournal_assistantAGENT_BASIC515journals

Run Lifecycle Flow

                  startRun()

        ┌─────────────▼─────────────┐
        │         queued            │
        └─────────────┬─────────────┘
                      │ (validation passes)
        ┌─────────────▼─────────────┐
   ┌───▶│         running           │◀──────────┐
   │    └──┬──────────┬──────────┬──┘            │
   │       │          │          │               │
   │  executeStep() pauseRun() failRun()    resumeRun()
   │       │          │          │               │
   │       ▼          ▼          ▼               │
   │   (next step) awaiting   failed             │
   │       │       _human        │               │
   │       │          │          │               │
   └───────┘          └──────────────────────────┘

   completeRun()


    completed

Credit Integration

Every agent run is backed by a credit reservation created at startRun() time:
  1. Reserve: creditService.reserveCredits(orgId, budget, runId) — atomically checks available balance and creates a reservation
  2. Consume: After each executeStep(), creditService.consumeCredits(orgId, reservationId, actualCredits) deducts from the reservation
  3. Release: On completeRun(), cancelRun(), or failRun(), creditService.releaseCredits() returns uncommitted credits to the pool
This ensures concurrent agent runs cannot overdraw the organization’s credit balance.

Event Emissions

The execution service emits these events at each lifecycle transition:
EventWhenKey Payload Fields
AGENT_TASK_STARTEDRun successfully queuedagentType, agentRunId, triggeredBy, creditBudget
AGENT_STEP_COMPLETEDStep finishesagentRunId, stepIndex, toolName, creditsUsed, durationMs
AGENT_ESCALATION_REQUIREDRun paused for human inputagentRunId, reason, options
AGENT_ESCALATION_RESOLVEDHuman responds to escalationagentRunId, resolvedBy, resolution
AGENT_TASK_COMPLETEDRun finishes successfullyagentRunId, totalCreditsUsed, totalSteps, durationMs
AGENT_TASK_FAILEDRun fails with erroragentRunId, error, failedAtStep
AGENT_TASK_CANCELLEDRun manually cancelledagentRunId, cancelledBy
AGENT_BUDGET_CONSUMEDCredit budget ≥80% consumedagentRunId, percentageUsed, creditsRemaining

Security Model

  • Agents inherit the triggering user’s RBAC permissions — captured at run start in AgentRun.permissions
  • Each tool checks permissions before execution via createAgentTool() wrapper
  • The AgentDefinition.requiredPermissions array is validated at startRun() time
  • Feature entitlement gates (AGENT_BASIC, AGENT_MULTI_STEP, AGENT_AUTONOMOUS) are checked via the entitlement system

Firestore Data Model

organizations/{orgId}/
  agent_runs/{runId}         ← AgentRun document
    steps: AgentStep[]       ← Embedded array (not subcollection)
    escalation?: AgentEscalation  ← Embedded object
  creditReservations/{id}    ← Managed by CreditService

Adding a New Agent

  1. Define the agent in agentDefinitions.ts:
    const MY_AGENT: AgentDefinition = {
      id: 'my_agent',
      name: 'My Agent',
      description: '...',
      requiredFeature: Feature.AGENT_MULTI_STEP,
      requiredPermissions: [Permission.VIEW_PROJECTS],
      allowedTools: ['query_documents', 'generate_report'],
      maxSteps: 10,
      defaultCreditBudget: 25,
      icon: 'Sparkles',
      category: 'general',
    };
    
  2. Add to AGENT_DEFINITIONS array
  3. Register any new tools in AgentToolRegistry.registerBuiltInTools()

Adding a New Tool

  1. Add to registerBuiltInTools() in AgentToolRegistry.ts:
    registry.register(createAgentTool({
      name: 'my_tool',
      description: '...',
      requiredPermissions: [Permission.SOME_PERM],
      execute: async (input, context) => {
        const { myService } = await import('@/features/my/services/myService');
        const result = await myService.doSomething(input.data as string);
        return { success: true, output: { result }, creditsUsed: 5 };
      },
    }));
    
  2. Add the tool name to the allowedTools array of relevant agent definitions

Maintenance

Update this document when:
  • Adding new agent definitions or tools
  • Changing the credit reservation/consumption flow
  • Modifying the run state machine
  • Adding new event emissions