Service Layer
- Entity ID:
ent-20260410-01b3fca2995f - Type:
concept - Scope:
shared - Status:
active - Aliases: services, service architecture
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:
- Config validation (
enableConfigs()) - Safe environment variables + TLS certificates
- Graceful shutdown handler
- Parallel async: 1P logging, GrowthBook, OAuth info, IDE detection, GitHub detection
- Remote settings promise init (if eligible)
- Network: mTLS → proxy agents → API preconnection
- 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
- Entrypoint system — initializes services during boot
- QueryEngine — uses API client, analytics, compaction, session memory
- Agent lifecycle — agents inherit service state from parent session
- Tool system — tools access services for execution context
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
- 22+ service modules covering infrastructure, policy, memory, features, and utilities
- Module-level singletons with lazy initialization — no DI container
- Fail-open design: non-critical services don't block startup
- Analytics is the central hub — all services log through it
- MCP is the most complex service (20+ files, state machine lifecycle)
Relations
rel-service-entrypoint: entrypoint-system --[initializes]--> service-layerrel-service-analytics: service-layer --[centers-on]--> analyticsrel-service-compact: service-layer --[contains]--> auto-compactrel-service-mcp: service-layer --[contains]--> mcp-subsystem
Sources
Source code at src/services/, initialization at src/entrypoints/init.ts