Spotter Agent — Code Map
Module wiring
apps/api/src/ai/ai.module.ts— registers controller, orchestrator, runtime services, tool registry, and every leaf provider. Imports the resource modules whose services the leaves call.
Controller
apps/api/src/ai/agent/agent.controller.tsGET /organizations/:orgId/agent/usageGET /organizations/:orgId/agent/conversationsGET /organizations/:orgId/agent/conversations/:conversationIdPOST /organizations/:orgId/agent/messagesPOST /organizations/:orgId/agent/conversations/:conversationId/confirm/:toolUseIdPOST /organizations/:orgId/agent/conversations/:conversationId/confirm-batchPOST /organizations/:orgId/agent/conversations/:conversationId/pick/:toolUseIdPOST /organizations/:orgId/agent/conversations/:conversationId/undo/:toolUseId
Orchestration / runtime
apps/api/src/ai/agent/agent.orchestrator.ts—run,resume,resumeAfterPick,driveModelLoop,tryQuickRoute(T1 Haiku router),trySeedIntent(T0),processToolUses(+planParallelReads/runReadsConcurrently),withCachedTail,tallyUsage,finalize.apps/api/src/ai/cost.util.ts—MODEL_RATES(keyed by base model id; every policy model priced, enforced bymodel-policy.unit.spec.ts),computeCostUsdMicros(per-model, 5m vs 1h cache-write rates). Shared with the parse module.apps/api/src/ai/agent/replay-slimming.ts—slimStaleToolResults: replay-time digest of old, large tool results.apps/api/src/ai/agent/agent.runtime.ts—AgentRuntimetype (orgId, clerkId, user, membership, role, locale, now, pageContext, abortSignal).apps/api/src/ai/agent/anthropic.client.ts— Anthropic SDK wrapper.apps/api/src/ai/model-policy.ts— the model standard (ADR-0022):MODEL_POLICY(role → model + call shape; Spotter’s roles arespotterLoop,spotterQuickRoute,spotterQuickAnswer,spotterTitle,spotterCompaction),requestParams,spotterLoopCall+EARLIER_SPOTTER_LOOP_CALLS(a resumed tool call finishes on the model that made it),thinks,callToolOnly;RETIRED_MODELS.AgentOrchestrator.loopHistorypicks a run’s call and replays thinking only to the model that wrote it.apps/api/src/ai/agent/context-builder.service.ts—buildSystem(runtime)(static prompt plusDAY_GRAIN_PROMPT, and org block, fully stable, bothcache_control5m) andbuildTurnContext(clock + page + playbook + ambient, stored on the user message).apps/api/src/ai/agent/turn-context.ts—<turn_context>block helpers:wrapTurnContext,hasTurnContext,stripTurnContext,visibleText,dropAmbientFromTurnContext. The single place that knows how to tell user text from turn context.apps/api/src/ai/agent/conversations.service.ts— persistence: create, listForUser, getOwn, listMessages, listMessagesForReplay, appendMessage, insertToolExecution, updateToolExecutionStatus, listToolExecutionsForMessage, hasOtherPendingForMessage, touchAfterTurn, getToolExecutionByToolUseId.apps/api/src/ai/agent/rate-limit.service.ts—preCheck,getUsageSnapshot.apps/api/src/ai/agent/cost-tracker.service.ts—recordTurn,getOrgSpendTodayMicros.apps/api/src/ai/agent/audit.adapter.ts— bridges toaudit_logswithmetadata.agent: true.apps/api/src/ai/agent/compaction.service.ts—generateTitleIfMissing,compactIfNeeded. Haiku-driven, time-budgeted.apps/api/src/ai/agent/rag.service.ts—findAmbient, hybrid lex+semantic search across org entities.
Observability
apps/api/src/ai/agent/observability/agent-observability.service.ts—emit,logTurnFailure,logReplayViolation,newTraceId. Fans out to Pino + Sentry + PostHog.apps/api/src/ai/agent/observability/error-classifier.ts—classifyAgentError,ReplayIntegrityError.apps/api/src/ai/agent/observability/replay-validator.ts—validateReplay,validateAndRepairReplay.apps/api/src/ai/agent/observability/storage-snapshot.service.ts—@ScheduledJob(…, '0 3 * * *')runSnapshot. Emitsagent.storage.snapshotevent.
Tool registry / runner
apps/api/src/ai/agent/tools/tool-registry.service.ts— discovers@AgentTool-decorated methods atonModuleInit, indexes by<router>.<action>.definitionsForRole(role)is the provider-neutral projection (role scope);toolsForRole(role)builds Anthropic tool definitions grouped by router from it, and the MCP server (docs/features/mcp-server) builds flat MCP tools from it.apps/api/src/ai/agent/tools/tool-runner.service.ts—execute({ router, action, rawInput, runtime }). RBAC defence, Zod validation, handler invocation, audit write, output → resourceId extraction.apps/api/src/ai/agent/tools/tool.types.ts—AgentToolMetadata(incl.mcp),ToolHandler,ToolConfirmationPreviewHandler,ToolRunResult,ToolScope,ToolAuditMeta,ToolInverseMeta,AgentToolKind.apps/api/src/ai/agent/confirmation-preview.ts— strips the reserved preview key from untrusted tool input, attaches only server-resolved confirmation data, and separates it from executable input on conversation reload.apps/api/src/ai/agent/tools/tool-feature-gate.service.ts—ToolFeatureGate.require(orgId, feature): leaf-side mirror ofPlatformTierGuardfor tools whose HTTP controllers carry@RequiresFeature(automations→email_automations;inboxand the leads bulk/pipeline admin tools →lead_management).apps/api/src/ai/agent/tools/decorators.ts—@AgentTool(opts). Stores execution, confirmation-preview, audit, inverse, and routing metadata via Reflect.
Tool leaves (per router)
| File | Router | Actions |
|---|---|---|
apps/api/src/ai/agent/tools/leaves/read.tools.ts | read | get_current_context (registered, hidden from the model), programs_list, members_search, members_get, exercises_search, exercises_resolve_batch, ask_user_to_pick (user_picker), search_anything, lookup_by_id, workout_comments_in_program, workout_comments_for_assignment |
apps/api/src/ai/agent/tools/leaves/workouts.tools.ts | workouts | create (inverse: delete), update, set_sections, delete (destructive), upsert |
apps/api/src/ai/agent/tools/leaves/parse.tools.ts | workouts | parse |
apps/api/src/ai/agent/tools/leaves/programs.tools.ts | programs | create (inverse: delete), update, delete (destructive), enroll_member |
apps/api/src/ai/agent/tools/leaves/assignments.tools.ts | assignments | assign_personal, replace_program_day (destructive), remove_program_day (destructive), update, set_published, delete (destructive), mark_comments_read, bulk_preview, bulk_delete (destructive), bulk_publish (always), list, get, my_week, my_today, coach_overview, complete (inverse: uncomplete), uncomplete, skip, create (inverse: delete), assign_schedule (always) |
apps/api/src/ai/agent/tools/leaves/bookings.tools.ts | bookings | list_mine, attendance_summary, attendance_trend, attendance_daily, book (always), cancel (always), admin_cancel (always), refund_credit (always) |
apps/api/src/ai/agent/tools/leaves/class-sessions.tools.ts | class_sessions | list_for_date, create (inverse: cancel), create_many (always; timetable × dates in one call), update, publish (inverse: unpublish), unpublish, cancel (destructive), bulk_preview, bulk_delete (destructive), bulk_publish (always), roster, attendance_history, qr_token, check_in (inverse: undo_check_in), undo_check_in, set_workouts (always), set_day_workouts (always), create_inline_workout, delete (destructive), copy_week (always) |
apps/api/src/ai/agent/tools/leaves/class-types.tools.ts | class_types | list_for_program |
apps/api/src/ai/agent/tools/leaves/daily-programming.tools.ts | daily_programming | list_for_week, list_for_date, set (always), set_status (always), remove (destructive) |
apps/api/src/ai/agent/tools/leaves/analytics.tools.ts | analytics | revenue_summary, revenue_trend, members_summary, members_growth, at_risk_members, popular_classes, class_utilization, coach_overview, org_insights, plan_distribution, members_activation, workouts_summary |
apps/api/src/ai/agent/tools/leaves/tasks.tools.ts | tasks | list, create (inverse: delete), update, complete, delete (destructive) |
apps/api/src/ai/agent/tools/leaves/program-templates.tools.ts | program_templates | list, get, preview, create (inverse: delete), update, duplicate (inverse: delete), upsert_cells (always), apply (always), from_history (always), delete (destructive) |
apps/api/src/ai/agent/tools/leaves/forms.tools.ts | forms | list_pending_for_org, compliance_status_for_member, templates_list, template_get, coverage, template_create, template_update, template_publish (always), template_bump_version (always), template_set_auto_issue, template_set_required_for_booking (always), template_widen_to_plan_group (always), template_archive (destructive), template_delete (destructive), assign, assign_all_missing (always), remind (always), email_link (always), instance_archive (destructive) |
apps/api/src/ai/agent/tools/leaves/memberships.tools.ts | memberships | list, stats, roster_summary, invitations_list, invite, create (always), update, bulk_invite (always), send_invitation (always), resend_invitation (always), revoke_invitation (destructive), erase (destructive) |
apps/api/src/ai/agent/tools/leaves/leads.tools.ts | leads | list, get, create, update, convert (always), import (always), pipelines_list, campaigns_list, add_to_pipeline, move_stage, tags_list, set_tags, archive (inverse: restore), restore, analytics, bulk_move_stage_preview, bulk_move_stage (always), bulk_add_to_pipeline (always), bulk_set_tags (always), bulk_archive (always), bulk_email (always), pipeline_create, pipeline_update, stage_add, stage_update, stage_remove (destructive), stages_reorder (always), campaign_create |
apps/api/src/ai/agent/tools/leaves/inbox.tools.ts | inbox | list, get, start (always), send (always), assign, set_status, mark_read |
apps/api/src/ai/agent/tools/leaves/automations.tools.ts | automations | list, get, preview, create (inverse: remove), update, set_status (always), run (always), remove (destructive) |
apps/api/src/ai/agent/tools/leaves/messages.tools.ts | messages | send |
apps/api/src/ai/agent/tools/leaves/announcements.tools.ts | announcements | create |
apps/api/src/ai/agent/tools/leaves/help.tools.ts | help | search |
apps/api/src/ai/agent/tools/leaves/arbox.tools.ts | arbox | status (owner only, flag-gated, read only) |
apps/api/src/ai/agent/tools/leaves/booking-policy.tools.ts | booking_policy, plans, memberships | booking_policy: explain, effective, rules_list, rule_create (always), rule_update (always), rule_archive (destructive); plans: quotas_list, quotas_set (always); memberships: quota_balances. See scheduling-bookings code map. |
apps/api/src/ai/agent/tools/leaves/bulk-tools.unit.spec.ts, core-tools.unit.spec.ts, leads-tools.unit.spec.ts, forms.tools.unit.spec.ts | (tests) | Bulk-tool semantics; TKN-305 catalog parity / confirm policy / role scope / delegation contracts; leads + forms leaf contracts. |
Total: 179 tools across 20 routers, 178 offered to the model (FIT-161 phase 1 shipped 63 across 11; the leads router carries the CRM surface; TKN-305 added the members / forms / automations / scheduling / programming / inbox operations surface — 91 tools and the automations, daily_programming, inbox routers; TKN-258 added the read-only arbox router). class_sessions.copy_week is the one Spotter tool the “Build the week with Spotter” flow adds beyond what a coach could already do by hand — it materializes last week’s live sessions into this week as drafts, in one write and one confirmation card, so Spotter can act on “copy last week” without ever composing programming itself.
Tool input/output schemas
libs/shared/src/lib/agent-schemas/index.ts— barrel.libs/shared/src/lib/agent-schemas/common.ts—AgentPageContext,AgentMessageRequest, structuredAgentConfirmationPreview(resolved target/impact, complete messages, old/new changes, validated exact arguments), conversation summary / detail / loaded message / loaded tool call.libs/shared/src/lib/agent-schemas/sse-events.ts— full SSE event discriminated union, including optional server-resolved confirmation impact.libs/shared/src/lib/agent-schemas/tool-categories.ts—AGENT_ROUTERS, per-router*_ACTIONSenums,ToolSideEffect,ToolConfirmPolicy.libs/shared/src/lib/agent-schemas/read.ts— everyread.*input schema.libs/shared/src/lib/agent-schemas/workouts.ts,programs.ts,assignments.ts,bookings.ts,analytics.ts,class-sessions.ts,class-types.ts,tasks.ts,forms.ts,leads.ts,memberships.ts,automations.ts,daily-programming.ts,inbox.ts,program-templates.ts— per-router input schemas.
Embeddings (supporting infra)
apps/api/src/ai/embeddings/embeddings.module.tsapps/api/src/ai/embeddings/embedding.service.ts— Voyagevoyage-multilingual-2client.apps/api/src/ai/embeddings/embedding-helper.tsapps/api/src/ai/embeddings/ai-cache.service.ts— keyed by(model, input_hash), persisted toai_cache.apps/api/src/ai/embeddings/ai.controller.tsapps/api/src/ai/embeddings/{workouts,programs,member-profiles}-embedding.{service,processor}.ts— domain embedding pipelines.apps/api/src/ai/embeddings/exercise-enrichment.{service,processor}.ts— exercise canonicalization.
Frontend
apps/web/src/components/agent/agent-launcher.tsxapps/web/src/components/agent/agent-sheet.tsxapps/web/src/components/agent/agent-sheet-mount.tsxapps/web/src/components/agent/composer.tsxapps/web/src/components/agent/message-list.tsxapps/web/src/components/agent/tool-call-card.tsxapps/web/src/components/agent/streaming-text.tsxapps/web/src/components/agent/usage-footer.tsxapps/web/src/components/agent/conversation-list.tsxapps/web/src/components/agent/suggested-prompts.tsxapps/web/src/providers/agent-provider.tsx— state machine, SSE reducer, conversation cache.
Schema
libs/db/src/lib/schema/agent.ts—ai_conversations,ai_messages,ai_tool_executions,ai_usage_daily+ relations.libs/db/src/lib/schema/ai-cache.ts—ai_cache(embeddings + enrichment).libs/db/src/lib/schema/enums.ts—aiMessageRole,aiToolStatusenums.libs/db/src/lib/schema/admin.ts—audit_logs(written viaAgentAuditAdapter).
Tier mapping
libs/shared/src/lib/constants/platform-tiers.ts—PLATFORM_TIER_MAPsource of truth foraiDailyBudgetUsdMicros.
Tests
apps/api/src/ai/agent/tools/leaves/bulk-tools.unit.spec.ts— bulk tool semantics.apps/api/src/ai/agent/tools/tool-registry.definitions.unit.spec.ts— tool-definition shape (one summary per action, stripped noise keys, fixed order, hidden tools) and per-role size ceilings over the real leaves;tools/tool-definition-stats.tsis the measuring helper.apps/api/src/ai/agent/agent.orchestrator.parallel-reads.unit.spec.ts— concurrent read batches: ordering of events / rows / blocks, the concurrency bound, breaker bookkeeping, and the sequential fallback for mixed batches.apps/api/src/ai/agent/agent.orchestrator.usage.unit.spec.ts— per-call cost accounting and the T1 router’s cache marker.apps/api/src/ai/agent/agent.orchestrator.turn-context.unit.spec.ts,turn-context.unit.spec.ts— stored turn context: system byte-stability across runs, prefix equality, UI/preview/title/router hiding, resume and legacy conversations.apps/api/src/ai/agent/tools/confirmation-target.service.ts— names approval cards (confirmationTargetspecs → one org-scoped lookup);tools/confirmation-target.unit.spec.tsholds the “every confirmable tool names its card” contract.apps/api/src/ai/agent/agent.orchestrator.confirm-batch.unit.spec.ts,agent.orchestrator.confirmation-target.unit.spec.ts— held siblings, batch decisions, skip-after-decline, atomic validation, loaderpendingReason.apps/api/src/ai/agent/replay-slimming.unit.spec.ts,apps/api/src/ai/cost.util.unit.spec.ts,apps/api/src/ai/agent/context-builder.unit.spec.ts.apps/api/src/ai/agent/tools/leaves/core-tools.unit.spec.ts— TKN-305: shared-catalog ↔ decorator parity, JSON-schema renderability of every input schema, confirm policy, role scopes, tier gating, delegation.apps/api/src/ai/agent/tools/leaves/class-sessions.copy-week.tools.unit.spec.ts—copy_week’s metadata contract (always confirms, staff-only, audited) and delegation toClassSessionsService.copyWeek.- Per-leaf and orchestrator unit tests (where present) live colocated.