Service Layer

Description

The collection of external integrations and shared services at src/services/. Contains 22+ subdirectories and 133+ TypeScript files organized by functional domain. The architecture uses module-level singletons with lazy initialization rather than a dependency injection container — services are accessed directly via imports, initialized in a specific order during startup, and designed to fail open when non-critical.

Architectural pattern

No DI container. Services use three access patterns:

1. Module-level singletons with lazy init

// analytics/index.ts — queue events until sink is attached
let sink: AnalyticsSink | null = null
const eventQueue: QueuedEvent[] = []
export function attachAnalyticsSink(newSink: AnalyticsSink): void { ... }
export function logEvent(...) { ... }

2. Promise-based initialization

// policyLimits/index.ts — initialize loading promise early, await later
let loadingCompletePromise: Promise<void> | null = null
export function initializePolicyLimitsLoadingPromise(): void { ... }
export async function waitForPolicyLimitsToLoad(): Promise<void> { ... }

3. Class-based with getInstance()

// diagnosticTracking.ts — classic singleton
export class DiagnosticTrackingService {
  private static instance: DiagnosticTrackingService | undefined
  static getInstance(): DiagnosticTrackingService { ... }
}
export const diagnosticTracker = DiagnosticTrackingService.getInstance()

The pattern is pragmatic: no factory, no service locator, just module-level state and lazy initialization. Services reference each other directly via imports.

Service catalog

Tier 1: Critical infrastructure

Analytics (services/analytics/) - Files: index.ts, sink.ts, config.ts, metadata.ts, datadog.ts, firstPartyEventLogger.ts, growthbook.ts - The central event bus — every user action flows through logEvent() - Queue + async attachment pattern: events queue until the analytics sink is initialized - Sinks: Datadog, 1P event logging (OpenTelemetry)

OAuth (services/oauth/) - 10+ files: client.ts, crypto.ts, auth-code-listener.ts, types.ts, etc. - OAuth 2.0 with PKCE — both automatic (browser) and manual (paste) flows - Class-based: new OAuthService() per request - Supports both Claude.ai (OAuth) and API key authentication

API client (services/api/) - client.ts — Anthropic SDK client factory with multi-provider support (Direct, AWS Bedrock, Azure Foundry, GCP Vertex AI) - bootstrap.ts — initial configuration/model data fetch - filesApi.ts, usage.ts, referral.ts, adminRequests.ts — domain-specific calls - Stateless functions, no service object

Tier 2: Policy and configuration

Policy limits (services/policyLimits/) - Organization-level feature gating (e.g., allow_remote_sessions) - Promise-based init + file caching + HTTP ETag validation + background polling (1-hour intervals) - Eligibility: Console users (API key), Enterprise/Team OAuth users - Fail-open design: errors don't block startup

Remote managed settings (services/remoteManagedSettings/) - Enterprise config delivery — remote settings override local settings.json - Same pattern as policyLimits: promise init + file cache + ETag + polling - Security checks before applying remote settings - 7 files including syncCache.ts, securityCheck.jsx

Tier 3: Memory and context management

Session memory (services/SessionMemory/) - Automatically extracts key information into markdown as conversation progresses - Runs as a forked subagent in the background without blocking - Triggered by token count thresholds or tool call counts - Feature-gated: tengu_session_memory - Output: ~/.claude/session-memory/{session-id}.md

Compaction (services/compact/) - 10+ files: context window management (full compact, micro compact, session memory compact) - See the auto-compact entity for full details

Auto-dream (services/autoDream/) - Background memory consolidation - Four-phase consolidation prompt: Orient → Gather → Consolidate → Prune - Consolidation lock prevents concurrent runs across sessions

Team memory sync (services/TeamMemorySync/) - Sync team memory across agents in swarms - File-based + promise initialization

Tier 4: Feature services

MCP (services/mcp/) - 20+ files — the most complex service - client.ts — MCP client lifecycle and tool/resource management - config.ts — parse claude_desktop_config.json and enterprise configs - auth.ts — OAuth token handling for MCP servers - vscodeSdkMcp.ts — VS Code extension integration - claudeai.ts — Claude.ai MCP server fetching - Complex state machine for connection lifecycle; merges user + enterprise configs; validates against policy limits

LSP (services/lsp/) - Language Server Protocol management for IDE integrations - Singleton manager instance - Initialize after plugin dir, shutdown on exit

Plugins (services/plugins/) - Third-party extensibility via NPM packages - Scopes: global, user, project

Tool execution (services/tools/) - StreamingToolExecutor.ts — execute tools with streaming output - toolExecution.ts — core execution logic - toolOrchestration.ts — multi-tool coordination - toolHooks.ts — lifecycle hooks

Tier 5: AI/UX features

Service Purpose
PromptSuggestion/ AI-powered prompt suggestions (feature-gated)
AgentSummary/ Summarize agent thoughts
MagicDocs/ Document generation
toolUseSummary/ Track tool usage patterns
tips/ Feature tip registry

Tier 6: Notifications and diagnostics

Service Purpose
notifier.ts Terminal notifications (iTerm2, Kitty, Ghostty, Terminal.app)
voice.ts, voiceStreamSTT.ts Voice input/output processing
diagnosticTracking.ts IDE diagnostic tracking (singleton)

Tier 7: Utilities

Service Purpose
tokenEstimation.ts Token counting and estimation
claudeAiLimits.ts Claude.ai quota/rate limit checks
preventSleep.ts Prevent system sleep during operations
mockRateLimits.ts Test mocking
vcr.ts HTTP recording/playback for tests

Initialization sequence

Services are initialized in src/entrypoints/init.ts (lines 57-200) in this order:

  1. Config validation (enableConfigs())
  2. Safe environment variables + TLS certificates
  3. Graceful shutdown handler
  4. Parallel async: 1P logging, GrowthBook, OAuth info, IDE detection, GitHub detection
  5. Remote settings promise init (if eligible)
  6. Network: mTLS → proxy agents → API preconnection
  7. Platform-specific: Windows shell, LSP cleanup, team cleanup

The ordering is critical: network must be configured before any API calls, config before feature flags, and environment variables before TLS.

Criticality levels

Blocks startup on failure: - Analytics sink attachment - mTLS / proxy configuration - OAuth token refresh (if using OAuth)

Non-blocking, fail-open: - Policy limits fetch - Remote managed settings - Bootstrap data (model list) - LSP initialization - All feature services

Optional (feature-gated): - SessionMemory (tengu_session_memory) - PromptSuggestion (tengu_prompt_suggestions) - MCP server management

Service interactions

Analytics is the hub — almost every service logs events via logEvent().

Policy limits and remote managed settings are independent of each other but follow the same pattern: load during init, cache to disk with ETags, poll hourly, provide waitFor*ToLoad() promises.

Session memory depends on GrowthBook (feature gates), post-sampling hooks (trigger), forked agent (background extraction), and analytics (logging).

MCP is the most interconnected: config merging (user + enterprise), policy limit validation, OAuth token refresh, VS Code communication.

What depends on it

Design trade-offs

Decision Trade-off
No DI container Simple imports, no container overhead, but service dependencies are implicit (not declarable/testable)
Fail-open for non-critical services Startup never blocked by optional features, but failures can be silent
Promise-based lazy init Services load in background, but callers must remember to await waitFor*ToLoad()
Queue + attachment (analytics) Events never lost during startup, but queue grows unbounded until sink attaches
File-based caching with ETags Survives process restarts, minimal network, but stale data possible if file is corrupted
1-hour polling for remote settings Low overhead, but config changes take up to an hour to propagate

Key claims

Relations

Sources

Source code at src/services/, initialization at src/entrypoints/init.ts