claude-code/spec/06_services_context_state.md
2026-04-01 01:20:27 +05:30

93 KiB

Claude Code — Services, Context, State & Screens

This document exhaustively covers all files in src/services/, src/context/, src/bootstrap/state.ts, src/coordinator/, src/server/, and src/screens/. Every exported symbol is listed with its full signature, key logic, configuration, and dependencies.


Table of Contents

  1. bootstrap/state.ts
  2. coordinator/coordinatorMode.ts
  3. server/types.ts
  4. server/createDirectConnectSession.ts
  5. server/directConnectManager.ts
  6. services/analytics/config.ts
  7. services/analytics/growthbook.ts
  8. services/analytics/metadata.ts
  9. services/analytics/index.ts
  10. services/analytics/sink.ts
  11. services/analytics/sinkKillswitch.ts
  12. services/analytics/datadog.ts
  13. services/analytics/firstPartyEventLogger.ts
  14. services/analytics/firstPartyEventLoggingExporter.ts
  15. services/api/bootstrap.ts
  16. services/api/client.ts
  17. services/api/claude.ts
  18. services/api/dumpPrompts.ts
  19. services/api/emptyUsage.ts
  20. services/api/errorUtils.ts
  21. services/api/errors.ts
  22. services/api/filesApi.ts
  23. services/api/firstTokenDate.ts
  24. services/api/grove.ts
  25. services/api/logging.ts
  26. services/api/metricsOptOut.ts
  27. services/api/overageCreditGrant.ts
  28. services/api/promptCacheBreakDetection.ts
  29. services/api/referral.ts
  30. services/api/sessionIngress.ts
  31. services/api/ultrareviewQuota.ts
  32. services/api/usage.ts
  33. services/api/withRetry.ts
  34. services/AgentSummary/agentSummary.ts
  35. services/autoDream/autoDream.ts
  36. services/autoDream/config.ts
  37. services/autoDream/consolidationLock.ts
  38. services/autoDream/consolidationPrompt.ts
  39. services/awaySummary.ts
  40. services/claudeAiLimits.ts
  41. services/claudeAiLimitsHook.ts
  42. services/compact/apiMicrocompact.ts
  43. services/compact/autoCompact.ts
  44. services/compact/compact.ts
  45. services/compact/compactWarningHook.ts
  46. services/compact/compactWarningState.ts
  47. services/compact/grouping.ts
  48. services/compact/microCompact.ts
  49. services/compact/postCompactCleanup.ts
  50. services/compact/prompt.ts
  51. services/compact/sessionMemoryCompact.ts
  52. services/compact/timeBasedMCConfig.ts
  53. services/diagnosticTracking.ts
  54. services/internalLogging.ts
  55. services/MagicDocs/magicDocs.ts
  56. services/MagicDocs/prompts.ts
  57. services/mcpServerApproval.tsx
  58. services/mockRateLimits.ts
  59. services/MCP (mcp/)
  60. services/notifier.ts
  61. services/preventSleep.ts
  62. services/PromptSuggestion/promptSuggestion.ts
  63. services/PromptSuggestion/speculation.ts
  64. services/rateLimitMocking.ts
  65. services/rateLimitMessages.ts
  66. services/SessionMemory/prompts.ts
  67. services/SessionMemory/sessionMemory.ts
  68. services/SessionMemory/sessionMemoryUtils.ts
  69. services/tokenEstimation.ts
  70. services/vcr.ts
  71. services/voice.ts
  72. services/voiceKeyterms.ts
  73. services/voiceStreamSTT.ts
  74. context/QueuedMessageContext.tsx
  75. context/fpsMetrics.tsx
  76. context/mailbox.tsx
  77. context/modalContext.tsx
  78. context/notifications.tsx
  79. context/overlayContext.tsx
  80. context/promptOverlayContext.tsx
  81. context/stats.tsx
  82. context/voice.tsx
  83. screens/Doctor.tsx
  84. screens/REPL.tsx
  85. screens/ResumeConversation.tsx

bootstrap/state.ts

Path: src/bootstrap/state.ts

Purpose: The single global session state singleton for the entire Claude Code process. Acts as the authoritative source of truth for all per-session metrics, model configuration, telemetry handles, and feature flags. Designed as a strict leaf in the import DAG — imports nothing from src/utils/ except via explicit safe indirection.

Key Types Exported:

export type ChannelEntry =
  | { kind: 'plugin'; name: string; marketplace: string; dev?: boolean }
  | { kind: 'server'; name: string; dev?: boolean }

export type AttributedCounter = {
  add(value: number, additionalAttributes?: Attributes): void
}

State Type (internal, not exported directly): Contains ~80 fields including:

  • originalCwd: string — resolved cwd at process start (NFC-normalized, symlinks resolved)
  • projectRoot: string — stable identity root (set at startup, never changed by mid-session EnterWorktreeTool)
  • totalCostUSD: number, totalAPIDuration: number, totalAPIDurationWithoutRetries: number
  • totalToolDuration: number, turnHookDurationMs: number, turnToolDurationMs: number
  • totalLinesAdded: number, totalLinesRemoved: number
  • cwd: string — current working directory (mutable, changes with shell.ts setCwd)
  • modelUsage: { [modelName: string]: ModelUsage } — per-model usage tracking
  • mainLoopModelOverride: ModelSetting | undefined, initialMainLoopModel: ModelSetting
  • sessionId: SessionId — UUID regenerated on clearConversation
  • parentSessionId: SessionId | undefined — previous session (for lineage tracking)
  • isInteractive: boolean, kairosActive: boolean, strictToolResultPairing: boolean
  • sdkAgentProgressSummariesEnabled: boolean, userMsgOptIn: boolean
  • clientType: string (default 'cli'), sessionSource: string | undefined
  • meter: Meter | null, sessionCounter, locCounter, prCounter, commitCounter, costCounter, tokenCounter, codeEditToolDecisionCounter, activeTimeCounter — OTel metrics
  • sessionId: SessionId (randomUUID at init), parentSessionId: SessionId | undefined
  • loggerProvider: LoggerProvider | null, eventLogger: ReturnType<typeof logs.getLogger> | null
  • meterProvider: MeterProvider | null, tracerProvider: BasicTracerProvider | null
  • agentColorMap: Map<string, AgentColorName>, agentColorIndex: number
  • lastAPIRequest, lastAPIRequestMessages, lastClassifierRequests, cachedClaudeMdContent
  • inMemoryErrorLog: Array<{ error: string; timestamp: string }>
  • inlinePlugins: string[], chromeFlagOverride: boolean | undefined
  • sessionBypassPermissionsMode: boolean, scheduledTasksEnabled: boolean
  • sessionCronTasks: SessionCronTask[], sessionCreatedTeams: Set<string>
  • sessionTrustAccepted: boolean, sessionPersistenceDisabled: boolean
  • hasExitedPlanMode: boolean, needsPlanModeExitAttachment: boolean, needsAutoModeExitAttachment: boolean
  • initJsonSchema: Record<string, unknown> | null, registeredHooks: Partial<Record<HookEvent, RegisteredHookMatcher[]>> | null
  • planSlugCache: Map<string, string> — sessionId → wordSlug
  • teleportedSessionInfo: { isTeleported, hasLoggedFirstMessage, sessionId } | null
  • invokedSkills: Map<string, { skillName, skillPath, content, invokedAt, agentId }> — keyed by "${agentId ?? ''}:${skillName}"
  • slowOperations: Array<{ operation, durationMs, timestamp }> — ant-only dev bar
  • sdkBetas: string[] | undefined, mainThreadAgentType: string | undefined
  • isRemoteMode: boolean, directConnectServerUrl: string | undefined
  • systemPromptSectionCache: Map<string, string | null>, lastEmittedDate: string | null
  • additionalDirectoriesForClaudeMd: string[], allowedChannels: ChannelEntry[], hasDevChannels: boolean
  • sessionProjectDir: string | null — transcript directory override
  • promptCache1hAllowlist: string[] | null, promptCache1hEligible: boolean | null
  • afkModeHeaderLatched: boolean | null, fastModeHeaderLatched: boolean | null
  • cacheEditingHeaderLatched: boolean | null, thinkingClearLatched: boolean | null
  • promptId: string | null, lastMainRequestId: string | undefined
  • lastApiCompletionTimestamp: number | null, pendingPostCompaction: boolean

Exported Functions (getters/setters/mutators):

export function getSessionId(): SessionId
export function regenerateSessionId(options?: { setCurrentAsParent?: boolean }): SessionId
export function getParentSessionId(): SessionId | undefined
export function switchSession(sessionId: SessionId, projectDir?: string | null): void
export const onSessionSwitch: Signal<[id: SessionId]>['subscribe']
export function getSessionProjectDir(): string | null
export function getOriginalCwd(): string
export function getProjectRoot(): string
export function setOriginalCwd(cwd: string): void
export function setProjectRoot(cwd: string): void  // --worktree startup only
export function getCwdState(): string
export function setCwdState(cwd: string): void
export function getDirectConnectServerUrl(): string | undefined
export function setDirectConnectServerUrl(url: string): void
export function addToTotalDurationState(duration: number, durationWithoutRetries: number): void
export function resetTotalDurationStateAndCost_FOR_TESTS_ONLY(): void
export function addToTotalCostState(cost: number, modelUsage: ModelUsage, model: string): void
export function getTotalCostUSD(): number
export function getTotalAPIDuration(): number
export function getTotalDuration(): number
export function getTotalAPIDurationWithoutRetries(): number
export function getTotalToolDuration(): number
export function addToToolDuration(duration: number): void
export function getTurnHookDurationMs(): number
export function addToTurnHookDuration(duration: number): void
export function resetTurnHookDuration(): void
export function getTurnHookCount(): number
export function getTurnToolDurationMs(): number
export function resetTurnToolDuration(): void
export function getTurnToolCount(): number
export function getTurnClassifierDurationMs(): number
export function addToTurnClassifierDuration(duration: number): void
export function resetTurnClassifierDuration(): void
export function getTurnClassifierCount(): number
export function getStatsStore(): { observe(name: string, value: number): void } | null
export function setStatsStore(store: { observe(name: string, value: number): void } | null): void
export function updateLastInteractionTime(immediate?: boolean): void
export function flushInteractionTime(): void
export function addToTotalLinesChanged(added: number, removed: number): void
export function getTotalLinesAdded(): number
export function getTotalLinesRemoved(): number
export function getTotalInputTokens(): number
export function getTotalOutputTokens(): number
export function getTotalCacheReadInputTokens(): number
export function getTotalCacheCreationInputTokens(): number
export function getTotalWebSearchRequests(): number
export function getTurnOutputTokens(): number
export function getCurrentTurnTokenBudget(): number | null
export function snapshotOutputTokensForTurn(budget: number | null): void
export function getBudgetContinuationCount(): number
export function incrementBudgetContinuationCount(): void
export function setHasUnknownModelCost(): void
export function hasUnknownModelCost(): boolean
export function getLastMainRequestId(): string | undefined
export function setLastMainRequestId(requestId: string): void
export function getLastApiCompletionTimestamp(): number | null
export function setLastApiCompletionTimestamp(timestamp: number): void
export function markPostCompaction(): void
export function consumePostCompaction(): boolean
export function getLastInteractionTime(): number
export function markScrollActivity(): void
export function getIsScrollDraining(): boolean
export function waitForScrollIdle(): Promise<void>
export function getModelUsage(): { [modelName: string]: ModelUsage }
export function getUsageForModel(model: string): ModelUsage | undefined
export function getMainLoopModelOverride(): ModelSetting | undefined
export function getInitialMainLoopModel(): ModelSetting
export function setMainLoopModelOverride(model: ModelSetting | undefined): void
// ... and many more setters for isInteractive, clientType, sessionSource, telemetry counters, etc.

Key Logic:

  • STATE is a module-level singleton initialized via getInitialState() on import
  • updateLastInteractionTime(immediate?): deferred by default (batches keypresses into single Date.now() per Ink render); pass immediate=true for post-render useEffect callbacks
  • flushInteractionTime(): called by Ink before each render cycle
  • Scroll drain: markScrollActivity() sets a debounce flag (scrollDraining) for 150ms; background intervals call getIsScrollDraining() to yield; waitForScrollIdle() polls with 150ms intervals
  • switchSession() atomically updates sessionId + sessionProjectDir; emits sessionSwitched signal
  • regenerateSessionId() can optionally set current as parent (used for plan mode → implementation lineage)
  • markPostCompaction() / consumePostCompaction(): one-shot latch, auto-resets after first consumption

Configuration:

  • SCROLL_DRAIN_IDLE_MS = 150
  • RESERVOIR_SIZE (histogram sampling) = 1024 (in stats.tsx)

Dependencies: @anthropic-ai/sdk, @opentelemetry/api, @opentelemetry/sdk-*, src/utils/crypto.js, src/utils/signal.js, src/utils/settings/settingsCache.js, src/types/ids.js


coordinator/coordinatorMode.ts

Path: src/coordinator/coordinatorMode.ts

Purpose: Implements multi-worker "coordinator mode" where Claude Code orchestrates multiple parallel subagents. Provides the system prompt, user context injection, mode detection, and session-resume alignment logic.

Exports:

export function isCoordinatorMode(): boolean
export function matchSessionMode(
  sessionMode: 'coordinator' | 'normal' | undefined
): string | undefined
export function getCoordinatorUserContext(
  mcpClients: ReadonlyArray<{ name: string }>,
  scratchpadDir?: string
): { [k: string]: string }
export function getCoordinatorSystemPrompt(): string

Key Logic:

  • isCoordinatorMode(): reads CLAUDE_CODE_COORDINATOR_MODE env var; only active when feature('COORDINATOR_MODE') bundle flag is set
  • matchSessionMode(): when resuming a session, aligns the current coordinator mode with the stored session mode. Flips process.env.CLAUDE_CODE_COORDINATOR_MODE in-place (since isCoordinatorMode() reads it live). Returns a user-visible warning message if mode was switched, undefined if no change needed. Logs tengu_coordinator_mode_switched analytics event
  • getCoordinatorUserContext(): returns { workerToolsContext: string } with worker tool list, MCP server names, and scratchpad directory (if gate tengu_scratch enabled). In CLAUDE_CODE_SIMPLE mode, limits worker tools to Bash/Read/Edit
  • getCoordinatorSystemPrompt(): returns a multi-section system prompt (1500+ chars) describing coordinator role, available tools (Agent, SendMessage, TaskStop), task workflow phases (Research → Synthesis → Implementation → Verification), concurrency strategy, worker prompt writing guidelines, and full example session

Internal Constants:

const INTERNAL_WORKER_TOOLS = new Set([
  TEAM_CREATE_TOOL_NAME,
  TEAM_DELETE_TOOL_NAME,
  SEND_MESSAGE_TOOL_NAME,
  SYNTHETIC_OUTPUT_TOOL_NAME,
])

Configuration:

  • COORDINATOR_MODE bundle feature flag
  • CLAUDE_CODE_COORDINATOR_MODE env var
  • CLAUDE_CODE_SIMPLE env var — restricts worker tool set to Bash/Read/Edit
  • GrowthBook gate tengu_scratch — enables scratchpad directory context

Dependencies: bun:bundle, constants/tools.js, services/analytics/growthbook.js, services/analytics/index.js, various tool name constants, utils/envUtils.js


server/types.ts

Path: src/server/types.ts

Purpose: Shared type definitions for the Claude Code server (direct-connect mode). Provides the Zod validation schema for session creation responses.

Exports:

export const connectResponseSchema: () => ZodObject<{
  session_id: ZodString
  ws_url: ZodString
  work_dir: ZodString.optional()
}>

export type ServerConfig = {
  port: number
  host?: string
  authToken?: string
}

export type SessionState = 'starting' | 'running' | 'detached' | 'stopping' | 'stopped'

export type SessionInfo = {
  sessionId: string
  state: SessionState
  wsUrl: string
  workDir?: string
  createdAt: number
  lastActivity: number
}

export type SessionIndexEntry = {
  sessionId: string
  createdAt: number
  workDir?: string
}

export type SessionIndex = Record<string, SessionIndexEntry>

Key Logic: connectResponseSchema() is a factory function (not a cached value) to allow Zod to be lazy-loaded. Used by createDirectConnectSession.ts to validate the POST /sessions response body.

Dependencies: zod


server/createDirectConnectSession.ts

Path: src/server/createDirectConnectSession.ts

Purpose: Creates a session on a remote direct-connect Claude Code server. Posts to /sessions, validates response, returns a DirectConnectConfig ready for use by the REPL or headless runner.

Exports:

export class DirectConnectError extends Error {
  constructor(message: string)
  name: 'DirectConnectError'
}

export async function createDirectConnectSession(opts: {
  serverUrl: string
  authToken?: string
  cwd: string
  dangerouslySkipPermissions?: boolean
}): Promise<{
  config: DirectConnectConfig
  workDir?: string
}>

Key Logic:

  • POSTs { cwd, dangerously_skip_permissions? } as JSON to ${serverUrl}/sessions
  • Sends Authorization: Bearer ${authToken} if provided
  • Validates response JSON via connectResponseSchema().safeParse()
  • Returns { config: { serverUrl, sessionId, wsUrl, authToken }, workDir }
  • Throws DirectConnectError on fetch failure, non-OK HTTP status, or response parse failure

Dependencies: server/types.js, server/directConnectManager.js, utils/errors.js, utils/slowOperations.js


server/directConnectManager.ts

Path: src/server/directConnectManager.ts

Purpose: WebSocket client for communicating with a remote direct-connect Claude Code server. Handles message routing, permission request/response, interrupt signals, and connection lifecycle.

Exports:

export type DirectConnectConfig = {
  serverUrl: string
  sessionId: string
  wsUrl: string
  authToken?: string
}

export type DirectConnectCallbacks = {
  onMessage: (message: SDKMessage) => void
  onPermissionRequest: (request: SDKControlPermissionRequest, requestId: string) => void
  onConnected?: () => void
  onDisconnected?: () => void
  onError?: (error: Error) => void
}

export class DirectConnectSessionManager {
  constructor(config: DirectConnectConfig, callbacks: DirectConnectCallbacks)
  connect(): void
  sendMessage(content: RemoteMessageContent): boolean
  respondToPermissionRequest(requestId: string, result: RemotePermissionResponse): void
  sendInterrupt(): void
  disconnect(): void
  isConnected(): boolean
}

Key Logic:

  • connect(): opens WebSocket with Authorization: Bearer header (Bun WebSocket headers override); sets up open, message, close, error listeners
  • Message parsing: splits NDJSON lines, parses each line, dispatches:
    • control_request with subtype can_use_toolonPermissionRequest()
    • unrecognized control subtypes → auto-sends error response (prevents server hang)
    • Filtered out: control_response, keep_alive, control_cancel_request, streamlined_text, streamlined_tool_use_summary, system messages with subtype post_turn_summary
    • All others → onMessage()
  • sendMessage(): formats as SDKUserMessage ({ type: 'user', message: { role: 'user', content }, parent_tool_use_id: null, session_id: '' })
  • respondToPermissionRequest(): formats as SDKControlResponse with behavior and either updatedInput (allow) or message (deny)
  • sendInterrupt(): sends { type: 'control_request', request_id: crypto.randomUUID(), request: { subtype: 'interrupt' } }

Dependencies: entrypoints/agentSdkTypes.js, entrypoints/sdk/controlTypes.js, remote/RemoteSessionManager.js, utils/debug.js, utils/slowOperations.js, utils/teleport/api.js


services/analytics/config.ts

Path: src/services/analytics/config.ts

Purpose: Shared analytics configuration — common logic for disabling analytics across all backends.

Exports:

export function isAnalyticsDisabled(): boolean
export function isFeedbackSurveyDisabled(): boolean

Key Logic:

  • isAnalyticsDisabled(): returns true when NODE_ENV === 'test', CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, CLAUDE_CODE_USE_FOUNDRY truthy, or isTelemetryDisabled() is true
  • isFeedbackSurveyDisabled(): returns true when NODE_ENV === 'test' or isTelemetryDisabled() — does NOT gate on 3P providers (Bedrock/Vertex/Foundry) since the survey is local UI with no transcript data; enterprise captures via OTEL

Dependencies: utils/envUtils.js, utils/privacyLevel.js


services/analytics/growthbook.ts

Path: src/services/analytics/growthbook.ts

Purpose: GrowthBook feature flag and dynamic config client. Provides cached and blocking access to feature gates, handles remote eval with disk persistence, manages refresh lifecycle, and exposes override APIs for development/testing.

Key Types:

export type GrowthBookUserAttributes = {
  user_id?: string
  org_id?: string
  user_type?: string
  // ... other Statsig-compatible attributes
}

Exports:

export function onGrowthBookRefresh(listener: () => void): () => void
export function hasGrowthBookEnvOverride(feature: string): boolean
export function getAllGrowthBookFeatures(): Record<string, unknown>
export function getGrowthBookConfigOverrides(): Record<string, unknown>
export function setGrowthBookConfigOverride(feature: string, value: unknown): void
export function clearGrowthBookConfigOverrides(): void
export function getApiBaseUrlHost(): string | undefined
export function initializeGrowthBook(): Promise<GrowthBook | null>
export function getFeatureValue_DEPRECATED<T>(feature: string, defaultValue: T): Promise<T>
export function getFeatureValue_CACHED_MAY_BE_STALE<T>(feature: string, defaultValue: T): T
export function getFeatureValue_CACHED_WITH_REFRESH<T>(feature: string, defaultValue: T): T  // deprecated
export function checkStatsigFeatureGate_CACHED_MAY_BE_STALE(gate: string): boolean
export function checkSecurityRestrictionGate(gate: string): Promise<boolean>
export function checkGate_CACHED_OR_BLOCKING(gate: string): Promise<boolean>
export function getDynamicConfig_CACHED_MAY_BE_STALE<T>(config: string, defaultValue: T): T
export function refreshGrowthBookAfterAuthChange(): void

Key Logic:

  • Initialization (initializeGrowthBook()): Memoized singleton. Loads disk-cached features from ~/.claude/cachedGrowthBookFeatures. Applies CLAUDE_INTERNAL_FC_OVERRIDES env var overrides (JSON). Connects to GrowthBook remote with 5000ms timeout, then sets up periodic refresh. Returns null when analytics disabled or in API key mode without user_id
  • Remote eval workaround: GrowthBook's remote eval returns { value } but client expects { defaultValue }. The code transforms { value: V }{ defaultValue: V } before storing in remoteEvalFeatureValues Map. Synced to disk via syncRemoteEvalToDisk()
  • Caching tiers:
    • _DEPRECATED functions: block on initializeGrowthBook() Promise
    • _CACHED_MAY_BE_STALE: returns synchronously from in-memory cache (may be stale after refresh)
    • _CACHED_OR_BLOCKING: awaits init, then returns cached value; used for security gates only
  • Security gates (checkSecurityRestrictionGate()): awaits init, checks gate value, blocks if not initialized. Used for enterprise policy enforcement
  • Refresh listeners: onGrowthBookRefresh() registers a listener called after each GrowthBook refresh cycle; returns unsubscribe function
  • Overrides: setGrowthBookConfigOverride() / clearGrowthBookConfigOverrides() in-process override map; CLAUDE_INTERNAL_FC_OVERRIDES JSON env var for process-level overrides

Configuration:

  • Disk cache: ~/.claude/cachedGrowthBookFeatures
  • Init timeout: 5000ms
  • CLAUDE_INTERNAL_FC_OVERRIDES env var: JSON override map

Dependencies: growthbook SDK, services/analytics/config.js, utils/auth.js, utils/config.js


services/analytics/metadata.ts

Path: src/services/analytics/metadata.ts

Purpose: Event metadata enrichment for analytics. Provides types and utilities for building structured EventMetadata objects with environment context, process metrics, and safe telemetry extraction from tool inputs.

Constants:

  • TOOL_INPUT_STRING_TRUNCATE_AT = 512 — strings longer than this get truncated
  • TOOL_INPUT_STRING_TRUNCATE_TO = 128 — truncated target length
  • TOOL_INPUT_MAX_JSON_CHARS = 4096 — JSON input cap before discarding
  • MAX_FILE_EXTENSION_LENGTH = 10 — max chars for file extensions

Exports:

export type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS = never  // marker type

export function sanitizeToolNameForAnalytics(toolName: string): never  // returns marker type

export function isToolDetailsLoggingEnabled(): boolean  // gated on OTEL_LOG_TOOL_DETAILS env

export function isAnalyticsToolDetailsLoggingEnabled(
  mcpServerType: string | undefined,
  mcpServerBaseUrl: string | undefined
): boolean

export function mcpToolDetailsForAnalytics(
  toolName: string,
  mcpServerType: string | undefined,
  mcpServerBaseUrl: string | undefined
): { mcpServerName?: never; mcpToolName?: never }

export function extractMcpToolDetails(
  toolName: string
): { serverName: string; mcpToolName: string } | undefined

export function extractSkillName(
  toolName: string,
  input: unknown
): never | undefined  // returns marker type or undefined

export function extractToolInputForTelemetry(input: unknown): string | undefined

export function getFileExtensionForAnalytics(filePath: string): never | undefined

export function getFileExtensionsFromBashCommand(
  command: string,
  simulatedSedEditFilePath?: string
): never | undefined

export type EnvContext = {
  userType: string
  isCI: boolean
  platform: string
  // ... other context fields
}

export type ProcessMetrics = {
  heapUsedMB: number
  heapTotalMB: number
  rssMB: number
  externalMB: number
}

export type EventMetadata = {
  // enriched event payload type
}

export type EnrichMetadataOptions = {
  includeProcessMetrics?: boolean
  // ...
}

export async function getEventMetadata(options?: EnrichMetadataOptions): Promise<EventMetadata>
export async function buildEnvContext(): Promise<EnvContext>  // memoized

Key Logic:

  • BUILTIN_MCP_SERVER_NAMES set is gated behind CHICAGO_MCP feature flag — determines which MCP servers are considered "builtin"
  • extractToolInputForTelemetry(): JSON-serializes input, truncates strings over TOOL_INPUT_STRING_TRUNCATE_AT to TOOL_INPUT_STRING_TRUNCATE_TO, caps total at TOOL_INPUT_MAX_JSON_CHARS
  • getFileExtensionsFromBashCommand(): parses bash command to extract file extensions using regex patterns; handles sed -i specially via simulatedSedEditFilePath
  • buildEnvContext() is memoized — called once per process and cached
  • Agent identification classifies turns as: teammate (subagent of another), subagent (spawned by coordinator), standalone

Dependencies: services/analytics/growthbook.js, utils/envUtils.js, utils/platform.js


services/analytics/index.ts

Path: src/services/analytics/index.ts

Purpose: The main analytics entry point — a no-dependency module that provides a queuing facade for all event logging. Events are queued until a sink is attached, preventing startup ordering issues.

Design: Explicitly has NO dependencies to avoid import cycles. Events are queued in eventQueue until attachAnalyticsSink() drains them via queueMicrotask.

Exports:

export type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS = never  // marker type
export type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED = never  // PII-tagged marker type

export function stripProtoFields<V>(
  metadata: Record<string, V>
): Record<string, V>

export type AnalyticsSink = {
  logEvent: (eventName: string, metadata: LogEventMetadata) => void
  logEventAsync: (eventName: string, metadata: LogEventMetadata) => Promise<void>
}

export function attachAnalyticsSink(newSink: AnalyticsSink): void
export function logEvent(eventName: string, metadata: LogEventMetadata): void
export async function logEventAsync(eventName: string, metadata: LogEventMetadata): Promise<void>
export function _resetForTesting(): void

Types (internal):

type LogEventMetadata = { [key: string]: boolean | number | undefined }
type QueuedEvent = { eventName: string; metadata: LogEventMetadata; async: boolean }

Key Logic:

  • attachAnalyticsSink(): idempotent (no-op if sink already set). Drains queue via queueMicrotask to avoid blocking startup. For ant users (USER_TYPE === 'ant'), logs analytics_sink_attached with queued_event_count
  • stripProtoFields(): removes keys starting with _PROTO_ from event metadata (for non-1P destinations). Returns same reference if no _PROTO_ keys present
  • _PROTO_* keys route to PII-tagged BigQuery columns — stripped before Datadog but preserved for firstPartyEventLoggingExporter
  • Metadata type is intentionally restricted to boolean | number | undefined — no strings unless explicitly cast with the marker type

services/analytics/sink.ts

Path: src/services/analytics/sink.ts

Purpose: The analytics sink implementation that routes events to Datadog and first-party event logging backends. Handles sampling, metadata enrichment, and per-sink kill switches.

Exports:

export function createAnalyticsSink(options?: {
  isInteractive?: boolean
}): AnalyticsSink

Key Logic:

  • Routes events to two sinks: Datadog (via logDatadogEvent) and first-party event logging (via log1PEvent)
  • Checks isSinkKilled('datadog') and isSinkKilled('firstParty') before dispatching to each sink
  • Applies sampling based on tengu_event_sampling_config dynamic config — adds sample_rate to metadata when sampled
  • Strips _PROTO_* keys before Datadog fanout (stripProtoFields)
  • Metadata is enriched with getEventMetadata() before dispatch

services/analytics/sinkKillswitch.ts

Path: src/services/analytics/sinkKillswitch.ts

Purpose: Per-sink analytics kill switch, controlled by a GrowthBook JSON config.

Exports:

export type SinkName = 'datadog' | 'firstParty'

export function isSinkKilled(sink: SinkName): boolean

Key Logic:

  • Config name: tengu_frond_boric (mangled/obfuscated name)
  • Shape: { datadog?: boolean, firstParty?: boolean }true stops dispatch to that sink
  • Default {} (nothing killed). Fail-open: missing/malformed config = sink stays on
  • Must NOT be called from isGrowthBookEnabled() — would cause recursion; call at per-event dispatch sites instead

Dependencies: services/analytics/growthbook.js


services/analytics/datadog.ts

Path: src/services/analytics/datadog.ts

Purpose: Datadog metrics and event logging integration for Claude Code analytics.

Key Logic:

  • Sends events via @datadog/datadog-ci or direct HTTP to Datadog API
  • Disabled when isAnalyticsDisabled() returns true
  • Event namespace: tengu_* prefix for all Claude Code events
  • Tags include version, platform, user type, session metadata

services/analytics/firstPartyEventLogger.ts

Path: src/services/analytics/firstPartyEventLogger.ts

Purpose: First-party event logging integration — routes events to the internal event logging system.

Key Logic:

  • Gated by is1PEventLoggingEnabled() which checks GrowthBook feature tengu_fpel and isAnalyticsDisabled()
  • Enriches events with EventMetadata from getEventMetadata()
  • Handles proto field hoisting from _PROTO_* keys

services/analytics/firstPartyEventLoggingExporter.ts

Path: src/services/analytics/firstPartyEventLoggingExporter.ts

Purpose: OpenTelemetry log exporter that routes logs to the first-party event logging pipeline.

Key Logic:

  • Implements OTel LogRecordExporter interface
  • Hoists _PROTO_* keys to top-level proto fields in the BQ destination
  • Calls stripProtoFields() after hoisting as defensive cleanup
  • Only sends to first-party pipeline (not Datadog)

services/api/bootstrap.ts

Path: src/services/api/bootstrap.ts

Purpose: Bootstraps the API client with session-specific configuration at startup. Wires together auth, proxy, and telemetry settings.

Key Logic:

  • Configures ANTHROPIC_API_URL base URL, auth headers, proxy settings
  • Calls initializeGrowthBook() early in startup
  • Sets up OTel span management
  • Handles CLAUDE_CODE_SKIP_BEDROCK_TLS for Bedrock TLS verification skip

services/api/client.ts

Path: src/services/api/client.ts

Purpose: Provides the configured SDK client instance and helper utilities for making API calls.

Exports:

export function getClient(): Anthropic
export function getClientForModel(model: string): Anthropic

Key Logic:

  • Client is created lazily and cached
  • Applies base URL overrides from ANTHROPIC_BASE_URL env
  • Configures mTLS via getMtlsConfig()
  • Routes through proxy if HTTPS_PROXY / HTTP_PROXY set

services/api/claude.ts

Path: src/services/api/claude.ts

Purpose: Main API call layer for Claude completions. Handles prompt caching, extra body parameters, task budget configuration, metadata, and user message formatting.

Exports:

export function getExtraBodyParams(betaHeaders?: string[]): JsonObject
export function getPromptCachingEnabled(model: string): boolean
export function getCacheControl(opts: {
  scope?: string
  querySource?: QuerySource
}): { type: 'ephemeral' | 'persistent'; ttl?: number; scope?: string }
export function configureTaskBudgetParams(
  taskBudget: number,
  outputConfig: OutputConfig,
  betas: string[]
): void
export function getAPIMetadata(): { user_id: string }
export async function verifyApiKey(
  apiKey: string,
  isNonInteractiveSession: boolean
): Promise<boolean>
export function userMessageToMessageParam(
  message: UserMessage,
  addCache: boolean,
  enablePromptCaching: boolean,
  querySource?: QuerySource
): MessageParam

Internal functions (not exported):

  • configureEffortParams(): sets thinking budget based on effort level
  • should1hCacheTTL(): checks if 1h TTL is applicable for current user/model

Key Logic:

  • Prompt caching: getPromptCachingEnabled() checks model allowlist. getCacheControl() returns { type: 'ephemeral' } normally, { type: 'ephemeral', ttl: 3600 } when 1h TTL gate passes
  • 1h TTL gate: should1hCacheTTL() checks tengu_prompt_cache_1h_config GrowthBook allowlist (session-stable, latched in STATE.promptCache1hAllowlist) and STATE.promptCache1hEligible (also latched to prevent mid-session overage flips)
  • Anti-distillation: tengu_anti_distill_fake_tool_injection GrowthBook gate — injects fake tools into API calls as a training data quality signal
  • Extra body params: getExtraBodyParams() assembles beta headers, model-specific params, and context management config

Dependencies: @anthropic-ai/sdk, bootstrap/state.js, services/analytics/growthbook.js, services/compact/apiMicrocompact.js


services/api/dumpPrompts.ts

Path: src/services/api/dumpPrompts.ts

Purpose: Debug utility for ant users — dumps API request/response JSONL logs to disk for inspection. Used for prompt debugging and sharing bug reports.

Exports:

export function getLastApiRequests(): Array<{ timestamp: string; request: unknown }>
export function clearApiRequestCache(): void
export function clearDumpState(agentIdOrSessionId: string): void
export function clearAllDumpState(): void
export function addApiRequestToCache(requestData: unknown): void
export function getDumpPromptsPath(agentIdOrSessionId?: string): string
export function createDumpPromptsFetch(
  agentIdOrSessionId: string
): ClientOptions['fetch']

Key Logic:

  • Ant-only (no-op for non-ant users)
  • MAX_CACHED_REQUESTS = 5 — in-memory ring buffer of recent requests
  • Deferred writes via setImmediate to avoid blocking the request path
  • JSONL format with records of types: init, system_update, message, response
  • Per-session state tracking with fingerprint-based change detection (avoids re-writing unchanged system prompts)
  • Path: ~/.claude/dump-prompts/<agentIdOrSessionId>.jsonl

services/api/emptyUsage.ts

Path: src/services/api/emptyUsage.ts

Purpose: Provides a zero-value Usage object for cases where usage data is unavailable.

Exports:

export const EMPTY_USAGE: Usage
export type NonNullableUsage = {
  input_tokens: number
  output_tokens: number
  cache_read_input_tokens: number
  cache_creation_input_tokens: number
}

services/api/errorUtils.ts

Path: src/services/api/errorUtils.ts

Purpose: Utilities for classifying and handling API errors.

Exports:

export function isRateLimitError(error: unknown): boolean
export function isOverloadedError(error: unknown): boolean
export function isAuthError(error: unknown): boolean
export function isConnectionError(error: unknown): boolean
export function getRetryAfterMs(error: unknown): number | undefined

services/api/errors.ts

Path: src/services/api/errors.ts

Purpose: Defines all API error message constants and classification functions for user-facing error handling.

Exports:

export const API_ERROR_MESSAGE_PREFIX = 'API Error'
export function startsWithApiErrorPrefix(text: string): boolean

export const PROMPT_TOO_LONG_ERROR_MESSAGE: string
export function isPromptTooLongMessage(msg: string): boolean
export function parsePromptTooLongTokenCounts(
  rawMessage: string
): { actualTokens: number; limitTokens: number } | null
export function getPromptTooLongTokenGap(msg: string): number | undefined

export function isMediaSizeError(raw: unknown): boolean
export function isMediaSizeErrorMessage(msg: string): boolean

export const CREDIT_BALANCE_TOO_LOW_ERROR_MESSAGE: string
export const INVALID_API_KEY_ERROR_MESSAGE: string
export const INVALID_API_KEY_ERROR_MESSAGE_EXTERNAL: string
export const TOKEN_REVOKED_ERROR_MESSAGE: string
export const CCR_AUTH_ERROR_MESSAGE: string
export const REPEATED_529_ERROR_MESSAGE: string
export const CUSTOM_OFF_SWITCH_MESSAGE: string
export const API_TIMEOUT_ERROR_MESSAGE: string
export const OAUTH_ORG_NOT_ALLOWED_ERROR_MESSAGE: string

export function getPdfTooLargeErrorMessage(): string
export function getPdfPasswordProtectedErrorMessage(): string
export function getPdfInvalidErrorMessage(): string
export function getImageTooLargeErrorMessage(): string
export function getRequestTooLargeErrorMessage(): string
export function getTokenRevokedErrorMessage(): string
export function getOauthOrgNotAllowedErrorMessage(): string

services/api/filesApi.ts

Path: src/services/api/filesApi.ts

Purpose: Files API client — downloads, uploads, lists, and manages files in Files API (beta).

Constants:

  • FILES_API_BETA_HEADER = 'files-api-2025-04-14,oauth-2025-04-20'
  • MAX_FILE_SIZE_BYTES = 500 * 1024 * 1024 (500MB)
  • DEFAULT_CONCURRENCY = 5
  • MAX_RETRIES = 3
  • BASE_DELAY_MS = 500

Exports:

export type File = {
  fileId: string
  relativePath: string
  mimeType?: string
}

export type FilesApiConfig = {
  apiKey?: string
  baseUrl?: string
  sessionId?: string
}

export type DownloadResult = {
  fileId: string
  relativePath: string
  success: boolean
  error?: string
  savedPath?: string
}

export type UploadResult = {
  fileId: string
  relativePath: string
  success: boolean
  error?: string
  remoteFileId?: string
}

export type FileMetadata = {
  id: string
  filename: string
  created_at: number
  purpose: string
  size: number
}

export class UploadNonRetriableError extends Error {}

export function parseFileSpecs(fileSpecs: string[]): File[]
export async function downloadFile(fileId: string, config: FilesApiConfig): Promise<Buffer>
export function buildDownloadPath(
  basePath: string,
  sessionId: string,
  relativePath: string
): string | null
export async function downloadAndSaveFile(
  attachment: File,
  config: FilesApiConfig
): Promise<DownloadResult>
export async function downloadSessionFiles(
  files: File[],
  config: FilesApiConfig,
  concurrency?: number
): Promise<DownloadResult[]>
export async function uploadFile(
  filePath: string,
  relativePath: string,
  config: FilesApiConfig,
  opts?: { retries?: number }
): Promise<UploadResult>
export async function uploadSessionFiles(
  files: File[],
  config: FilesApiConfig,
  concurrency?: number
): Promise<UploadResult[]>
export async function listFilesCreatedAfter(
  afterCreatedAt: number,
  config: FilesApiConfig
): Promise<FileMetadata[]>

Key Logic:

  • buildDownloadPath(): path traversal guard — if relativePath contains .. components or resolves outside basePath/sessionId, returns null
  • Download/upload use exponential backoff: BASE_DELAY_MS * 2^attempt with jitter
  • uploadSessionFiles() / downloadSessionFiles(): parallel with configurable concurrency (default 5)
  • listFilesCreatedAfter(): paginated using cursor, collects all pages

services/api/firstTokenDate.ts

Path: src/services/api/firstTokenDate.ts

Purpose: Fetches and stores the date when the user first made a Claude Code API call.

Exports:

export async function fetchAndStoreClaudeCodeFirstTokenDate(): Promise<void>

Key Logic:

  • Fetches /api/organization/claude_code_first_token_date
  • Stores in claudeCodeFirstTokenDate config field
  • Idempotent — no-ops if already stored

services/api/grove.ts

Path: src/services/api/grove.ts

Purpose: Grove is a consumer Terms/Privacy Policy notification feature. Manages fetching, caching, and determining whether to show the Grove notice to users.

Constants:

  • GROVE_CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1000 (24 hours)

Exports:

export type AccountSettings = {
  groveEnabled: boolean
  groveNoticeViewed: boolean
  // ...
}

export type GroveConfig = {
  enabled: boolean
  forceShow: boolean
  // ...
}

export type ApiResult<T> = { success: true; data: T } | { success: false; error: string }

export async function getGroveSettings(): Promise<ApiResult<AccountSettings>>  // memoized 24h
export async function markGroveNoticeViewed(): Promise<void>
export async function updateGroveSettings(groveEnabled: boolean): Promise<void>
export async function isQualifiedForGrove(): Promise<boolean>
export async function getGroveNoticeConfig(): Promise<ApiResult<GroveConfig>>  // memoized 24h
export function calculateShouldShowGrove(
  settingsResult: ApiResult<AccountSettings>,
  configResult: ApiResult<GroveConfig>,
  showIfAlreadyViewed: boolean
): boolean
export async function checkGroveForNonInteractive(): Promise<void>

Key Logic:

  • Cache-first with background refresh: returns cached data immediately, refreshes in background after expiry
  • calculateShouldShowGrove(): checks config enabled, settings not viewed, and user qualification
  • checkGroveForNonInteractive(): called in non-interactive mode to log grove status without showing UI

services/api/logging.ts

Path: src/services/api/logging.ts

Purpose: API query/success/error logging with gateway detection and OTel span management.

Exports:

export type GlobalCacheStrategy = 'tool_based' | 'system_prompt' | 'none'

export function logAPIQuery(opts: {
  model: string
  querySource: QuerySource
  // ... other fields
}): void

export function logAPIError(opts: {
  error: unknown
  model: string
  querySource: QuerySource
  // ... other fields
}): void

export function logAPISuccessAndDuration(opts: {
  model: string
  usage: Usage
  querySource: QuerySource
  durationMs: number
  // ... other fields
}): void

Key Logic:

  • Gateway detection: identifies litellm, helicone, portkey, cloudflare-ai-gateway, kong, braintrust, databricks from ANTHROPIC_BASE_URL
  • Events: tengu_api_query, tengu_api_error, tengu_api_success
  • OTel spans created/ended around API calls
  • Teleport session tracking: logs extra fields when session is teleported
  • Re-exports EMPTY_USAGE and NonNullableUsage

services/api/metricsOptOut.ts

Path: src/services/api/metricsOptOut.ts

Purpose: Checks whether metrics collection is enabled for the current organization. Implements two-tier caching.

Constants:

  • CACHE_TTL_MS = 60 * 60 * 1000 (1 hour in-memory)
  • DISK_CACHE_TTL_MS = 24 * 60 * 60 * 1000 (24 hours on disk)
  • Endpoint: api/claude_code/organizations/metrics_enabled

Exports:

export async function checkMetricsEnabled(): Promise<MetricsStatus>
export function _clearMetricsEnabledCacheForTesting(): void

Key Logic:

  • Two-tier cache: in-memory (1h TTL) → disk (24h TTL) → network
  • Requires profile OAuth scope; returns enabled: true if unauthenticated or scope missing
  • MetricsStatus: { enabled: boolean; source: 'cache' | 'network' | 'default' }

services/api/overageCreditGrant.ts

Path: src/services/api/overageCreditGrant.ts

Purpose: Manages overage credit grant information for subscribed users who exceed their plan limits.

Constants:

  • CACHE_TTL_MS = 60 * 60 * 1000 (1 hour)

Exports:

export type OverageCreditGrantInfo = {
  hasGrant: boolean
  amount?: number
  currency?: string
  expiresAt?: string
}

export type OverageCreditGrantCacheEntry = {
  data: OverageCreditGrantInfo
  fetchedAt: number
  orgId: string
}

export function getCachedOverageCreditGrant(): OverageCreditGrantInfo | null
export function invalidateOverageCreditGrantCache(): void
export async function refreshOverageCreditGrantCache(): Promise<void>
export function formatGrantAmount(info: OverageCreditGrantInfo): string | null

Key Logic:

  • Per-org cache in overageCreditGrantCache Map (keyed by org ID)
  • formatGrantAmount(): formats amount as currency string (e.g., "$5.00") or null if no grant

services/api/promptCacheBreakDetection.ts

Path: src/services/api/promptCacheBreakDetection.ts

Purpose: Detects unexpected prompt cache breaks that indicate server-side cache eviction. Writes diff files to disk for debugging.

Constants:

  • CACHE_TTL_1HOUR_MS = 3_600_000 (1 hour)
  • MIN_CACHE_MISS_TOKENS = 2_000 — minimum to consider a break significant
  • 95% threshold — cache reads must drop to ≤5% of expected to count as a break
  • MAX_TRACKED_SOURCES = 10

Exports:

export type PromptStateSnapshot = {
  messages: MessageParam[]
  systemPrompt: string
  tools: unknown[]
  timestamp: number
  querySource: QuerySource
}

export const CACHE_TTL_1HOUR_MS: number

export function recordPromptState(snapshot: PromptStateSnapshot): void
export async function checkResponseForCacheBreak(
  querySource: QuerySource,
  cacheReadTokens: number,
  cacheCreationTokens: number,
  messages: MessageParam[],
  agentId?: string,
  requestId?: string
): Promise<void>
export function notifyCacheDeletion(querySource: QuerySource, agentId?: string): void
export function notifyCompaction(querySource: QuerySource, agentId?: string): void
export function cleanupAgentTracking(agentId: string): void
export function resetPromptCacheBreakDetection(): void

Key Logic:

  • 2-phase detection: recordPromptState() before call, checkResponseForCacheBreak() after
  • Per-source tracking Map: keyed by querySource (or agent:${agentId})
  • Tracked source prefixes: repl_main_thread, sdk, agent:custom, agent:default, agent:builtin
  • Writes diff files to ~/.claude/tmp/cache-break-*.diff when a break is detected
  • Events: tengu_prompt_cache_break
  • notifyCacheDeletion() / notifyCompaction(): suppress false positives after intentional cache clearing

services/api/referral.ts

Path: src/services/api/referral.ts

Purpose: Manages referral program eligibility, redemptions, and guest passes for Claude Code subscribers.

Constants:

  • CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1000 (24 hours)

Exports:

export async function fetchReferralEligibility(
  campaign?: string
): Promise<ReferralEligibilityResponse>
export async function fetchReferralRedemptions(
  campaign?: string
): Promise<ReferralRedemptionsResponse>
export function checkCachedPassesEligibility(): {
  eligible: boolean
  needsRefresh: boolean
  hasCache: boolean
}
export function formatCreditAmount(reward: ReferrerReward): string
export function getCachedReferrerReward(): ReferrerRewardInfo | null
export function getCachedRemainingPasses(): number | null
export async function fetchAndStorePassesEligibility(): Promise<ReferralEligibilityResponse | null>
export async function getCachedOrFetchPassesEligibility(): Promise<ReferralEligibilityResponse | null>
export async function prefetchPassesEligibility(): Promise<void>

Key Logic:

  • Max-subscription only — returns null / ineligible for non-max subscribers
  • In-flight deduplication: multiple calls to getCachedOrFetchPassesEligibility() share one pending Promise
  • 24h cache TTL

services/api/sessionIngress.ts

Path: src/services/api/sessionIngress.ts

Purpose: Manages session log ingress — append-log with optimistic concurrency for multi-writer scenarios (e.g., continued sessions from different machines).

Constants:

  • MAX_RETRIES = 10
  • BASE_DELAY_MS = 500 — exponential backoff base

Exports:

export async function appendSessionLog(
  sessionId: string,
  entry: SessionLogEntry,
  url: string
): Promise<boolean>
export async function getSessionLogs(
  sessionId: string,
  url: string
): Promise<Entry[] | null>
export async function getSessionLogsViaOAuth(
  sessionId: string,
  accessToken: string,
  orgUUID: string
): Promise<Entry[] | null>
export async function getTeleportEvents(
  sessionId: string,
  accessToken: string,
  orgUUID: string
): Promise<Entry[] | null>
export function clearSession(sessionId: string): void
export function clearAllSessions(): void

Key Logic:

  • Optimistic concurrency: Last-Uuid header on append; 409 response adopts server's last UUID
  • Sequential wrappers per session prevent out-of-order appends
  • getTeleportEvents(): paginated (max 100 pages, 1000 events/page)
  • clearSession() / clearAllSessions(): clears in-memory sequential wrapper state

services/api/ultrareviewQuota.ts

Path: src/services/api/ultrareviewQuota.ts

Purpose: Fetches ultrareview (deep code review) quota information for subscribed users.

Exports:

export type UltrareviewQuotaResponse = {
  used: number
  limit: number
  resetsAt: string
}

export async function fetchUltrareviewQuota(): Promise<UltrareviewQuotaResponse | null>

Key Logic:

  • Endpoint: /v1/ultrareview/quota
  • Subscriber-only; returns null for non-subscribers or on error
  • 5 second timeout

services/api/usage.ts

Path: src/services/api/usage.ts

Purpose: Fetches usage statistics for the current user/organization from the Claude Code API.

Exports:

export async function fetchUsage(): Promise<UsageStats | null>
export type UsageStats = {
  // usage breakdown fields
}

services/api/withRetry.ts

Path: src/services/api/withRetry.ts

Purpose: Generic retry wrapper for API calls with exponential backoff.

Exports:

export async function withRetry<T>(
  fn: () => Promise<T>,
  opts?: {
    maxRetries?: number
    baseDelayMs?: number
    shouldRetry?: (error: unknown) => boolean
  }
): Promise<T>

services/AgentSummary/agentSummary.ts

Path: src/services/AgentSummary/agentSummary.ts

Purpose: Periodic background summarization of agent conversations to compress context while preserving key information.

Key Logic:

  • Runs on a 30-second background timer
  • Generates summaries using the main Claude model
  • Compressed summaries are injected back as system messages
  • Used by subagents and teammates to manage long-running conversations

services/autoDream/autoDream.ts

Path: src/services/autoDream/autoDream.ts

Purpose: Background memory consolidation system. Periodically scans session transcripts and uses a forked agent to consolidate learnings into persistent memory files.

Constants:

  • SESSION_SCAN_INTERVAL_MS = 10 * 60 * 1000 (10 minutes)
  • DEFAULTS = { minHours: 24, minSessions: 5 } — minimum time and sessions before consolidation

Exports:

export function initAutoDream(): () => void  // returns stop/cleanup function

Key Logic:

  • Gate order: time check (minHours) → session count check (minSessions) → consolidation lock
  • GrowthBook config tengu_onyx_plover controls { minHours, minSessions, enabled }
  • initAutoDream() registers as a post-sampling hook; returns cleanup function
  • Uses SESSION_SCAN_INTERVAL_MS for polling
  • Spawns a forked agent using buildConsolidationPrompt()

services/autoDream/config.ts

Path: src/services/autoDream/config.ts

Purpose: Configuration gate for the autoDream memory consolidation feature.

Exports:

export function isAutoDreamEnabled(): boolean

Key Logic: User setting takes precedence over GrowthBook gate tengu_onyx_plover. Checks userSettings.autoDream first, then GrowthBook.


services/autoDream/consolidationLock.ts

Path: src/services/autoDream/consolidationLock.ts

Purpose: File-based mutex lock for the memory consolidation process to prevent concurrent consolidations across sessions/processes.

Constants:

  • LOCK_FILE = '.consolidate-lock' — in memory directory
  • HOLDER_STALE_MS = 60 * 60 * 1000 (1 hour) — stale lock threshold

Exports:

export async function readLastConsolidatedAt(): Promise<number>
export async function tryAcquireConsolidationLock(): Promise<number | null>
export async function rollbackConsolidationLock(priorMtime: number): Promise<void>
export async function listSessionsTouchedSince(sinceMs: number): Promise<string[]>
export async function recordConsolidation(): Promise<void>

Key Logic:

  • Lock file mtime = lastConsolidatedAt timestamp (dual-purpose: both locking and timestamp)
  • PID-based ownership — stale locks (PID dead or >1h old) are overwritten
  • tryAcquireConsolidationLock(): returns prior mtime on success, null if already held
  • rollbackConsolidationLock(): restores mtime to priorMtime on consolidation failure

services/autoDream/consolidationPrompt.ts

Path: src/services/autoDream/consolidationPrompt.ts

Purpose: Builds the system prompt for the memory consolidation agent.

Exports:

export function buildConsolidationPrompt(
  memoryRoot: string,
  transcriptDir: string,
  extra: string
): string

Key Logic: Returns a 4-phase prompt:

  1. Orient — read existing memory files to understand current state
  2. Gather recent signal — read session transcripts since last consolidation
  3. Consolidate — merge new learnings into memory files
  4. Prune and index — remove stale entries, update index file

services/awaySummary.ts

Path: src/services/awaySummary.ts

Purpose: Generates "away summaries" — brief catch-up summaries shown when the user returns to a long-running session after being away.

Key Logic:

  • Triggered when lastInteractionTime gap exceeds threshold
  • Uses Claude to generate a brief (1-3 sentence) summary of what happened while away
  • Displayed as a system message above the prompt

services/claudeAiLimits.ts

Path: src/services/claudeAiLimits.ts

Purpose: Fetches and manages rate limit information for Claude.ai-authenticated users.

Exports:

export async function fetchClaudeAiLimits(): Promise<ClaudeAiLimits | null>
export type ClaudeAiLimits = {
  // rate limit fields
}

services/claudeAiLimitsHook.ts

Path: src/services/claudeAiLimitsHook.ts

Purpose: React hook for accessing Claude.ai rate limit data with automatic refresh.

Exports:

export function useClaudeAiLimits(): ClaudeAiLimits | null

services/compact/apiMicrocompact.ts

Path: src/services/compact/apiMicrocompact.ts

Purpose: API-native context management strategies using server-side cache_edits feature. Configures context window editing without full client-side rewriting.

Constants:

  • DEFAULT_MAX_INPUT_TOKENS = 180_000
  • DEFAULT_TARGET_INPUT_TOKENS = 40_000

Type: ContextEditStrategy:

export type ContextEditStrategy =
  | {
      type: 'clear_tool_uses_20250919'
      trigger?: { type: 'input_tokens'; value: number }
      keep?: { type: 'tool_uses'; value: number }
      clear_tool_inputs?: boolean | string[]
      exclude_tools?: string[]
      clear_at_least?: { type: 'input_tokens'; value: number }
    }
  | {
      type: 'clear_thinking_20251015'
      keep: { type: 'thinking_turns'; value: number } | 'all'
    }

export type ContextManagementConfig = {
  edits: ContextEditStrategy[]
}

Exports:

export function getAPIContextManagement(options?: {
  hasThinking?: boolean
  isRedactThinkingActive?: boolean
  clearAllThinking?: boolean
}): ContextManagementConfig | undefined

Key Logic:

  • Tool clearing strategies are ant-only, gated by USE_API_CLEAR_TOOL_RESULTS and USE_API_CLEAR_TOOL_USES env vars
  • TOOLS_CLEARABLE_RESULTS: shell tools, Glob, Grep, FileRead, WebFetch, WebSearch
  • TOOLS_CLEARABLE_USES: FileEdit, FileWrite, NotebookEdit
  • Thinking clearing: when hasThinking && !isRedactThinkingActive, adds clear_thinking_20251015
  • When clearAllThinking (>1h idle = confirmed cache miss): keeps only last 1 thinking turn

services/compact/autoCompact.ts

Path: src/services/compact/autoCompact.ts

Purpose: Automatic context compaction — triggers full conversation summarization when context window usage exceeds threshold.

Key Logic:

  • Monitors token usage against configurable threshold (default 90% of context window)
  • When triggered, calls compact() to summarize the conversation
  • Posts a CompactBoundaryMessage in the conversation to mark compaction point
  • Resets token tracking after compaction

services/compact/compact.ts

Path: src/services/compact/compact.ts

Purpose: Full conversation compaction — replaces conversation history with an LLM-generated summary.

Key Logic:

  • Uses the detailed analysis instruction prompts from prompt.ts
  • Optionally performs partial compaction (keeps recent messages)
  • The NO_TOOLS_PREAMBLE constant is a critical instruction preventing the compaction model from calling tools during summarization
  • Writes compact summary as a synthetic <compact_summary> tagged message

services/compact/compactWarningHook.ts

Path: src/services/compact/compactWarningHook.ts

Purpose: React hook for accessing the compact warning suppression state.

Exports:

export function useCompactWarningSuppression(): boolean

services/compact/compactWarningState.ts

Path: src/services/compact/compactWarningState.ts

Purpose: Store and actions for suppressing the "compact recommended" warning after microcompact runs.

Exports:

export const compactWarningStore: Store<boolean>
export function suppressCompactWarning(): void
export function clearCompactWarningSuppression(): void

services/compact/grouping.ts

Path: src/services/compact/grouping.ts

Purpose: Groups conversation messages by API round (each assistant message with its preceding user message).

Exports:

export function groupMessagesByApiRound(messages: Message[]): Message[][]

Key Logic: Groups by assistant message.id boundary — each group contains one API round (user + assistant + tool results).


services/compact/microCompact.ts

Path: src/services/compact/microCompact.ts

Purpose: Microcompaction — lightweight context reduction by clearing tool result content without full conversation summarization. Two paths: cached microcompact (via API cache_edits) and time-based microcompact (direct content mutation when cache is cold).

Constants (exported):

export const TIME_BASED_MC_CLEARED_MESSAGE = '[Old tool result content cleared]'

Compactable tool sets:

const COMPACTABLE_TOOLS = new Set([
  FILE_READ_TOOL_NAME, SHELL_TOOL_NAMES..., GREP_TOOL_NAME, GLOB_TOOL_NAME,
  WEB_SEARCH_TOOL_NAME, WEB_FETCH_TOOL_NAME, FILE_EDIT_TOOL_NAME, FILE_WRITE_TOOL_NAME
])

Exports:

export function consumePendingCacheEdits():
  import('./cachedMicrocompact.js').CacheEditsBlock | null

export function getPinnedCacheEdits():
  import('./cachedMicrocompact.js').PinnedCacheEdits[]

export function pinCacheEdits(
  userMessageIndex: number,
  block: import('./cachedMicrocompact.js').CacheEditsBlock
): void

export function markToolsSentToAPIState(): void

export function resetMicrocompactState(): void

export function estimateMessageTokens(messages: Message[]): number

export type PendingCacheEdits = {
  trigger: 'auto'
  deletedToolIds: string[]
  baselineCacheDeletedTokens: number
}

export type MicrocompactResult = {
  messages: Message[]
  compactionInfo?: {
    pendingCacheEdits?: PendingCacheEdits
  }
}

export function evaluateTimeBasedTrigger(
  messages: Message[],
  querySource: QuerySource | undefined
): { gapMinutes: number; config: TimeBasedMCConfig } | null

export async function microcompactMessages(
  messages: Message[],
  toolUseContext?: ToolUseContext,
  querySource?: QuerySource
): Promise<MicrocompactResult>

Key Logic:

  • microcompactMessages() dispatch order:
    1. Time-based trigger check: if gap since last assistant > threshold → maybeTimeBasedMicrocompact() (short-circuits)
    2. Cached MC path: if CACHED_MICROCOMPACT feature enabled, model supported, and main thread source → cachedMicrocompactPath()
    3. Otherwise: return messages unchanged (legacy path removed)
  • Cached MC path: registers tool results grouped by user message; calls getToolResultsToDelete(); queues CacheEditsBlock as pendingCacheEdits; does NOT mutate message content
  • Time-based MC path: directly mutates tool result content to TIME_BASED_MC_CLEARED_MESSAGE; resets cached MC state; notifies cache break detection
  • estimateMessageTokens(): rough estimation with 4/3 padding factor; images/documents = 2000 tokens
  • isMainThreadSource(): prefix-matches repl_main_thread (handles output style variants like repl_main_thread:outputStyle:custom)
  • Events: tengu_cached_microcompact, tengu_time_based_microcompact

services/compact/postCompactCleanup.ts

Path: src/services/compact/postCompactCleanup.ts

Purpose: Runs cleanup tasks after any compaction (auto or manual /compact).

Exports:

export function runPostCompactCleanup(querySource?: QuerySource): void

Key Logic: Clears: microcompact state, context collapse state, system prompt sections, classifier approvals, speculative checks, beta tracing state, session messages cache.


services/compact/prompt.ts

Path: src/services/compact/prompt.ts

Purpose: Prompt constants used for compact summarization.

Key Exports:

  • NO_TOOLS_PREAMBLE: Critical instruction string prepended to compact calls — prevents the model from invoking any tools during summarization
  • DETAILED_ANALYSIS_INSTRUCTION_BASE: Instruction for full compaction
  • DETAILED_ANALYSIS_INSTRUCTION_PARTIAL: Instruction for partial compaction (keeps recent messages)

services/compact/sessionMemoryCompact.ts

Path: src/services/compact/sessionMemoryCompact.ts

Purpose: Session memory compaction — a lighter alternative to full compact that summarizes older session memory segments while preserving recent tool context.

Constants:

export const DEFAULT_SM_COMPACT_CONFIG: SessionMemoryCompactConfig = {
  minTokens: 10000,
  minTextBlockMessages: 5,
  maxTokens: 40000
}

Exports:

export type SessionMemoryCompactConfig = {
  minTokens: number
  minTextBlockMessages: number
  maxTokens: number
}

export function setSessionMemoryCompactConfig(config: SessionMemoryCompactConfig): void
export function getSessionMemoryCompactConfig(): SessionMemoryCompactConfig
export function resetSessionMemoryCompactConfig(): void
export function hasTextBlocks(message: Message): boolean
export function adjustIndexToPreserveAPIInvariants(
  messages: Message[],
  startIndex: number
): number
export function calculateMessagesToKeepIndex(
  messages: Message[],
  lastSummarizedIndex: number
): number
export function shouldUseSessionMemoryCompaction(): boolean
export async function trySessionMemoryCompaction(
  messages: Message[],
  agentId?: string,
  autoCompactThreshold?: number
): Promise<CompactionResult | null>

Key Logic:

  • GrowthBook config tengu_sm_compact_config overrides defaults
  • Guarded by ENABLE_CLAUDE_CODE_SM_COMPACT / DISABLE_CLAUDE_CODE_SM_COMPACT env vars
  • AND requires both GrowthBook gates tengu_session_memory AND tengu_sm_compact to be enabled
  • adjustIndexToPreserveAPIInvariants(): ensures cut point doesn't leave orphaned tool_use without tool_result
  • calculateMessagesToKeepIndex(): binary search-based index calculation respecting min/max token bounds

services/compact/timeBasedMCConfig.ts

Path: src/services/compact/timeBasedMCConfig.ts

Purpose: Configuration for time-based microcompact (triggers on idle gap).

Exports:

export type TimeBasedMCConfig = {
  enabled: boolean
  gapThresholdMinutes: number
  keepRecent: number
}

export function getTimeBasedMCConfig(): TimeBasedMCConfig

Key Logic:

  • GrowthBook config: tengu_slate_heron
  • Defaults: { enabled: false, gapThresholdMinutes: 60, keepRecent: 5 }

services/diagnosticTracking.ts

Path: src/services/diagnosticTracking.ts

Purpose: Tracks file diagnostics (lint errors, type errors) before and after file edits to detect regressions introduced by Claude Code.

Constants:

  • MAX_DIAGNOSTICS_SUMMARY_CHARS = 4000

Exports:

export interface Diagnostic {
  severity: 'error' | 'warning' | 'information' | 'hint'
  message: string
  source?: string
  range: { start: { line: number; character: number }; end: { line: number; character: number } }
}

export interface DiagnosticFile {
  uri: string
  diagnostics: Diagnostic[]
}

export class DiagnosticTrackingService {
  static getInstance(): DiagnosticTrackingService
  initialize(mcpClient: unknown): void
  shutdown(): Promise<void>
  reset(): void
  ensureFileOpened(fileUri: string): Promise<void>
  beforeFileEdited(filePath: string): Promise<void>
}

Key Logic:

  • Singleton via getInstance()
  • beforeFileEdited(): captures current diagnostics for a file before any edits, so post-edit diagnostics can be compared
  • initialize(): connects to LSP MCP client for diagnostic data
  • shutdown(): flushes any pending diagnostic comparisons

services/internalLogging.ts

Path: src/services/internalLogging.ts

Purpose: Internal logging utilities for internal (ant) users — logs K8s namespace, container ID, and tool permission context for debugging.

Exports:

export async function getContainerId(): Promise<string | null>  // memoized
export async function logPermissionContextForAnts(
  toolPermissionContext: unknown,
  moment: string
): Promise<void>

Key Logic:

  • K8s namespace: reads from /var/run/secrets/kubernetes.io/serviceaccount/namespace
  • Container ID: extracted from /proc/self/mountinfo (first overlay/device entry)
  • Both are memoized — container identity doesn't change mid-session
  • Only logs when USER_TYPE === 'ant'

services/MagicDocs/magicDocs.ts

Path: src/services/MagicDocs/magicDocs.ts

Purpose: Auto-maintained markdown documentation files that stay in sync with code changes made by Claude Code.

Key Logic:

  • Monitors file edits and regenerates associated markdown docs
  • Uses Claude to understand the semantic meaning of changes
  • Prompts defined in prompts.ts

services/MagicDocs/prompts.ts

Path: src/services/MagicDocs/prompts.ts

Purpose: System prompt and instruction templates for magic docs generation.


services/mcpServerApproval.tsx

Path: src/services/mcpServerApproval.tsx

Purpose: Shows MCP server approval dialogs for pending project servers at startup, reusing the existing Ink root instance.

Exports:

export async function handleMcpjsonServerApprovals(root: Root): Promise<void>

Key Logic:

  • Queries getMcpConfigsByScope('project') for project-scoped MCP servers
  • Filters to servers with status 'pending' via getProjectMcpServerStatus()
  • Single pending server: renders MCPServerApprovalDialog
  • Multiple pending servers: renders MCPServerMultiselectDialog
  • Awaits user decision via Promise/resolve pattern before returning

services/mockRateLimits.ts

Path: src/services/mockRateLimits.ts

Purpose: Development/testing utility for simulating various rate limit scenarios without hitting actual API limits. Ant-only.

Exports:

export type MockHeaderKey =
  | 'x-ratelimit-requests-remaining'
  | 'x-ratelimit-tokens-remaining'
  // ... other rate limit header names

export type MockScenario =
  | 'primary_hard_limit'
  | 'secondary_hard_limit'
  | 'approaching_limit'
  | 'fast_mode_rate_limit'
  | 'burst_limit'
  // ... 20+ scenarios total

export function setMockHeader(key: MockHeaderKey, value?: string): void
export function addExceededLimit(type: string, hoursFromNow: number): void
export function setMockEarlyWarning(
  claimAbbrev: string,
  utilization: number,
  hoursFromNow?: number
): void
export function clearMockEarlyWarning(): void
export function setMockRateLimitScenario(scenario: MockScenario): void
export function getMockHeaderless429Message(): string | null
export function getMockHeaders(): MockHeaders | null
export function getMockStatus(): string
export function clearMockHeaders(): void
export function applyMockHeaders(headers: Headers): Headers
export function shouldProcessMockLimits(): boolean
export function getCurrentMockScenario(): MockScenario | null
export function getScenarioDescription(scenario: MockScenario): string
export function setMockSubscriptionType(type: SubscriptionType | null): void
export function getMockSubscriptionType(): SubscriptionType | null
export function shouldUseMockSubscription(): boolean
export function setMockBillingAccess(hasAccess: boolean | null): void
export function isMockFastModeRateLimitScenario(): boolean
export function checkMockFastModeRateLimit(isFastModeActive?: boolean): MockHeaders | null

services/notifier.ts

Path: src/services/notifier.ts

Purpose: System-level desktop notifications for long-running operations.

Key Logic:

  • Sends macOS/Linux notifications when task completes (user is away)
  • Uses node-notifier or native OS notification APIs
  • Gated on user preference and focus state

services/preventSleep.ts

Path: src/services/preventSleep.ts

Purpose: Prevents system sleep while Claude Code tasks are running.

Key Logic:

  • Uses platform-specific APIs (caffeinate on macOS, systemd-inhibit on Linux)
  • Returns a cleanup function to re-enable sleep
  • Only active during tool execution phases

services/PromptSuggestion/promptSuggestion.ts

Path: src/services/PromptSuggestion/promptSuggestion.ts

Purpose: Generates prompt suggestions based on current codebase context for the prompt input autocomplete.

Key Logic:

  • Analyzes recent git changes, open files, and task patterns
  • Returns ranked suggestion list
  • Caches suggestions per-context hash to avoid redundant generation

services/PromptSuggestion/speculation.ts

Path: src/services/PromptSuggestion/speculation.ts

Purpose: Speculative pre-execution — starts running likely next commands before user confirms, then either applies or discards the result.

Key Logic:

  • Monitors user typing patterns to predict next action
  • Pre-warms common tool executions (file reads, searches)
  • Cancels speculative execution if prediction was wrong

services/rateLimitMocking.ts

Path: src/services/rateLimitMocking.ts

Purpose: Facade layer for rate limit mock application and error checking.

Exports:

export function processRateLimitHeaders(headers: Headers): Headers
export function shouldProcessRateLimits(isSubscriber: boolean): boolean
export function checkMockRateLimitError(
  currentModel: string,
  isFastModeActive?: boolean
): APIError | null
export function isMockRateLimitError(error: unknown): boolean
export { shouldProcessMockLimits }  // re-exported from mockRateLimits.ts

services/rateLimitMessages.ts

Path: src/services/rateLimitMessages.ts

Purpose: Human-readable rate limit message formatting and display logic.

Key Logic:

  • Formats rate limit headers into user-friendly messages
  • Handles early warning, hard limit, and reset time display
  • Localizes timestamps to user's timezone

services/SessionMemory/prompts.ts

Path: src/services/SessionMemory/prompts.ts

Purpose: Prompt templates for session memory operations (summarization, retrieval, consolidation).


services/SessionMemory/sessionMemory.ts

Path: src/services/SessionMemory/sessionMemory.ts

Purpose: Manages persistent session memory — stores and retrieves relevant context snippets across sessions.

Key Logic:

  • GrowthBook gate: tengu_session_memory
  • Stores memory in ~/.claude/sessions/<sessionId>/memory.jsonl
  • Retrieves relevant memories using semantic similarity
  • Integrates with conversation context as system prompt additions

services/SessionMemory/sessionMemoryUtils.ts

Path: src/services/SessionMemory/sessionMemoryUtils.ts

Purpose: Utility functions for session memory operations (formatting, filtering, path resolution).


services/tokenEstimation.ts

Path: src/services/tokenEstimation.ts

Purpose: Rough token count estimation without calling the API tokenizer.

Exports:

export function roughTokenCountEstimation(text: string): number

Key Logic: Approximates token count as text.length / 4 (roughly 4 chars per token for English/code). Used for pre-flight estimates in compaction decisions.


services/vcr.ts

Path: src/services/vcr.ts

Purpose: VCR (Video Cassette Recorder) test fixture system — records and replays API interactions for deterministic testing.

Exports:

export async function withVCR<T>(
  messages: unknown[],
  f: () => Promise<T>
): Promise<T>

export async function withFixture<T>(
  input: unknown,
  fixtureName: string,
  f: () => Promise<T>
): Promise<T>

export async function withStreamingVCR<T>(
  messages: unknown[],
  f: () => Promise<T>
): Promise<T>

Key Logic:

  • SHA1-hashes input to create fixture filenames (deterministic, content-addressed)
  • FORCE_VCR env var: ants can force VCR mode outside of test environment
  • CI guard: fails if fixture is missing and VCR_RECORD is not set (prevents silent misses)
  • Fixture storage: src/test-fixtures/vcr/ directory

services/voice.ts

Path: src/services/voice.ts

Purpose: Audio recording service for push-to-talk voice input. Supports native audio (cpal via NAPI) on macOS/Linux/Windows with SoX and arecord fallbacks on Linux.

Constants:

  • RECORDING_SAMPLE_RATE = 16000
  • RECORDING_CHANNELS = 1
  • SILENCE_DURATION_SECS = '2.0' — SoX silence detection
  • SILENCE_THRESHOLD = '3%'

Exports:

export type RecordingAvailability = {
  available: boolean
  reason: string | null
}

export async function checkVoiceDependencies(): Promise<{
  available: boolean
  missing: string[]
  installCommand: string | null
}>

export async function requestMicrophonePermission(): Promise<boolean>

export async function checkRecordingAvailability(): Promise<RecordingAvailability>

export async function startRecording(
  onData: (chunk: Buffer) => void,
  onEnd: () => void,
  options?: { silenceDetection?: boolean }
): Promise<boolean>

export function stopRecording(): void

export function _resetArecordProbeForTesting(): void
export function _resetAlsaCardsForTesting(): void

Key Logic:

  • Backend selection priority: native (cpal via NAPI) → arecord (ALSA, Linux only) → SoX rec
  • Native module: audio-capture-napi is lazy-loaded on first voice keypress (dlopen blocks event loop ~1s warm, ~8s cold)
  • arecord probe: memoized async probe that verifies device open succeeds (not just binary existence); 150ms race timer
  • Linux ALSA guard: checks /proc/asound/cards before using native cpal to avoid spurious stderr
  • WSL handling: distinguishes WSL1 (no audio), Win10 WSL2 (no audio), Win11 WSLg (PulseAudio works)
  • SoX arguments: raw PCM 16kHz/16-bit/mono with --buffer 1024 for small chunk flushing and silence detection
  • Push-to-talk mode: silenceDetection: false ignores native module's silence-triggered onEnd

services/voiceKeyterms.ts

Path: src/services/voiceKeyterms.ts

Purpose: Generates domain-specific vocabulary hints (Deepgram "keywords") for improved STT accuracy in the voice_stream endpoint.

Constants:

  • MAX_KEYTERMS = 50
  • GLOBAL_KEYTERMS: hardcoded list including 'MCP', 'symlink', 'grep', 'regex', 'localhost', 'codebase', 'TypeScript', 'JSON', 'OAuth', 'webhook', 'gRPC', 'dotfiles', 'subagent', 'worktree'

Exports:

export function splitIdentifier(name: string): string[]

export async function getVoiceKeyterms(
  recentFiles?: ReadonlySet<string>
): Promise<string[]>

Key Logic:

  • splitIdentifier(): splits camelCase/PascalCase/kebab-case/snake_case/path identifiers into words; discards fragments ≤2 chars
  • getVoiceKeyterms(): combines global terms + project root basename + git branch words + recent file name words
  • Project root basename kept whole (not split) to match full project name phrases
  • Git branch words split via splitIdentifier(); recent files split by filename stem
  • Capped at MAX_KEYTERMS = 50

services/voiceStreamSTT.ts

Path: src/services/voiceStreamSTT.ts

Purpose: voice_stream WebSocket STT client. Connects to wss://api.anthropic.com/api/ws/speech_to_text/voice_stream using OAuth credentials for push-to-talk transcription.

Constants:

  • VOICE_STREAM_PATH = '/api/ws/speech_to_text/voice_stream'
  • KEEPALIVE_INTERVAL_MS = 8_000
  • FINALIZE_TIMEOUTS_MS = { safety: 5_000, noData: 1_500 } (exported for tests)
  • Wire messages: KEEPALIVE_MSG = '{"type":"KeepAlive"}', CLOSE_STREAM_MSG = '{"type":"CloseStream"}'

Exports:

export const FINALIZE_TIMEOUTS_MS: { safety: number; noData: number }

export type VoiceStreamCallbacks = {
  onTranscript: (text: string, isFinal: boolean) => void
  onError: (error: string, opts?: { fatal?: boolean }) => void
  onClose: () => void
  onReady: (connection: VoiceStreamConnection) => void
}

export type FinalizeSource =
  | 'post_closestream_endpoint'
  | 'no_data_timeout'
  | 'safety_timeout'
  | 'ws_close'
  | 'ws_already_closed'

export type VoiceStreamConnection = {
  send: (audioChunk: Buffer) => void
  finalize: () => Promise<FinalizeSource>
  close: () => void
  isConnected: () => boolean
}

export function isVoiceStreamAvailable(): boolean

export async function connectVoiceStream(
  callbacks: VoiceStreamCallbacks,
  options?: { language?: string; keyterms?: string[] }
): Promise<VoiceStreamConnection | null>

Key Logic:

  • Only available for OAuth-authenticated users (OAuth tokens); gates on isOAuthAuthEnabled() and valid access token
  • Routes to api.anthropic.com (not claude.ai) to avoid Cloudflare TLS fingerprinting challenges
  • VOICE_STREAM_BASE_URL env var allows override for testing
  • URL params: encoding=linear16, sample_rate=16000, channels=1, endpointing_ms=300, utterance_end_ms=1000
  • GrowthBook gate tengu_cobalt_frost: enables Nova 3 STT provider via use_conversation_engine=true&stt_provider=deepgram-nova3
  • Keyterms appended as repeated keyterms= query params
  • keepalive interval: 8s
  • finalize() sends CloseStream, races noData (1.5s) vs safety (5s) timers vs TranscriptEndpoint message
  • Wire message types: TranscriptText, TranscriptEndpoint, TranscriptError, error
  • Ant-only build (behind feature('VOICE_MODE') gate)

context/QueuedMessageContext.tsx

Path: src/context/QueuedMessageContext.tsx

Purpose: React context for the queued message system — tracks messages waiting to be sent to Claude when the current turn completes.

Exports:

export const QueuedMessageContext: React.Context<QueuedMessage[]>
export function useQueuedMessages(): QueuedMessage[]
export function QueuedMessageProvider(props: { children: React.ReactNode }): JSX.Element

context/fpsMetrics.tsx

Path: src/context/fpsMetrics.tsx

Purpose: FPS (frames per second) measurement context for Ink terminal rendering performance monitoring.

Exports:

export function FpsMetricsProvider(props: { children: React.ReactNode }): JSX.Element
export function useFpsMetrics(): { fps: number; frameCount: number }

context/mailbox.tsx

Path: src/context/mailbox.tsx

Purpose: Provides inter-agent messaging context — the "mailbox" for receiving messages from other agents/workers.

Exports:

export type MailboxMessage = { from: string; content: string; timestamp: number }
export const MailboxContext: React.Context<MailboxMessage[]>
export function MailboxProvider(props: { children: React.ReactNode }): JSX.Element
export function useMailbox(): MailboxMessage[]

context/modalContext.tsx

Path: src/context/modalContext.tsx

Purpose: Context for managing modal dialog state — tracks which modal is currently open and provides open/close actions.

Exports:

export type ModalState = { isOpen: boolean; content: React.ReactNode | null }
export const ModalContext: React.Context<ModalState>
export function ModalProvider(props: { children: React.ReactNode }): JSX.Element
export function useModal(): { open: (content: React.ReactNode) => void; close: () => void }

context/notifications.tsx

Path: src/context/notifications.tsx

Purpose: Notification queue management — displays toast-style notifications in the status line with priority ordering, deduplication, fold/merge, and timeout handling.

Types:

type Priority = 'low' | 'medium' | 'high' | 'immediate'

type BaseNotification = {
  key: string
  invalidates?: string[]
  priority: Priority
  timeoutMs?: number
  fold?: (accumulator: Notification, incoming: Notification) => Notification
}

type TextNotification = BaseNotification & { text: string; color?: keyof Theme }
type JSXNotification = BaseNotification & { jsx: React.ReactNode }
export type Notification = TextNotification | JSXNotification

Exports:

const DEFAULT_TIMEOUT_MS = 8000

export function useNotifications(): {
  addNotification: (content: Notification) => void
  removeNotification: (key: string) => void
}

Key Logic:

  • Notification state lives in AppState (notifications.current + notifications.queue)
  • immediate priority: bypasses queue, shows immediately, re-queues current (non-immediate) notification
  • fold function: merges notifications with the same key (accumulator pattern)
  • Deduplication: only one notification per key in queue + current
  • invalidates[]: removes named notifications from queue and clears current if matching
  • DEFAULT_TIMEOUT_MS = 8000 (8 seconds); auto-advance to next queued after timeout
  • Module-level currentTimeoutId tracks the active auto-dismiss timer

context/overlayContext.tsx

Path: src/context/overlayContext.tsx

Purpose: Overlay tracking for Escape key coordination. Tracks which overlays (dialogs, selects) are currently open so the cancel handler doesn't misinterpret Escape presses.

Constants:

const NON_MODAL_OVERLAYS = new Set(['autocomplete'])

Exports:

export function useRegisterOverlay(id: string, enabled?: boolean): void
export function useIsOverlayActive(): boolean
export function useIsModalOverlayActive(): boolean

Key Logic:

  • State stored in AppState.activeOverlays: Set<string>
  • useRegisterOverlay(): registers on mount (useEffect), unregisters on unmount via cleanup
  • On overlay close: triggers instances.get(process.stdout)?.invalidatePrevFrame() (via useLayoutEffect) to force full-damage diff — prevents ghost cells from tall overlays (e.g. FuzzyPicker)
  • useIsOverlayActive(): activeOverlays.size > 0
  • useIsModalOverlayActive(): any overlay in set that is NOT in NON_MODAL_OVERLAYS

context/promptOverlayContext.tsx

Path: src/context/promptOverlayContext.tsx

Purpose: Context for prompt-level overlay management — tracks overlays that affect prompt input focus and behavior.

Exports:

export function useRegisterPromptOverlay(id: string, enabled?: boolean): void
export function useIsPromptOverlayActive(): boolean

context/stats.tsx

Path: src/context/stats.tsx

Purpose: In-process performance metrics store with reservoir sampling for histograms. Persists metrics to project config on process exit.

Constants:

  • RESERVOIR_SIZE = 1024 — reservoir sampling capacity for histograms

Types:

export type StatsStore = {
  increment(name: string, value?: number): void
  set(name: string, value: number): void
  observe(name: string, value: number): void
  add(name: string, value: string): void
  getAll(): Record<string, number>
}

Exports:

export function createStatsStore(): StatsStore
export const StatsContext: React.Context<StatsStore | null>
export function StatsProvider(props: { store?: StatsStore; children: React.ReactNode }): JSX.Element
export function useStats(): StatsStore
export function useCounter(name: string): (value?: number) => void
export function useGauge(name: string): (value: number) => void
export function useTimer(name: string): (value: number) => void
// ... additional hook exports

Key Logic:

  • createStatsStore(): creates a stats store with three internal data structures:
    • metrics: Map<string, number> — counters and gauges
    • histograms: Map<string, Histogram> — reservoir-sampled distributions
    • sets: Map<string, Set<string>> — unique value sets (reported as .size)
  • observe(): histogram using reservoir sampling (Algorithm R) with RESERVOIR_SIZE = 1024
  • getAll(): returns flat Record<string, number> with histogram percentiles (_p50, _p95, _p99), min, max, avg, count
  • StatsProvider: flushes metrics to lastSessionMetrics in project config on process 'exit' event
  • useCounter() / useGauge() / useTimer(): memoized hooks returning bound store methods

context/voice.tsx

Path: src/context/voice.tsx

Purpose: Voice mode context — provides voice recording state, transcript, and connection status to the UI.

Exports:

export type VoiceContextValue = {
  isRecording: boolean
  transcript: string
  isConnecting: boolean
  error: string | null
  startRecording: () => void
  stopRecording: () => void
}

export const VoiceContext: React.Context<VoiceContextValue>
export function VoiceProvider(props: { children: React.ReactNode }): JSX.Element
export function useVoice(): VoiceContextValue

screens/Doctor.tsx

Path: src/screens/Doctor.tsx

Purpose: The /doctor command UI screen — displays system health diagnostics including version info, environment checks, MCP server status, sandbox status, keybinding warnings, and available updates.

Props:

type Props = {
  onDone: () => void
}

Internal Types:

type AgentInfo = {
  name: string
  version: string
  // ...
}

type VersionLockInfo = {
  locked: boolean
  version?: string
  // ...
}

Key Logic:

  • Uses getDoctorDiagnostic() for environment checks
  • Calls checkContextWarnings() for context-related issues
  • Loads dist tags with getNpmDistTags() and getGcsDistTags() (wrapped in Suspense)
  • Renders sub-sections: SandboxDoctorSection, ValidationErrorsList, KeybindingWarnings, McpParsingWarnings
  • Displays version comparison: current vs latest npm/GCS versions
  • Shows agent info, version lock status, and update channels

screens/REPL.tsx

Path: src/screens/REPL.tsx

Purpose: The main interactive REPL screen — the primary conversational UI that orchestrates the entire Claude Code interactive session.

Key Logic:

  • Manages the full conversation lifecycle: user input → API query → tool execution → response display
  • Handles all interactive features: conversation history, compaction, clear, file editing, permissions
  • Routes slash commands to command handlers
  • Manages modal dialogs (permissions, MCP approvals, etc.)
  • Integrates with all context providers: notifications, overlays, voice, stats
  • The largest component in the codebase, responsible for the overall user interaction loop

screens/ResumeConversation.tsx

Path: src/screens/ResumeConversation.tsx

Purpose: UI screen for the session resume flow — shows a list of recent sessions with previews and allows the user to select one to resume.

Key Logic:

  • Loads session index from disk
  • Renders SessionPreview components for each session
  • Handles keyboard navigation (arrow keys, Enter to select)
  • Filters sessions by project root or shows all
  • Passes selected session ID back to caller via callback

Cross-Cutting Architecture Notes

State Management Architecture

The codebase uses three distinct state layers:

  1. bootstrap/state.ts — Global mutable singleton for session-scoped state (costs, model, telemetry). Intentionally a DAG leaf — imports nothing from services.

  2. React AppState — UI state via Zustand-like store (state/AppState.ts). Accessed via useAppState() selectors and useSetAppState().

  3. React Contexts — Domain-specific contexts (notifications, overlays, stats, voice) for scoped subtree state.

Analytics Architecture

Events flow through a multi-layer pipeline:

logEvent() → index.ts queue → sink.ts dispatch
  → [sampling check] → [isSinkKilled check]
  → datadog.ts (strips _PROTO_ keys)
  → firstPartyEventLogger.ts (hoists _PROTO_ keys to proto fields)

All analytics is disabled when: NODE_ENV === test, 3P providers (Bedrock/Vertex/Foundry), or isTelemetryDisabled().

Compact/Microcompact Architecture

Context compression has multiple layers:

  1. apiMicrocompact.ts — API-native server-side editing (cache_edits) — no client-side mutation
  2. microCompact.ts — Client-side tool result clearing (cached path + time-based path)
  3. autoCompact.ts — Full conversation summarization trigger (threshold-based)
  4. compact.ts — Full summarization implementation (LLM call)
  5. sessionMemoryCompact.ts — Lighter session memory segment summarization

GrowthBook Feature Flag Naming Convention

All feature flags use obfuscated/mangled names with tengu_ prefix to prevent scraping:

  • Feature flags: tengu_prompt_cache_1h_config, tengu_session_memory, tengu_sm_compact, etc.
  • Kill switches: tengu_frond_boric (analytics sink killswitch)
  • Voice: tengu_cobalt_frost (Nova 3 STT)
  • AutoDream: tengu_onyx_plover
  • Coordinator mode: checked via feature('COORDINATOR_MODE') bundle flag