@forcefield/mcp-server 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/db/client.ts","../src/auth/key-exchange.ts","../src/auth/gating.ts","../src/config.ts","../src/auth/session.ts","../src/utils/metrics.ts","../src/utils/logger.ts","../src/auth/rls-check.ts","../src/utils/static-data.ts","../src/server.ts","../../shared/src/schemas/ff-company.ts","../../shared/src/schemas/common.ts","../../shared/src/schemas/ff-scan.ts","../../shared/src/schemas/ff-compliance.ts","../../shared/src/schemas/ff-vault.ts","../../shared/src/schemas/ff-generate.ts","../../shared/src/schemas/ff-equity.ts","../../shared/src/schemas/ff-integrate.ts","../../shared/src/schemas/ff-system.ts","../src/errors/catalog.ts","../src/auth/ownership.ts","../src/utils/pii-masker.ts","../src/utils/response.ts","../src/utils/audit.ts","../src/db/companies.ts","../src/db/completeness.ts","../src/db/documents.ts","../src/domain/compliance/rule-loader.ts","../src/domain/compliance/rule-evaluator.ts","../src/domain/compliance/deadline-generator.ts","../src/domain/compliance/severity.ts","../src/db/deadlines.ts","../src/tools/ff-company.ts","../src/tools/ff-scan.ts","../src/domain/scanning/path-validator.ts","../src/domain/scanning/extract-text.ts","../src/domain/scanning/ocr-worker.ts","../src/domain/compliance/compliance-scorer.ts","../src/domain/compliance/next-occurrence.ts","../src/domain/compliance/tax-calculator.ts","../src/domain/compliance/disclaimers.ts","../src/domain/templates/template-loader.ts","../src/domain/compliance/template-linker.ts","../src/tools/ff-compliance.ts","../src/tools/ff-system.ts","../src/tools/ff-vault.ts","../src/domain/classification/classifier.ts","../src/db/sensitive.ts","../src/tools/ff-generate.ts","../src/domain/templates/pre-filler.ts","../src/domain/templates/condition-evaluator.ts","../src/domain/templates/interview-engine.ts","../src/domain/templates/document-renderer.ts","../src/domain/templates/docx-generator.ts","../src/domain/templates/pdf-converter.ts","../src/domain/templates/category-mapping.ts","../src/db/interview-sessions.ts","../src/db/share-classes.ts","../src/db/equity-plans.ts","../src/db/stakeholders.ts","../src/db/equity-grants.ts","../src/domain/conversion.ts","../src/domain/ownership.ts","../src/domain/vesting.ts","../src/domain/waterfall.ts","../src/domain/modeling.ts","../src/domain/cap-table-import.ts","../src/domain/cap-table-export.ts","../src/tools/ff-equity.ts","../src/tools/ff-integrate.ts","../src/db/notification-preferences.ts","../src/db/integration-credentials.ts","../src/domain/integration/google-auth.ts","../src/domain/integration/google-drive.ts","../src/domain/integration/gmail.ts","../src/domain/integration/google-calendar.ts","../src/domain/integration/notion-auth.ts","../src/domain/integration/notion.ts"],"sourcesContent":["/**\n * Forcefield MCP Server — stdio entry point.\n *\n * Bootstrap: validate config → exchange API key → create session → start MCP server.\n * Communicates via stdout (JSON-RPC). All logging goes to stderr.\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { bootstrapSession } from './auth/session.js';\nimport { verifyRlsEnforced } from './auth/rls-check.js';\nimport { initStaticData } from './utils/static-data.js';\nimport { createMcpServer } from './server.js';\nimport { logger } from './utils/logger.js';\n\nasync function main(): Promise<void> {\n try {\n logger.info('Forcefield MCP server starting...');\n\n // Bootstrap: config validation + key exchange + gating load\n const ctx = await bootstrapSession();\n logger.info('Session bootstrapped', { userId: ctx.userId, tier: ctx.tier });\n\n // Initialize static data loader (must precede any tool usage)\n initStaticData(ctx.supabase);\n\n // Verify RLS is enforced (security check)\n await verifyRlsEnforced(ctx.supabase);\n\n // Create MCP server with all tools + resources\n const mcpServer = createMcpServer(ctx);\n\n // Connect via stdio\n const transport = new StdioServerTransport();\n await mcpServer.connect(transport);\n\n logger.info('Forcefield MCP server ready (stdio)');\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n process.stderr.write(`[fatal] Failed to start Forcefield: ${message}\\n`);\n\n if (message.includes('ff_live_')) {\n process.stderr.write(\n '[hint] Set FORCEFIELD_API_KEY to your API key. Run `npx -y @forcefield/mcp-server forcefield-setup` to configure.\\n',\n );\n }\n\n process.exit(1);\n }\n}\n\nmain();\n","/**\n * db/client.ts — Supabase client factory.\n * Creates an authenticated client using a JWT from key exchange.\n */\n\nimport { createClient, type SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../../../supabase/types/database.js';\n\nconst DEFAULT_URL = 'http://127.0.0.1:54321';\nconst DEFAULT_ANON_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0';\n\nexport type TypedSupabaseClient = SupabaseClient<Database>;\n\n/**\n * Create an authenticated Supabase client.\n * Uses the anon key for initial connection, then sets the JWT for auth.\n */\nexport function createSupabaseClient(\n jwt: string,\n supabaseUrl?: string,\n anonKey?: string,\n): TypedSupabaseClient {\n const url = supabaseUrl ?? DEFAULT_URL;\n const key = anonKey ?? DEFAULT_ANON_KEY;\n\n return createClient<Database>(url, key, {\n global: {\n headers: {\n Authorization: `Bearer ${jwt}`,\n },\n },\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n },\n });\n}\n","/**\n * auth/key-exchange.ts — Client-side API key → JWT exchange.\n *\n * Calls the key-exchange Edge Function to convert a long-lived API key\n * into a short-lived JWT. Caches the JWT and auto-re-exchanges on expiry.\n */\n\ninterface KeyExchangeResult {\n jwt: string;\n expiresAt: number; // Unix timestamp in ms\n userId: string;\n tier: 'free' | 'paid';\n}\n\ninterface KeyExchangeError {\n error: string;\n code: string;\n}\n\n/** Buffer before expiry to trigger re-exchange (5 minutes). */\nconst EXPIRY_BUFFER_MS = 5 * 60 * 1000;\n\nexport class KeyExchangeClient {\n private _cached: KeyExchangeResult | null = null;\n private readonly _apiKey: string;\n private readonly _edgeFunctionUrl: string;\n\n constructor(apiKey: string, supabaseUrl: string) {\n this._apiKey = apiKey;\n this._edgeFunctionUrl = `${supabaseUrl}/functions/v1/key-exchange`;\n }\n\n /**\n * Get a valid JWT, exchanging the API key if needed.\n * Transparently re-exchanges when the cached JWT is near expiry.\n */\n async getJwt(): Promise<KeyExchangeResult> {\n if (this._cached && !this._isExpiringSoon()) {\n return this._cached;\n }\n return this._exchange();\n }\n\n /** Check if the cached JWT is expiring within the buffer window. */\n private _isExpiringSoon(): boolean {\n if (!this._cached) return true;\n return Date.now() + EXPIRY_BUFFER_MS >= this._cached.expiresAt;\n }\n\n /** Perform the actual key exchange against the Edge Function. */\n private async _exchange(): Promise<KeyExchangeResult> {\n const response = await fetch(this._edgeFunctionUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this._apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as Partial<KeyExchangeError>;\n const message =\n body.error ?? 'API key invalid or revoked. Visit https://app.forcefield.dev/signup to get a new key.';\n throw new Error(`Key exchange failed (${response.status}): ${message}`);\n }\n\n const data = (await response.json()) as KeyExchangeResult;\n this._cached = data;\n return data;\n }\n\n /** Clear cached JWT (for testing or forced re-exchange). */\n clearCache(): void {\n this._cached = null;\n }\n}\n","/**\n * auth/gating.ts — Action-level tier gating.\n *\n * Reads the action_gating table from Supabase and caches for the session.\n * Falls back to hardcoded defaults if the table is unreachable.\n */\n\nimport type { TypedSupabaseClient } from '../db/client.js';\n\nexport interface GatingRule {\n tool: string;\n action: string;\n tier: 'free' | 'paid';\n}\n\nexport type GatingConfig = Map<string, GatingRule>;\n\n/** Default gating rules compiled into the package (fallback). */\nconst DEFAULT_FREE_ACTIONS = new Set([\n 'ff_company:create',\n 'ff_company:get',\n 'ff_company:list',\n 'ff_company:update',\n 'ff_company:add_note',\n 'ff_scan:file',\n 'ff_scan:folder',\n 'ff_compliance:generate_deadlines',\n 'ff_compliance:score',\n 'ff_compliance:list_deadlines',\n 'ff_compliance:complete',\n 'ff_compliance:reopen',\n 'ff_compliance:history',\n 'ff_compliance:guide',\n 'ff_compliance:template_links',\n 'ff_system:health',\n 'ff_system:audit_log',\n 'ff_system:get_metrics',\n 'ff_generate:list_templates',\n 'ff_generate:preview',\n]);\n\n/**\n * Load gating config from Supabase.\n * Falls back to hardcoded defaults on failure.\n */\nexport async function loadGatingConfig(supabase: TypedSupabaseClient): Promise<GatingConfig> {\n try {\n const { data, error } = await supabase\n .from('action_gating')\n .select('tool, action, tier');\n\n if (error || !data) {\n process.stderr.write(`[warn] Failed to load gating config: ${error?.message ?? 'no data'}. Using defaults.\\n`);\n return buildDefaultGating();\n }\n\n const gating: GatingConfig = new Map();\n for (const row of data) {\n const key = `${row.tool}:${row.action}`;\n gating.set(key, {\n tool: row.tool,\n action: row.action,\n tier: row.tier as 'free' | 'paid',\n });\n }\n return gating;\n } catch {\n process.stderr.write('[warn] Gating table unreachable. Using hardcoded defaults.\\n');\n return buildDefaultGating();\n }\n}\n\nfunction buildDefaultGating(): GatingConfig {\n const gating: GatingConfig = new Map();\n for (const key of DEFAULT_FREE_ACTIONS) {\n const [tool, action] = key.split(':') as [string, string];\n gating.set(key, { tool, action, tier: 'free' });\n }\n return gating;\n}\n\n/**\n * Check if an action is allowed for the given tier.\n * Throws if the action requires a higher tier.\n */\nexport function assertActionAllowed(\n gating: GatingConfig,\n tool: string,\n action: string,\n userTier: 'free' | 'paid',\n): void {\n if (isGatingDisabled()) {\n return;\n }\n\n const key = `${tool}:${action}`;\n const rule = gating.get(key);\n\n // If not in gating table, default to paid (safe default)\n const requiredTier = rule?.tier ?? 'paid';\n\n if (requiredTier === 'paid' && userTier === 'free') {\n throw new TierRestrictionError(tool, action);\n }\n}\n\nexport class TierRestrictionError extends Error {\n readonly tool: string;\n readonly action: string;\n\n constructor(tool: string, action: string) {\n super(\n `${tool}/${action} requires a paid subscription. ` +\n 'Upgrade at https://app.forcefield.dev/settings to unlock this feature.',\n );\n this.name = 'TierRestrictionError';\n this.tool = tool;\n this.action = action;\n }\n}\n\nfunction isGatingDisabled(): boolean {\n const value = process.env.FORCEFIELD_DISABLE_GATING;\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';\n}\n","/**\n * config.ts — Single source for all env vars.\n * Zod-validated. Fail fast if required vars missing.\n * No `process.env` access anywhere else in the codebase.\n */\n\nimport { z } from 'zod';\n\nconst BoolFromEnv = z.preprocess((value) => {\n if (typeof value === 'boolean') return value;\n if (typeof value !== 'string') return value;\n const normalized = value.trim().toLowerCase();\n return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';\n}, z.boolean());\n\nconst ConfigSchema = z.object({\n FORCEFIELD_API_KEY: z.string().startsWith('ff_live_'),\n SUPABASE_URL: z.string().url().default('http://127.0.0.1:54321'),\n SUPABASE_ANON_KEY: z.string().min(1).optional(),\n SUPABASE_SERVICE_ROLE_KEY: z.string().min(1).optional(),\n CLOUDCONVERT_API_KEY: z.string().optional(),\n GOOGLE_CLIENT_ID: z.string().optional(),\n GOOGLE_REDIRECT_URI: z.string().url().optional(),\n NOTION_CLIENT_ID: z.string().optional(),\n NOTION_REDIRECT_URI: z.string().url().optional(),\n LOG_LEVEL: z.enum(['error', 'warn', 'info', 'debug']).default('info'),\n FORCEFIELD_LOCAL_DEV: BoolFromEnv.default(false),\n FORCEFIELD_DISABLE_GATING: BoolFromEnv.default(false),\n FORCEFIELD_LOCAL_DATA_FALLBACK: BoolFromEnv.default(false),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\nlet _config: Config | undefined;\n\n/**\n * Parse and validate config from env vars.\n * Caches result after first call.\n * Throws on invalid config (fail-fast).\n */\nexport function getConfig(): Config {\n if (_config) return _config;\n _config = ConfigSchema.parse(process.env);\n return _config;\n}\n\n/**\n * Reset cached config (for testing only).\n */\nexport function _resetConfig(): void {\n _config = undefined;\n}\n","/**\n * auth/session.ts — SessionContext bootstrap.\n *\n * Created at MCP server startup. Passed to tool handlers via closure.\n * No global singletons. Tests inject mock context.\n */\n\nimport type { TypedSupabaseClient } from '../db/client.js';\nimport { createSupabaseClient } from '../db/client.js';\nimport { KeyExchangeClient } from './key-exchange.js';\nimport { loadGatingConfig, type GatingConfig } from './gating.js';\nimport { getConfig } from '../config.js';\n\nexport interface SessionContext {\n supabase: TypedSupabaseClient;\n userId: string;\n tier: 'free' | 'paid';\n gating: GatingConfig;\n /** Re-exchange the API key and refresh the Supabase client. */\n refreshAuth: () => Promise<void>;\n}\n\n/**\n * Bootstrap the session: exchange API key for JWT, load gating, create context.\n * Uses Promise.all for key exchange + gating load to save one round-trip.\n */\nexport async function bootstrapSession(): Promise<SessionContext> {\n const config = getConfig();\n const supabaseUrl = config.SUPABASE_URL;\n\n const keyExchangeClient = new KeyExchangeClient(config.FORCEFIELD_API_KEY, supabaseUrl);\n\n // Exchange key for JWT\n const exchangeResult = await keyExchangeClient.getJwt();\n\n // Create authenticated client\n let supabase = createSupabaseClient(exchangeResult.jwt, supabaseUrl, config.SUPABASE_ANON_KEY);\n\n // Load gating config (uses the authenticated client)\n const gating = await loadGatingConfig(supabase);\n\n const context: SessionContext = {\n supabase,\n userId: exchangeResult.userId,\n tier: exchangeResult.tier,\n gating,\n refreshAuth: async () => {\n keyExchangeClient.clearCache();\n const freshResult = await keyExchangeClient.getJwt();\n supabase = createSupabaseClient(freshResult.jwt, supabaseUrl, config.SUPABASE_ANON_KEY);\n context.supabase = supabase;\n context.userId = freshResult.userId;\n context.tier = freshResult.tier;\n },\n };\n\n return context;\n}\n","/**\n * utils/metrics.ts — Lightweight in-process metrics for observability.\n *\n * Tracks tool call counts, latencies (p50/p95/p99), and error rates.\n * Exposed via ff_system(action: \"get_metrics\") for dashboards/alerts.\n * Resets on process restart (MCP runs per-session).\n */\n\ninterface ToolMetrics {\n calls: number;\n errors: number;\n latencies: number[]; // ms, last 1000\n}\n\nconst MAX_LATENCY_SAMPLES = 1000;\nconst metricsMap = new Map<string, ToolMetrics>();\n\nfunction getOrCreate(key: string): ToolMetrics {\n let m = metricsMap.get(key);\n if (!m) {\n m = { calls: 0, errors: 0, latencies: [] };\n metricsMap.set(key, m);\n }\n return m;\n}\n\nexport function recordToolCall(tool: string, action: string, durationMs: number, error: boolean): void {\n const key = `${tool}.${action}`;\n const m = getOrCreate(key);\n m.calls++;\n if (error) m.errors++;\n m.latencies.push(durationMs);\n if (m.latencies.length > MAX_LATENCY_SAMPLES) {\n m.latencies.shift();\n }\n}\n\nfunction percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0;\n const idx = Math.ceil((p / 100) * sorted.length) - 1;\n return sorted[Math.max(0, idx)]!;\n}\n\nexport interface MetricsSummary {\n tool: string;\n calls: number;\n errors: number;\n errorRate: number;\n p50Ms: number;\n p95Ms: number;\n p99Ms: number;\n}\n\nexport function getMetrics(): MetricsSummary[] {\n const result: MetricsSummary[] = [];\n\n for (const [tool, m] of metricsMap) {\n const sorted = [...m.latencies].sort((a, b) => a - b);\n result.push({\n tool,\n calls: m.calls,\n errors: m.errors,\n errorRate: m.calls > 0 ? m.errors / m.calls : 0,\n p50Ms: percentile(sorted, 50),\n p95Ms: percentile(sorted, 95),\n p99Ms: percentile(sorted, 99),\n });\n }\n\n return result.sort((a, b) => b.calls - a.calls);\n}\n\nexport function getHealthStatus(): {\n status: 'healthy' | 'degraded';\n uptimeMs: number;\n totalCalls: number;\n totalErrors: number;\n} {\n let totalCalls = 0;\n let totalErrors = 0;\n\n for (const m of metricsMap.values()) {\n totalCalls += m.calls;\n totalErrors += m.errors;\n }\n\n const errorRate = totalCalls > 0 ? totalErrors / totalCalls : 0;\n\n return {\n status: errorRate > 0.1 ? 'degraded' : 'healthy',\n uptimeMs: Math.floor(process.uptime() * 1000),\n totalCalls,\n totalErrors,\n };\n}\n","/**\n * utils/logger.ts — Structured JSON logger to stderr.\n *\n * stdout is reserved for MCP JSON-RPC. All logging goes to stderr.\n * Never log PII. Log every tool invocation with timing.\n */\n\nimport { recordToolCall } from './metrics.js';\n\nexport type LogLevel = 'error' | 'warn' | 'info' | 'debug';\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n};\n\nlet currentLevel: LogLevel = 'info';\n\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LEVEL_PRIORITY[level] <= LEVEL_PRIORITY[currentLevel];\n}\n\ninterface LogEntry {\n level: LogLevel;\n message: string;\n [key: string]: unknown;\n}\n\nfunction writeLog(entry: LogEntry): void {\n if (!shouldLog(entry.level)) return;\n const line = JSON.stringify({ ...entry, timestamp: new Date().toISOString() });\n process.stderr.write(line + '\\n');\n}\n\nexport const logger = {\n error(message: string, extra?: Record<string, unknown>): void {\n writeLog({ level: 'error', message, ...extra });\n },\n\n warn(message: string, extra?: Record<string, unknown>): void {\n writeLog({ level: 'warn', message, ...extra });\n },\n\n info(message: string, extra?: Record<string, unknown>): void {\n writeLog({ level: 'info', message, ...extra });\n },\n\n debug(message: string, extra?: Record<string, unknown>): void {\n writeLog({ level: 'debug', message, ...extra });\n },\n\n /** Log a tool invocation with timing and record metrics. Call at start, use returned function at end. */\n toolInvocation(tool: string, action: string): (error?: boolean) => void {\n const start = Date.now();\n return (error = false) => {\n const durationMs = Date.now() - start;\n writeLog({ level: 'info', message: 'Tool invocation', tool, action, durationMs, error });\n recordToolCall(tool, action, durationMs, error);\n };\n },\n} as const;\n","/**\n * auth/rls-check.ts — RLS health check on MCP server startup.\n *\n * Verifies that RLS is enforced by attempting an unscoped query.\n * If RLS is accidentally disabled, the query would return rows\n * from other users — a critical security failure.\n */\n\nimport type { TypedSupabaseClient } from '../db/client.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Verify RLS is enforced on the companies table.\n * A properly configured RLS policy should only return companies\n * owned by the authenticated user. If count > 1000, something is wrong.\n */\nexport async function verifyRlsEnforced(supabase: TypedSupabaseClient): Promise<void> {\n const { count, error } = await supabase\n .from('companies')\n .select('*', { count: 'exact', head: true });\n\n if (error) {\n // If the query fails, RLS is likely working (e.g., no auth)\n logger.warn('RLS check query failed (expected if no data)', { error: error.message });\n return;\n }\n\n // A single user should have at most a handful of companies.\n // If we see > 100, RLS may be disabled and we're seeing all rows.\n if (count != null && count > 100) {\n logger.error('RLS CHECK FAILED: companies query returned too many rows', {\n count,\n warning: 'RLS may be disabled — investigate immediately',\n });\n throw new Error(\n `RLS health check failed: companies table returned ${count} rows. ` +\n 'This suggests Row Level Security may be disabled. ' +\n 'Check Supabase dashboard → Authentication → Policies.',\n );\n }\n\n logger.info('RLS health check passed', { companiesVisible: count ?? 0 });\n}\n","/**\n * utils/static-data.ts — Singleton client for loading static content from Supabase.\n *\n * Proprietary data (compliance rules, templates, filing guides, etc.) is stored\n * in the static_content table rather than bundled in the npm package.\n */\n\nimport type { TypedSupabaseClient } from '../db/client.js';\nimport { readFile, readdir } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nlet client: TypedSupabaseClient | null = null;\nconst LOCAL_DATA_DIR = join(dirname(fileURLToPath(import.meta.url)), '../../../../data');\n\nfunction isLocalFallbackEnabled(): boolean {\n const localDev = process.env.FORCEFIELD_LOCAL_DEV;\n if (localDev && ['1', 'true', 'yes', 'on'].includes(localDev.trim().toLowerCase())) {\n return true;\n }\n\n const value = process.env.FORCEFIELD_LOCAL_DATA_FALLBACK;\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';\n}\n\nasync function loadFromLocalFile(path: string): Promise<string> {\n const fullPath = join(LOCAL_DATA_DIR, path);\n return readFile(fullPath, 'utf-8');\n}\n\nasync function loadLocalCategory(\n category: string,\n): Promise<Array<{ path: string; content: string }>> {\n if (category !== 'template_preview') return [];\n\n const previewsDir = join(LOCAL_DATA_DIR, 'templates', 'previews');\n const entries = await readdir(previewsDir, { withFileTypes: true });\n const files = entries\n .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n const results: Array<{ path: string; content: string }> = [];\n for (const entry of files) {\n const path = `templates/previews/${entry.name}`;\n const content = await readFile(join(previewsDir, entry.name), 'utf-8');\n results.push({ path, content });\n }\n return results;\n}\n\n/**\n * Initialize the static data loader with an authenticated Supabase client.\n * Must be called once at startup before any loader functions.\n */\nexport function initStaticData(supabase: TypedSupabaseClient): void {\n client = supabase;\n}\n\n/**\n * Load a single static file by its path (e.g., 'compliance/_index.json').\n */\nexport async function loadStaticFile(path: string): Promise<string> {\n if (client) {\n const { data, error } = await client\n .from('static_content')\n .select('content')\n .eq('path', path)\n .single();\n if (!error && data?.content) {\n return data.content;\n }\n if (!isLocalFallbackEnabled()) {\n throw new Error(`Failed to load \"${path}\": ${error?.message ?? 'no content returned'}`);\n }\n } else if (!isLocalFallbackEnabled()) {\n throw new Error('Static data client not initialized.');\n }\n\n try {\n return await loadFromLocalFile(path);\n } catch (err) {\n throw new Error(\n `Failed to load \"${path}\" from local fallback: ${\n err instanceof Error ? err.message : 'unknown error'\n }`,\n );\n }\n}\n\n/**\n * Load all static files for a given category.\n */\nexport async function loadStaticFilesByCategory(\n category: string,\n): Promise<Array<{ path: string; content: string }>> {\n if (client) {\n const { data, error } = await client\n .from('static_content')\n .select('path, content')\n .eq('category', category);\n if (!error) {\n return data ?? [];\n }\n if (!isLocalFallbackEnabled()) {\n throw new Error(`Failed to load category \"${category}\": ${error.message}`);\n }\n } else if (!isLocalFallbackEnabled()) {\n throw new Error('Static data client not initialized.');\n }\n\n try {\n return await loadLocalCategory(category);\n } catch (err) {\n throw new Error(\n `Failed to load category \"${category}\" from local fallback: ${\n err instanceof Error ? err.message : 'unknown error'\n }`,\n );\n }\n}\n","/**\n * server.ts — MCP server registration.\n *\n * Registers all 8 domain tools and workflow resources.\n * All tools have at least one active action:\n * ff_company, ff_scan, ff_compliance, ff_system, ff_vault, ff_generate, ff_equity, ff_integrate.\n * Workflow resources: forcefield://workflow/* — loads markdown from workflows/ dir.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type { ServerRequest, ServerNotification } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport type { SessionContext } from './auth/session.js';\n\n/** MCP request handler extra context — provides sendNotification, progressToken, etc. */\nexport type McpExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;\nimport { handleFfCompany } from './tools/ff-company.js';\nimport { handleFfScan } from './tools/ff-scan.js';\nimport { handleFfCompliance } from './tools/ff-compliance.js';\nimport { handleFfSystem } from './tools/ff-system.js';\nimport { handleFfVault } from './tools/ff-vault.js';\nimport { handleFfGenerate } from './tools/ff-generate.js';\nimport { handleFfEquity } from './tools/ff-equity.js';\nimport { handleFfIntegrate } from './tools/ff-integrate.js';\nimport { validateAndStoreWarnings } from './domain/templates/template-loader.js';\nimport { logger } from './utils/logger.js';\n\nconst WORKFLOWS_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'workflows');\n\nconst SERVER_NAME = 'forcefield';\nconst SERVER_VERSION = '0.0.0';\n\n/** Workflow names served as MCP resources. */\nconst WORKFLOW_NAMES = [\n 'ff-start',\n 'ff-onboard',\n 'ff-help',\n 'ff-status',\n 'ff-gap-check',\n 'ff-remediate',\n 'ff-file',\n 'ff-doc-gen',\n 'ff-cap-table',\n] as const;\n\n/**\n * Create and configure the MCP server with all tools and resources.\n * Does NOT connect — caller must call `server.connect(transport)`.\n */\nexport function createMcpServer(ctx: SessionContext): McpServer {\n const server = new McpServer({\n name: SERVER_NAME,\n version: SERVER_VERSION,\n });\n\n registerTools(server, ctx);\n registerResources(server);\n\n // Fire-and-forget startup validation — don't delay server startup\n void validateAndStoreWarnings();\n\n return server;\n}\n\n// === Tool Registration ===\n\nfunction registerTools(server: McpServer, ctx: SessionContext): void {\n const passthroughInput = z.object({}).passthrough();\n\n // --- Implemented tools ---\n\n server.registerTool(\n 'ff_company',\n {\n title: 'Company Management',\n inputSchema: passthroughInput,\n description: 'Create, read, update, and manage company profiles. Actions: create, get, list, update, add_note, completeness.',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfCompany(ctx, args, extra),\n );\n\n server.registerTool(\n 'ff_scan',\n {\n title: 'Document Scanner',\n inputSchema: passthroughInput,\n description: 'Scan local files and folders to extract text and metadata. Actions: file, folder.',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfScan(ctx, args, extra),\n );\n\n server.registerTool(\n 'ff_compliance',\n {\n title: 'Compliance Engine',\n inputSchema: passthroughInput,\n description: 'Compliance engine — generates deadlines, scores, and filing guidance. All responses include disclaimers: this is informational guidance, not legal advice. Actions: generate_deadlines, score, guide, list_deadlines, complete, reopen, override, calculate_tax, history, template_links.',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfCompliance(ctx, args, extra),\n );\n\n server.registerTool(\n 'ff_system',\n {\n title: 'System Diagnostics',\n inputSchema: passthroughInput,\n description: 'Health checks, metrics, audit log access, and account management. Actions: health, get_metrics, audit_log, export_all, delete_account.',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfSystem(ctx, args, extra),\n );\n\n server.registerTool(\n 'ff_vault',\n {\n title: 'Document Vault',\n inputSchema: passthroughInput,\n description: 'Upload, search, and manage corporate documents. Actions: upload, get, metadata, search, list, delete, store_sensitive, get_sensitive, save_generated (all active).',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfVault(ctx, args, extra),\n );\n\n // --- Partially implemented tools ---\n\n server.registerTool(\n 'ff_generate',\n {\n title: 'Document Generator',\n inputSchema: passthroughInput,\n description: 'Generate corporate documents from templates. Actions: list_templates, preview, start_interview, answer_interview, generate (all active).',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfGenerate(ctx, args, extra),\n );\n\n server.registerTool(\n 'ff_equity',\n {\n title: 'Cap Table & Equity',\n inputSchema: passthroughInput,\n description: 'Manage share classes, equity grants, ownership, vesting, waterfalls, round modeling, document import, and cap table export. Actions: create_class, create_plan, list_classes, add_stakeholder, add_grant, update_grant, cancel_grant, exercise_grant, convert_grant, ownership, vesting, waterfall, model_round, import, export (all active).',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfEquity(ctx, args, extra),\n );\n\n server.registerTool(\n 'ff_integrate',\n {\n title: 'Cloud Integrations',\n inputSchema: passthroughInput,\n description: 'Cloud integrations and notification preferences. Actions: scan_drive, scan_email, scan_notion, push_calendar, set_notifications (all active).',\n },\n async (args: Record<string, unknown>, extra: McpExtra) => handleFfIntegrate(ctx, args, extra),\n );\n\n logger.info('Registered 8 MCP tools (8 active, 0 stubs)');\n}\n\n// === Resource Registration ===\n\nfunction registerResources(server: McpServer): void {\n for (const name of WORKFLOW_NAMES) {\n const uri = `forcefield://workflow/${name}`;\n\n server.registerResource(\n name,\n uri,\n {\n title: `${name} workflow`,\n description: `Guided workflow: ${name}. Install slash commands for the full experience.`,\n mimeType: 'text/markdown',\n },\n async () => ({\n contents: [{\n uri,\n text: await loadWorkflow(name),\n }],\n }),\n );\n }\n\n logger.info(`Registered ${WORKFLOW_NAMES.length} workflow resources`);\n}\n\n/**\n * Load workflow markdown from workflows/ directory.\n * Falls back to placeholder if file doesn't exist yet.\n */\nasync function loadWorkflow(name: string): Promise<string> {\n const filePath = join(WORKFLOWS_DIR, `${name}.md`);\n try {\n return await readFile(filePath, 'utf-8');\n } catch {\n return getWorkflowPlaceholder(name);\n }\n}\n\nfunction getWorkflowPlaceholder(name: string): string {\n return [\n `# ${name}`,\n '',\n 'This workflow is not yet available. It will be added in a future update.',\n '',\n 'Install/configure Forcefield in this repo with the setup wizard:',\n '```',\n 'forcefield-setup',\n '```',\n '',\n 'Available workflows:',\n ...WORKFLOW_NAMES.map((w) => `- ${w}`),\n ].join('\\n');\n}\n\n/** Exported for testing. */\nexport { loadWorkflow };\n\nexport { SERVER_NAME, SERVER_VERSION, WORKFLOW_NAMES };\n","import { z } from 'zod';\nimport { CompanyIdSchema, EntityTypeSchema, PaginationSchema } from './common.js';\n\nconst CompanyCreateSchema = z.object({\n action: z.literal('create'),\n name: z.string().min(1),\n entityType: EntityTypeSchema,\n stateOfFormation: z.string().length(2),\n formationDate: z.string().date().optional(),\n timezone: z.string().optional().describe('IANA timezone string'),\n});\n\nconst CompanyUpdateSchema = z.object({\n action: z.literal('update'),\n companyId: CompanyIdSchema,\n name: z.string().min(1).optional(),\n entityType: EntityTypeSchema.optional(),\n stateOfFormation: z.string().length(2).optional(),\n formationDate: z.string().date().optional(),\n hasEmployees: z.boolean().optional(),\n hasPayroll: z.boolean().optional(),\n fiscalYearEnd: z.string().date().optional(),\n timezone: z.string().optional(),\n registeredAgentName: z.string().optional(),\n registeredAgentAddress: z.record(z.string(), z.string()).optional(),\n officers: z\n .array(\n z.object({\n name: z.string(),\n title: z.string(),\n email: z.string().email().optional(),\n }),\n )\n .optional(),\n foreignRegistrations: z\n .array(\n z.object({\n state: z.string().length(2),\n registrationDate: z.string().date().optional(),\n endDate: z.string().date().optional(),\n status: z.string().optional(),\n }),\n )\n .optional(),\n estimatedTaxCadence: z.enum(['annual', 'quarterly']).optional(),\n usesContractors: z.boolean().optional(),\n dataPrivacyProgramRequired: z.boolean().optional(),\n ipGovernanceRequired: z.boolean().optional(),\n formationDocumentId: z.string().uuid().nullable().optional(),\n});\n\nconst CompanyGetSchema = z.object({\n action: z.literal('get'),\n companyId: CompanyIdSchema,\n showSensitive: z.boolean().default(false),\n});\n\nconst CompanyListSchema = z.object({\n action: z.literal('list'),\n ...PaginationSchema.shape,\n});\n\nconst CompanyAddNoteSchema = z.object({\n action: z.literal('add_note'),\n companyId: CompanyIdSchema,\n note: z.string().min(1),\n});\n\nconst CompanyEntityStatusSchema = z.object({\n action: z.literal('cross_entity_status'),\n});\n\nexport const FfCompanySchema = z.discriminatedUnion('action', [\n CompanyCreateSchema,\n CompanyUpdateSchema,\n CompanyGetSchema,\n CompanyListSchema,\n CompanyAddNoteSchema,\n CompanyEntityStatusSchema,\n]);\n\nexport type FfCompanyInput = z.infer<typeof FfCompanySchema>;\n","import { z } from 'zod';\n\n/** Reusable field schemas shared across tool inputs. */\n\nexport const CompanyIdSchema = z.string().uuid().describe('Company UUID');\n\nexport const EntityTypeSchema = z.enum(['C-Corp', 'S-Corp', 'LLC', 'LP', 'GP']);\n\nexport const CoverageTierSchema = z.enum(['full', 'basic']);\n\nexport const TierSchema = z.enum(['free', 'paid']);\n\nexport const DeadlineStatusSchema = z.enum([\n 'pending',\n 'completed',\n 'overdue',\n 'suppressed',\n]);\n\nexport const CategorySchema = z.enum([\n 'state_annual_report',\n 'state_franchise_tax',\n 'state_payroll',\n 'state_registration',\n 'state_publication',\n 'federal_income_tax',\n 'federal_payroll',\n 'federal_information_return',\n 'federal_other',\n 'governance',\n 'intellectual_property',\n 'data_privacy',\n]);\n\nexport const InstrumentTypeSchema = z.enum([\n 'common_stock',\n 'preferred_stock',\n 'safe',\n 'convertible_note',\n 'warrant',\n 'option_iso',\n 'option_nso',\n 'rsu',\n 'membership_interest',\n 'profits_interest',\n]);\n\nexport const SeveritySchema = z.enum(['critical', 'high', 'medium', 'low']);\n\n/** Pagination parameters — standard contract across all paginated endpoints. */\nexport const PaginationSchema = z.object({\n limit: z.number().int().min(1).max(100).default(25).describe('Max results per page'),\n offset: z.number().int().min(0).default(0).describe('Number of results to skip'),\n});\n\nexport type Pagination = z.infer<typeof PaginationSchema>;\n","import { z } from 'zod';\nimport { CompanyIdSchema } from './common.js';\n\nconst ScanFileSchema = z.object({\n action: z.literal('file'),\n companyId: CompanyIdSchema,\n filePath: z.string().min(1).describe('Absolute path to local file'),\n});\n\nconst ScanFolderSchema = z.object({\n action: z.literal('folder'),\n companyId: CompanyIdSchema,\n folderPath: z.string().min(1).describe('Absolute path to local folder'),\n recursive: z.boolean().default(true),\n});\n\nexport const FfScanSchema = z.discriminatedUnion('action', [\n ScanFileSchema,\n ScanFolderSchema,\n]);\n\nexport type FfScanInput = z.infer<typeof FfScanSchema>;\n","import { z } from 'zod';\nimport { CompanyIdSchema, PaginationSchema, SeveritySchema, CategorySchema } from './common.js';\n\nconst ComplianceGenerateDeadlinesSchema = z.object({\n action: z.literal('generate_deadlines'),\n companyId: CompanyIdSchema,\n forceRegenerate: z.boolean().default(false),\n});\n\nconst ComplianceScoreSchema = z.object({\n action: z.literal('score'),\n companyId: CompanyIdSchema,\n});\n\nconst ComplianceListDeadlinesSchema = z.object({\n action: z.literal('list_deadlines'),\n companyId: CompanyIdSchema,\n status: z.enum(['pending', 'completed', 'overdue', 'suppressed', 'all']).default('all'),\n category: CategorySchema.optional(),\n severity: SeveritySchema.optional(),\n ...PaginationSchema.shape,\n});\n\nconst ComplianceCompleteSchema = z.object({\n action: z.literal('complete'),\n deadlineId: z.string().uuid(),\n filingDate: z.string().date(),\n confirmationNumber: z.string().optional(),\n amountPaid: z.number().min(0).optional(),\n documentId: z.string().uuid().optional(),\n notes: z.string().optional(),\n});\n\nconst ComplianceReopenSchema = z.object({\n action: z.literal('reopen'),\n deadlineId: z.string().uuid(),\n reason: z.string().min(1),\n});\n\nconst ComplianceOverrideSchema = z.object({\n action: z.literal('override'),\n companyId: CompanyIdSchema,\n ruleId: z.string(),\n applicable: z.boolean(),\n reason: z.string().min(1),\n});\n\nconst ComplianceCalculateTaxSchema = z.object({\n action: z.literal('calculate_tax'),\n companyId: CompanyIdSchema,\n ruleId: z.string(),\n inputs: z.record(z.string(), z.unknown()).optional().describe('Calculator-specific inputs'),\n});\n\nconst ComplianceHistorySchema = z.object({\n action: z.literal('history'),\n companyId: CompanyIdSchema,\n ...PaginationSchema.shape,\n});\n\nconst ComplianceGuideSchema = z.object({\n action: z.literal('guide'),\n ruleId: z.string(),\n guideType: z.enum(['filing', 'remediation']).default('filing'),\n});\n\nconst ComplianceTemplateLinksSchema = z.object({\n action: z.literal('template_links'),\n category: CategorySchema.optional(),\n ruleId: z.string().optional(),\n});\n\nexport const FfComplianceSchema = z.discriminatedUnion('action', [\n ComplianceGenerateDeadlinesSchema,\n ComplianceScoreSchema,\n ComplianceListDeadlinesSchema,\n ComplianceCompleteSchema,\n ComplianceReopenSchema,\n ComplianceOverrideSchema,\n ComplianceCalculateTaxSchema,\n ComplianceHistorySchema,\n ComplianceGuideSchema,\n ComplianceTemplateLinksSchema,\n]);\n\nexport type FfComplianceInput = z.infer<typeof FfComplianceSchema>;\n","import { z } from 'zod';\nimport { CompanyIdSchema, PaginationSchema } from './common.js';\n\nconst VaultUploadSchema = z.object({\n action: z.literal('upload'),\n companyId: CompanyIdSchema,\n filePath: z.string().min(1),\n documentType: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n});\n\nconst VaultGetSchema = z.object({\n action: z.literal('get'),\n documentId: z.string().uuid(),\n});\n\nconst VaultMetadataSchema = z.object({\n action: z.literal('metadata'),\n documentId: z.string().uuid(),\n});\n\nconst VaultSearchSchema = z.object({\n action: z.literal('search'),\n companyId: CompanyIdSchema,\n query: z.string().min(1),\n limit: z.number().int().min(1).max(100).default(10),\n offset: z.number().int().min(0).default(0),\n});\n\nconst VaultListSchema = z.object({\n action: z.literal('list'),\n companyId: CompanyIdSchema,\n documentClass: z.string().optional(),\n ...PaginationSchema.shape,\n});\n\nconst VaultDeleteSchema = z.object({\n action: z.literal('delete'),\n documentId: z.string().uuid(),\n});\n\nconst VaultStoreSensitiveSchema = z.object({\n action: z.literal('store_sensitive'),\n companyId: CompanyIdSchema,\n dataType: z.enum(['ein', 'entity_number', 'ssn', 'investor_ein', 'tax_id']),\n label: z.string().min(1),\n value: z.string().min(1),\n});\n\nconst VaultGetSensitiveSchema = z.object({\n action: z.literal('get_sensitive'),\n companyId: CompanyIdSchema,\n dataType: z.enum(['ein', 'entity_number', 'ssn', 'investor_ein', 'tax_id']).optional(),\n showSensitive: z.boolean().default(false),\n});\n\nconst VaultSaveGeneratedSchema = z.object({\n action: z.literal('save_generated'),\n companyId: CompanyIdSchema,\n documentId: z.string().uuid(),\n});\n\nexport const FfVaultSchema = z.discriminatedUnion('action', [\n VaultUploadSchema,\n VaultGetSchema,\n VaultMetadataSchema,\n VaultSearchSchema,\n VaultListSchema,\n VaultDeleteSchema,\n VaultStoreSensitiveSchema,\n VaultGetSensitiveSchema,\n VaultSaveGeneratedSchema,\n]);\n\nexport type FfVaultInput = z.infer<typeof FfVaultSchema>;\n","import { z } from 'zod';\nimport { CompanyIdSchema } from './common.js';\n\nconst GenerateListTemplatesSchema = z.object({\n action: z.literal('list_templates'),\n category: z.string().optional(),\n});\n\nconst GeneratePreviewSchema = z.object({\n action: z.literal('preview'),\n templateId: z.string().min(1),\n});\n\nconst GenerateStartInterviewSchema = z.object({\n action: z.literal('start_interview'),\n companyId: CompanyIdSchema,\n templateId: z.string().min(1),\n deadlineId: z.string().uuid().optional(),\n});\n\nconst GenerateAnswerInterviewSchema = z.object({\n action: z.literal('answer_interview'),\n companyId: CompanyIdSchema,\n sessionId: z.string().uuid(),\n answers: z.record(z.string(), z.unknown()),\n});\n\nconst GenerateGenerateSchema = z.object({\n action: z.literal('generate'),\n companyId: CompanyIdSchema,\n templateId: z.string().min(1),\n sessionId: z.string().uuid().optional(),\n variables: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const FfGenerateSchema = z.discriminatedUnion('action', [\n GenerateListTemplatesSchema,\n GeneratePreviewSchema,\n GenerateStartInterviewSchema,\n GenerateAnswerInterviewSchema,\n GenerateGenerateSchema,\n]);\n\nexport type FfGenerateInput = z.infer<typeof FfGenerateSchema>;\n","import { z } from 'zod';\nimport { CompanyIdSchema, InstrumentTypeSchema, PaginationSchema } from './common.js';\n\nexport const AntiDilutionTypeSchema = z.enum([\n 'none',\n 'broad_based_weighted_average',\n 'narrow_based_weighted_average',\n 'full_ratchet',\n]);\n\nconst EquityCreateClassSchema = z.object({\n action: z.literal('create_class'),\n companyId: CompanyIdSchema,\n name: z.string().min(1),\n classType: z.enum(['common', 'preferred']),\n authorizedShares: z.number().int().positive(),\n parValue: z.number().min(0).nullable().optional(),\n pricePerShare: z.number().min(0).optional(),\n votesPerShare: z.number().int().default(1),\n series: z.string().optional(),\n seniority: z.number().int().min(0).default(0),\n liquidationPreference: z.number().min(0).nullable().optional(),\n participationCap: z.number().min(0).nullable().optional(),\n conversionRights: z.string().nullable().optional(),\n antiDilutionType: AntiDilutionTypeSchema.default('none'),\n});\n\nconst EquityCreatePlanSchema = z.object({\n action: z.literal('create_plan'),\n companyId: CompanyIdSchema,\n name: z.string().min(1),\n shareClassId: z.string().uuid(),\n initialSharesReserved: z.number().int().positive(),\n boardApprovalDate: z.string().date().optional(),\n cancellationBehavior: z.enum(['return_to_pool', 'retire']).default('return_to_pool'),\n});\n\nconst EquityListClassesSchema = z.object({\n action: z.literal('list_classes'),\n companyId: CompanyIdSchema,\n ...PaginationSchema.shape,\n});\n\nconst EquityAddStakeholderSchema = z.object({\n action: z.literal('add_stakeholder'),\n companyId: CompanyIdSchema,\n name: z.string().min(1),\n email: z.string().email().optional(),\n type: z.enum(['founder', 'investor', 'employee', 'advisor', 'contractor']),\n stakeholderType: z.enum(['individual', 'institution']).default('individual'),\n institutionName: z.string().optional(),\n relationshipStartDate: z.string().date().optional(),\n});\n\nconst EquityAddGrantSchema = z.object({\n action: z.literal('add_grant'),\n companyId: CompanyIdSchema,\n stakeholderId: z.string().uuid(),\n instrumentType: InstrumentTypeSchema,\n shareClassId: z.string().uuid().optional(),\n equityPlanId: z.string().uuid().optional(),\n shares: z.number().optional(),\n amount: z.number().optional(),\n pricePerShare: z.number().optional(),\n valuationCap: z.number().optional(),\n discountRate: z.number().optional(),\n interestRate: z.number().optional(),\n grantDate: z.string().date(),\n boardApprovalDate: z.string().date().optional(),\n vestingMonths: z.number().int().optional(),\n cliffMonths: z.number().int().optional(),\n vestingStartDate: z.string().date().optional(),\n});\n\nconst EquityUpdateGrantSchema = z.object({\n action: z.literal('update_grant'),\n companyId: CompanyIdSchema,\n grantId: z.string().uuid(),\n election83bFiled: z.boolean().optional(),\n election83bDate: z.string().date().optional(),\n notes: z.string().optional(),\n boardApprovalDate: z.string().date().optional(),\n});\n\nconst EquityCancelGrantSchema = z.object({\n action: z.literal('cancel_grant'),\n companyId: CompanyIdSchema,\n grantId: z.string().uuid(),\n reason: z.string().optional(),\n});\n\nconst EquityExerciseGrantSchema = z.object({\n action: z.literal('exercise_grant'),\n companyId: CompanyIdSchema,\n grantId: z.string().uuid(),\n sharesToExercise: z.number().positive(),\n});\n\nconst EquityConvertGrantSchema = z.object({\n action: z.literal('convert_grant'),\n companyId: CompanyIdSchema,\n grantId: z.string().uuid(),\n shareClassId: z.string().uuid(),\n pricePerShare: z.number().positive(),\n});\n\nconst EquityOwnershipSchema = z.object({\n action: z.literal('ownership'),\n companyId: CompanyIdSchema,\n asOfDate: z.string().date().optional(),\n includeOptions: z.boolean().default(true),\n includeSafes: z.boolean().default(true),\n includeWarrants: z.boolean().default(true),\n});\n\nconst EquityVestingSchema = z.object({\n action: z.literal('vesting'),\n companyId: CompanyIdSchema,\n grantId: z.string().uuid(),\n asOfDate: z.string().date().optional(),\n});\n\nconst EquityWaterfallSchema = z.object({\n action: z.literal('waterfall'),\n companyId: CompanyIdSchema,\n exitAmount: z.number().positive(),\n includeOptions: z.boolean().default(false),\n});\n\nconst EquityModelRoundSchema = z.object({\n action: z.literal('model_round'),\n companyId: CompanyIdSchema,\n investmentAmount: z.number().positive(),\n preMoneyValuation: z.number().positive(),\n newShareClassName: z.string().optional(),\n newOptionPoolPercent: z.number().min(0).max(100).optional(),\n convertSafes: z.boolean().default(true),\n convertNotes: z.boolean().default(true),\n roundName: z.string().optional(),\n});\n\nconst EquityImportSchema = z.object({\n action: z.literal('import'),\n companyId: CompanyIdSchema,\n /** Vault document ID — fetch extractedText from vault. */\n documentId: z.string().uuid().optional(),\n /** Direct text input (e.g., from ff_scan output). */\n extractedText: z.string().min(1).optional(),\n /** Hint to focus regex patterns on a specific instrument type. */\n instrumentHint: z.enum(['safe', 'convertible_note', 'common_stock', 'preferred_stock', 'option', 'warrant']).optional(),\n});\n\nconst EquityExportSchema = z.object({\n action: z.literal('export'),\n companyId: CompanyIdSchema,\n format: z.enum(['csv', 'xlsx', 'markdown', 'json']).default('csv'),\n outputPath: z.string().optional(),\n});\n\nexport const FfEquitySchema = z.discriminatedUnion('action', [\n EquityCreateClassSchema,\n EquityCreatePlanSchema,\n EquityListClassesSchema,\n EquityAddStakeholderSchema,\n EquityAddGrantSchema,\n EquityUpdateGrantSchema,\n EquityCancelGrantSchema,\n EquityExerciseGrantSchema,\n EquityConvertGrantSchema,\n EquityOwnershipSchema,\n EquityVestingSchema,\n EquityWaterfallSchema,\n EquityModelRoundSchema,\n EquityImportSchema,\n EquityExportSchema,\n]);\n\nexport type FfEquityInput = z.infer<typeof FfEquitySchema>;\n","import { z } from 'zod';\nimport { CompanyIdSchema, CategorySchema } from './common.js';\n\nconst IntegrateScanDriveSchema = z.object({\n action: z.literal('scan_drive'),\n companyId: CompanyIdSchema,\n folderId: z.string().optional(),\n query: z.string().optional().describe('Drive search query'),\n maxResults: z.number().int().min(1).max(50).default(20),\n});\n\nconst IntegrateScanEmailSchema = z.object({\n action: z.literal('scan_email'),\n companyId: CompanyIdSchema,\n query: z.string().optional().describe('Gmail search query'),\n maxResults: z.number().int().min(1).max(50).default(10),\n});\n\nconst IntegrateScanNotionSchema = z.object({\n action: z.literal('scan_notion'),\n companyId: CompanyIdSchema,\n pageId: z.string().optional().describe('Fetch a specific Notion page by ID'),\n databaseId: z.string().optional().describe('Query a specific Notion database'),\n query: z.string().optional().describe('Search query for workspace-wide search'),\n maxResults: z.number().int().min(1).max(50).default(10),\n});\n\nconst IntegratePushCalendarSchema = z.object({\n action: z.literal('push_calendar'),\n companyId: CompanyIdSchema,\n deadlineIds: z.array(z.string().uuid()).optional().describe('Specific deadlines, or all if omitted'),\n});\n\nconst IntegrateSetNotificationsSchema = z.object({\n action: z.literal('set_notifications'),\n enabled: z.boolean(),\n frequency: z.enum(['daily', 'weekly', 'monthly']).default('weekly'),\n mode: z.enum(['minimal', 'digest']).default('minimal'),\n emailOverride: z.string().email().optional(),\n disabledCategories: z.array(CategorySchema).default([]),\n webhookUrl: z.string().url().optional(),\n});\n\nexport const FfIntegrateSchema = z.discriminatedUnion('action', [\n IntegrateScanDriveSchema,\n IntegrateScanEmailSchema,\n IntegrateScanNotionSchema,\n IntegratePushCalendarSchema,\n IntegrateSetNotificationsSchema,\n]);\n\nexport type FfIntegrateInput = z.infer<typeof FfIntegrateSchema>;\n","import { z } from 'zod';\n\nconst SystemHealthSchema = z.object({\n action: z.literal('health'),\n});\n\nconst SystemAuditLogSchema = z.object({\n action: z.literal('audit_log'),\n toolName: z.string().optional().describe('Filter by tool name'),\n companyId: z.string().uuid().optional().describe('Filter by company ID'),\n startDate: z.string().date().optional().describe('Start date (YYYY-MM-DD)'),\n endDate: z.string().date().optional().describe('End date (YYYY-MM-DD)'),\n limit: z.number().int().min(1).max(100).default(25),\n offset: z.number().int().min(0).default(0),\n});\n\nconst SystemExportAllSchema = z.object({\n action: z.literal('export_all'),\n format: z.enum(['json', 'zip']).default('json'),\n});\n\nconst SystemDeleteAccountSchema = z.object({\n action: z.literal('delete_account'),\n confirmPhrase: z.literal('DELETE MY ACCOUNT'),\n});\n\nconst SystemGetMetricsSchema = z.object({\n action: z.literal('get_metrics'),\n});\n\nexport const FfSystemSchema = z.discriminatedUnion('action', [\n SystemHealthSchema,\n SystemAuditLogSchema,\n SystemExportAllSchema,\n SystemDeleteAccountSchema,\n SystemGetMetricsSchema,\n]);\n\nexport type FfSystemInput = z.infer<typeof FfSystemSchema>;\n","/**\n * errors/catalog.ts — Error factory for domain + tool handlers.\n *\n * Domain functions throw FfError. Tool handlers catch and format\n * via formatToolResponse(). Unexpected errors logged + returned as generic message.\n */\n\nexport type ErrorCode =\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'VALIDATION_FAILED'\n | 'TIER_RESTRICTED'\n | 'SERVICE_UNAVAILABLE'\n | 'SCAN_FAILED'\n | 'UPLOAD_FAILED'\n | 'FILE_TOO_LARGE'\n | 'NOT_IMPLEMENTED'\n | 'CONFLICT'\n | 'INTERNAL_ERROR';\n\nexport class FfError extends Error {\n readonly code: ErrorCode;\n readonly suggestion?: string;\n readonly statusCode: number;\n\n constructor(code: ErrorCode, message: string, statusCode: number, suggestion?: string) {\n super(message);\n this.name = 'FfError';\n this.code = code;\n this.statusCode = statusCode;\n this.suggestion = suggestion;\n }\n}\n\nexport const ffError = {\n notFound(resource: string, id?: string): FfError {\n const msg = id ? `${resource} \"${id}\" not found.` : `${resource} not found.`;\n return new FfError('NOT_FOUND', msg, 404, `Check the ${resource} ID and try again.`);\n },\n\n unauthorized(): FfError {\n return new FfError(\n 'UNAUTHORIZED',\n 'Authentication required. Check your API key.',\n 401,\n 'Run the Forcefield setup wizard to configure your API key.',\n );\n },\n\n forbidden(resource?: string): FfError {\n const msg = resource\n ? `You don't have access to this ${resource}.`\n : 'You don\\'t have access to this resource.';\n return new FfError('FORBIDDEN', msg, 403);\n },\n\n validationFailed(issues: Array<{ path: string; message: string }>): FfError {\n const detail = issues.map((i) => `${i.path}: ${i.message}`).join('; ');\n return new FfError('VALIDATION_FAILED', `Invalid input: ${detail}`, 400);\n },\n\n tierRestricted(tool: string, action: string): FfError {\n return new FfError(\n 'TIER_RESTRICTED',\n `${tool}/${action} requires a paid subscription.`,\n 402,\n 'Upgrade at https://app.forcefield.dev/settings to unlock this feature.',\n );\n },\n\n serviceUnavailable(service: string): FfError {\n return new FfError(\n 'SERVICE_UNAVAILABLE',\n `${service} is temporarily unavailable. Try again in a moment.`,\n 503,\n );\n },\n\n scanFailed(path: string, reason: string): FfError {\n return new FfError(\n 'SCAN_FAILED',\n `Failed to scan \"${path}\": ${reason}`,\n 422,\n 'Check the file path and format, then try again.',\n );\n },\n\n uploadFailed(fileName: string, reason: string): FfError {\n return new FfError(\n 'UPLOAD_FAILED',\n `Failed to upload \"${fileName}\": ${reason}`,\n 422,\n 'Check the file and try again.',\n );\n },\n\n fileTooLarge(fileSize: number): FfError {\n const sizeMb = (fileSize / (1024 * 1024)).toFixed(1);\n return new FfError(\n 'FILE_TOO_LARGE',\n `File too large (${sizeMb}MB). 25MB max. Split or compress before uploading.`,\n 413,\n );\n },\n\n notImplemented(feature: string): FfError {\n return new FfError(\n 'NOT_IMPLEMENTED',\n `${feature} is not available yet. Coming in a future update.`,\n 501,\n 'Check https://forcefield.dev/changelog for release updates.',\n );\n },\n\n conflict(resource: string, detail: string): FfError {\n return new FfError('CONFLICT', `${resource} conflict: ${detail}`, 409);\n },\n\n internal(detail?: string): FfError {\n const msg = detail\n ? `An internal error occurred: ${detail}`\n : 'An internal error occurred. Please try again.';\n return new FfError('INTERNAL_ERROR', msg, 500);\n },\n} as const;\n","/**\n * auth/ownership.ts — Company ownership validation.\n *\n * Defense in depth: RLS enforces user_id at the DB level, but tool handlers\n * also call validateCompanyOwnership() at the application layer.\n * Returns the company row if valid — eliminates double DB fetch.\n */\n\nimport type { TypedSupabaseClient } from '../db/client.js';\nimport { ffError } from '../errors/catalog.js';\n\nexport interface CompanyRow {\n id: string;\n userId: string;\n name: string;\n entityType: string;\n stateOfFormation: string;\n status: string;\n}\n\n/**\n * Validate that the current user owns the given company.\n * Returns the company row (camelCase) if valid.\n * Throws FfError (NOT_FOUND) if company doesn't exist or user doesn't own it.\n *\n * Note: RLS already filters by user_id, so a missing row means either\n * the company doesn't exist OR the user doesn't own it. We return the\n * same error for both (no information leakage).\n */\nexport async function validateCompanyOwnership(\n supabase: TypedSupabaseClient,\n companyId: string,\n _userId: string,\n): Promise<CompanyRow> {\n const { data, error } = await supabase\n .from('companies')\n .select('id, user_id, name, entity_type, state_of_formation, status')\n .eq('id', companyId)\n .maybeSingle();\n\n if (error) {\n throw ffError.serviceUnavailable('Database');\n }\n\n if (!data) {\n throw ffError.notFound('company', companyId);\n }\n\n // Map snake_case DB columns to camelCase\n return {\n id: data.id,\n userId: data.user_id,\n name: data.name,\n entityType: data.entity_type,\n stateOfFormation: data.state_of_formation,\n status: data.status,\n };\n}\n","/**\n * utils/pii-masker.ts — PII masking for tool responses.\n *\n * Default on. Applied by formatToolResponse() before returning to Claude.\n * `showSensitive: true` bypasses masking (audit logged separately).\n */\n\n/** SSN: 123-45-6789 → ***-**-**** */\nconst SSN_PATTERN = /\\b\\d{3}-\\d{2}-\\d{4}\\b/g;\nconst SSN_REPLACEMENT = '***-**-****';\n\n/** EIN: 12-3456789 → **-******* */\nconst EIN_PATTERN = /\\b\\d{2}-\\d{7}\\b/g;\nconst EIN_REPLACEMENT = '**-*******';\n\n/** Bare 9-digit: 123456789 → ********* */\nconst NINE_DIGIT_PATTERN = /\\b\\d{9}\\b/g;\nconst NINE_DIGIT_REPLACEMENT = '*********';\n\n/**\n * Mask PII patterns in a string.\n */\nexport function maskString(value: string): string {\n return value\n .replace(SSN_PATTERN, SSN_REPLACEMENT)\n .replace(EIN_PATTERN, EIN_REPLACEMENT)\n .replace(NINE_DIGIT_PATTERN, NINE_DIGIT_REPLACEMENT);\n}\n\n/**\n * Deep-mask PII in an object, array, or primitive.\n * Returns a new value — never mutates the input.\n */\nexport function maskPii<T>(value: T): T {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (typeof value === 'string') {\n return maskString(value) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => maskPii(item)) as T;\n }\n\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n result[key] = maskPii(val);\n }\n return result as T;\n }\n\n // numbers, booleans — pass through\n return value;\n}\n","/**\n * utils/response.ts — Single response choke point for all tool handlers.\n *\n * ALL tool responses pass through formatToolResponse().\n * Applies PII masking (default on), enforces response shape.\n */\n\nimport { maskPii } from './pii-masker.js';\nimport { FfError } from '../errors/catalog.js';\n\nexport interface NextAction {\n tool: string;\n action: string;\n params?: Record<string, unknown>;\n}\n\nexport interface ComplianceMeta {\n disclaimer: string;\n verificationGuidance: string | null;\n glossary: Array<{ term: string; definition: string }>;\n}\n\nexport interface SuccessResult<T = unknown> {\n success: true;\n data: T;\n message: string;\n nextAction?: NextAction;\n prompt?: string;\n compliance?: ComplianceMeta;\n}\n\nexport interface ErrorResult {\n success: false;\n error: {\n code: string;\n message: string;\n suggestion?: string;\n };\n}\n\nexport type ToolResult<T = unknown> = SuccessResult<T> | ErrorResult;\n\ninterface FormatOptions {\n showSensitive?: boolean;\n}\n\n/**\n * Format a successful tool response.\n * Applies PII masking unless showSensitive is true.\n * Returns MCP-compatible content array.\n */\nexport function formatToolResponse<T>(\n result: { data: T; message: string; nextAction?: NextAction; prompt?: string; compliance?: ComplianceMeta },\n options?: FormatOptions,\n): { content: Array<{ type: 'text'; text: string }> } {\n const masked = options?.showSensitive ? result.data : maskPii(result.data);\n const message = options?.showSensitive ? result.message : maskPii(result.message);\n\n const response: SuccessResult<T> = {\n success: true,\n data: masked as T,\n message: message as string,\n ...(result.nextAction && { nextAction: result.nextAction }),\n ...(result.prompt && { prompt: result.prompt }),\n ...(result.compliance && { compliance: result.compliance }),\n };\n\n return { content: [{ type: 'text', text: JSON.stringify(response) }] };\n}\n\n/**\n * Format an error response.\n * Works with FfError or plain Error.\n * Returns MCP-compatible content array.\n */\nexport function formatErrorResponse(\n error: unknown,\n): { content: Array<{ type: 'text'; text: string }>; isError: true } {\n if (error instanceof FfError) {\n const response: ErrorResult = {\n success: false,\n error: {\n code: error.code,\n message: error.message,\n ...(error.suggestion && { suggestion: error.suggestion }),\n },\n };\n return { content: [{ type: 'text', text: JSON.stringify(response) }], isError: true };\n }\n\n // Unexpected error — generic message\n const response: ErrorResult = {\n success: false,\n error: {\n code: 'INTERNAL_ERROR',\n message: 'An unexpected error occurred. Please try again.',\n },\n };\n return { content: [{ type: 'text', text: JSON.stringify(response) }], isError: true };\n}\n","/**\n * utils/audit.ts — Async audit trail writer.\n *\n * Append-only audit_log table. Async write — never blocks tool execution.\n * PII is redacted from input_summary before logging.\n */\n\nimport type { TypedSupabaseClient } from '../db/client.js';\nimport type { Json } from '../../../../supabase/types/database.js';\nimport { maskPii } from './pii-masker.js';\nimport { logger } from './logger.js';\n\ninterface AuditEntry {\n tool: string;\n action: string;\n companyId?: string;\n entityType?: string;\n entityId?: string;\n input?: Record<string, unknown>;\n result: 'success' | 'error';\n errorCode?: string;\n}\n\n/**\n * Write an audit log entry asynchronously.\n * Never throws — failures are logged to stderr but don't block the caller.\n */\nexport function writeAuditLog(\n supabase: TypedSupabaseClient,\n userId: string,\n entry: AuditEntry,\n): void {\n // Redact PII from input before logging\n const inputRedacted = entry.input ? (maskPii(entry.input) as Json) : null;\n const resultSummary: Json = entry.errorCode\n ? { status: entry.result, errorCode: entry.errorCode }\n : { status: entry.result };\n\n // Fire-and-forget — wrap in Promise.resolve to get .catch()\n Promise.resolve(\n supabase\n .from('audit_log')\n .insert({\n user_id: userId,\n tool_name: entry.tool,\n action: entry.action,\n company_id: entry.companyId ?? null,\n entity_type: entry.entityType ?? null,\n entity_id: entry.entityId ?? null,\n input_summary: inputRedacted,\n result_summary: resultSummary,\n }),\n )\n .then(({ error }) => {\n if (error) {\n logger.warn('Failed to write audit log', {\n tool: entry.tool,\n action: entry.action,\n error: error.message,\n });\n }\n })\n .catch((err: unknown) => {\n logger.warn('Audit log write threw', {\n tool: entry.tool,\n action: entry.action,\n error: err instanceof Error ? err.message : 'unknown',\n });\n });\n}\n","/**\n * db/companies.ts — Company CRUD operations + camelCase mapper.\n *\n * Every function receives supabase client as parameter (from SessionContext).\n * Returns null for single-row lookups when not found (uses .maybeSingle()).\n * Only throws for unexpected Supabase errors.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\nimport type { Json } from '../../../../supabase/types/database.js';\n\n// --- Types ---\n\nexport interface CompanyRecord {\n id: string;\n userId: string;\n name: string;\n entityType: string;\n stateOfFormation: string;\n formationDate: string | null;\n einEncrypted: string | null;\n registeredAgentName: string | null;\n registeredAgentAddress: Record<string, string> | null;\n shareStructure: Record<string, unknown> | null;\n officers: Array<{ name: string; title: string; email?: string }>;\n foreignRegistrations: Array<{ state: string; registrationDate?: string; endDate?: string; status?: string }>;\n hasEmployees: boolean;\n hasPayroll: boolean;\n fiscalYearEnd: string | null;\n timezone: string | null;\n status: string;\n formationDocumentId: string | null;\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CompanyCreateInput {\n userId: string;\n name: string;\n entityType: string;\n stateOfFormation: string;\n formationDate?: string;\n timezone?: string;\n}\n\nexport interface CompanyUpdateInput {\n name?: string;\n entityType?: string;\n stateOfFormation?: string;\n formationDate?: string;\n hasEmployees?: boolean;\n hasPayroll?: boolean;\n fiscalYearEnd?: string;\n timezone?: string;\n registeredAgentName?: string;\n registeredAgentAddress?: Record<string, string>;\n officers?: Array<{ name: string; title: string; email?: string }>;\n foreignRegistrations?: Array<{ state: string; registrationDate?: string; endDate?: string; status?: string }>;\n estimatedTaxCadence?: 'annual' | 'quarterly';\n usesContractors?: boolean;\n dataPrivacyProgramRequired?: boolean;\n ipGovernanceRequired?: boolean;\n formationDocumentId?: string | null;\n}\n\nexport interface CompanyListResult {\n items: CompanyRecord[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): CompanyRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n name: row.name as string,\n entityType: row.entity_type as string,\n stateOfFormation: row.state_of_formation as string,\n formationDate: (row.formation_date as string) ?? null,\n einEncrypted: (row.ein_encrypted as string) ?? null,\n registeredAgentName: (row.registered_agent_name as string) ?? null,\n registeredAgentAddress: (row.registered_agent_address as Record<string, string>) ?? null,\n shareStructure: (row.share_structure as Record<string, unknown>) ?? null,\n officers: (row.officers as CompanyRecord['officers']) ?? [],\n foreignRegistrations: (row.foreign_registrations as CompanyRecord['foreignRegistrations']) ?? [],\n hasEmployees: row.has_employees as boolean,\n hasPayroll: row.has_payroll as boolean,\n fiscalYearEnd: (row.fiscal_year_end as string) ?? null,\n timezone: (row.timezone as string) ?? null,\n status: row.status as string,\n formationDocumentId: (row.formation_document_id as string) ?? null,\n metadata: (row.metadata as Record<string, unknown>) ?? {},\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\nexport async function createCompany(\n supabase: TypedSupabaseClient,\n input: CompanyCreateInput,\n): Promise<CompanyRecord> {\n const { data, error } = await supabase\n .from('companies')\n .insert({\n user_id: input.userId,\n name: input.name,\n entity_type: input.entityType,\n state_of_formation: input.stateOfFormation,\n formation_date: input.formationDate ?? null,\n timezone: input.timezone ?? 'UTC',\n })\n .select()\n .single();\n\n if (error) {\n if (error.code === '23505') {\n // unique_violation on (user_id, name)\n throw Object.assign(new Error(`A company named \"${input.name}\" already exists.`), {\n code: 'CONFLICT',\n });\n }\n throw error;\n }\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function getCompanyById(\n supabase: TypedSupabaseClient,\n companyId: string,\n): Promise<CompanyRecord | null> {\n const { data, error } = await supabase\n .from('companies')\n .select('*')\n .eq('id', companyId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function listCompanies(\n supabase: TypedSupabaseClient,\n limit = 20,\n offset = 0,\n): Promise<CompanyListResult> {\n const { data, error, count } = await supabase\n .from('companies')\n .select('*', { count: 'exact' })\n .order('created_at', { ascending: false })\n .range(offset, offset + limit - 1);\n\n if (error) throw error;\n\n const items = (data ?? []).map((row) => mapRow(row as unknown as Record<string, unknown>));\n const total = count ?? items.length;\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n}\n\nexport async function updateCompany(\n supabase: TypedSupabaseClient,\n companyId: string,\n input: CompanyUpdateInput,\n): Promise<CompanyRecord> {\n // Build update object with only provided fields (snake_case)\n const updates: Record<string, unknown> = {};\n if (input.name !== undefined) updates.name = input.name;\n if (input.entityType !== undefined) updates.entity_type = input.entityType;\n if (input.stateOfFormation !== undefined) updates.state_of_formation = input.stateOfFormation;\n if (input.formationDate !== undefined) updates.formation_date = input.formationDate;\n if (input.hasEmployees !== undefined) updates.has_employees = input.hasEmployees;\n if (input.hasPayroll !== undefined) updates.has_payroll = input.hasPayroll;\n if (input.fiscalYearEnd !== undefined) updates.fiscal_year_end = input.fiscalYearEnd;\n if (input.timezone !== undefined) updates.timezone = input.timezone;\n if (input.registeredAgentName !== undefined) updates.registered_agent_name = input.registeredAgentName;\n if (input.registeredAgentAddress !== undefined) updates.registered_agent_address = input.registeredAgentAddress as Json;\n if (input.officers !== undefined) updates.officers = input.officers as Json;\n if (input.foreignRegistrations !== undefined) updates.foreign_registrations = input.foreignRegistrations as Json;\n if (input.formationDocumentId !== undefined) updates.formation_document_id = input.formationDocumentId;\n\n const metadataPatch: Record<string, unknown> = {};\n if (input.estimatedTaxCadence !== undefined) {\n metadataPatch.estimatedTaxCadence = input.estimatedTaxCadence;\n }\n if (input.usesContractors !== undefined) {\n metadataPatch.usesContractors = input.usesContractors;\n }\n if (input.dataPrivacyProgramRequired !== undefined) {\n metadataPatch.dataPrivacyProgramRequired = input.dataPrivacyProgramRequired;\n }\n if (input.ipGovernanceRequired !== undefined) {\n metadataPatch.ipGovernanceRequired = input.ipGovernanceRequired;\n }\n\n if (Object.keys(metadataPatch).length > 0) {\n const { data: current, error: fetchError } = await supabase\n .from('companies')\n .select('metadata')\n .eq('id', companyId)\n .single();\n if (fetchError) throw fetchError;\n const currentMetadata = (current?.metadata as Record<string, unknown> | null) ?? {};\n updates.metadata = { ...currentMetadata, ...metadataPatch } as Json;\n }\n\n const { data, error } = await supabase\n .from('companies')\n .update(updates)\n .eq('id', companyId)\n .select()\n .single();\n\n if (error) throw error;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function addCompanyNote(\n supabase: TypedSupabaseClient,\n companyId: string,\n note: string,\n): Promise<CompanyRecord> {\n // Fetch current metadata\n const { data: current, error: fetchError } = await supabase\n .from('companies')\n .select('metadata')\n .eq('id', companyId)\n .single();\n\n if (fetchError) throw fetchError;\n\n const metadata = (current?.metadata ?? {}) as Record<string, unknown>;\n const notes = (metadata.notes as Array<{ text: string; timestamp: string }>) ?? [];\n notes.push({ text: note, timestamp: new Date().toISOString() });\n\n const { data, error } = await supabase\n .from('companies')\n .update({ metadata: { ...metadata, notes } as Json })\n .eq('id', companyId)\n .select()\n .single();\n\n if (error) throw error;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n","/**\n * db/completeness.ts — Company profile completeness scoring.\n *\n * Classifies fields as critical / important / optional, computes a weighted\n * completeness score (0–100), and lists missing fields for smart ingestion.\n */\n\nimport type { CompanyRecord } from './companies.js';\n\nexport type FieldPriority = 'critical' | 'important' | 'optional';\n\nexport interface FieldStatus {\n field: string;\n label: string;\n priority: FieldPriority;\n populated: boolean;\n}\n\nexport interface CompletenessResult {\n score: number; // 0–100\n populated: number;\n total: number;\n missing: FieldStatus[];\n fields: FieldStatus[];\n}\n\n/** Weight per priority tier (must sum to 1.0). */\nconst WEIGHTS: Record<FieldPriority, number> = {\n critical: 0.4,\n important: 0.4,\n optional: 0.2,\n};\n\ninterface FieldDef {\n field: keyof CompanyRecord;\n label: string;\n priority: FieldPriority;\n /** Custom check for \"populated\". Default: not null/undefined and not empty array/object. */\n check?: (value: unknown) => boolean;\n}\n\nconst FIELD_DEFS: FieldDef[] = [\n // Critical (3 fields)\n { field: 'entityType', label: 'Entity type', priority: 'critical' },\n { field: 'stateOfFormation', label: 'State of formation', priority: 'critical' },\n { field: 'formationDate', label: 'Formation date', priority: 'critical' },\n\n // Important (4 fields)\n { field: 'einEncrypted', label: 'EIN', priority: 'important' },\n {\n field: 'officers',\n label: 'Officers / directors',\n priority: 'important',\n check: (v) => Array.isArray(v) && v.length > 0,\n },\n { field: 'registeredAgentName', label: 'Registered agent name', priority: 'important' },\n { field: 'registeredAgentAddress', label: 'Registered agent address', priority: 'important' },\n\n // Optional (6 fields)\n { field: 'shareStructure', label: 'Share structure', priority: 'optional' },\n { field: 'fiscalYearEnd', label: 'Fiscal year end', priority: 'optional' },\n { field: 'timezone', label: 'Timezone', priority: 'optional' },\n { field: 'hasEmployees', label: 'Has employees (set)', priority: 'optional', check: (v) => v !== null && v !== undefined },\n { field: 'hasPayroll', label: 'Has payroll (set)', priority: 'optional', check: (v) => v !== null && v !== undefined },\n {\n field: 'foreignRegistrations',\n label: 'Foreign registrations',\n priority: 'optional',\n check: (v) => Array.isArray(v) && v.length > 0,\n },\n];\n\nfunction isPopulated(value: unknown): boolean {\n if (value === null || value === undefined) return false;\n if (typeof value === 'string') return value.length > 0;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === 'object') return Object.keys(value as object).length > 0;\n return true; // booleans, numbers\n}\n\n/**\n * Compute completeness score for a company profile.\n */\nexport function computeCompleteness(company: CompanyRecord): CompletenessResult {\n const fields: FieldStatus[] = [];\n\n for (const def of FIELD_DEFS) {\n const value = company[def.field];\n const populated = def.check ? def.check(value) : isPopulated(value);\n fields.push({\n field: def.field,\n label: def.label,\n priority: def.priority,\n populated,\n });\n }\n\n // Compute weighted score per tier\n const tiers: Record<FieldPriority, { populated: number; total: number }> = {\n critical: { populated: 0, total: 0 },\n important: { populated: 0, total: 0 },\n optional: { populated: 0, total: 0 },\n };\n\n for (const f of fields) {\n tiers[f.priority].total++;\n if (f.populated) tiers[f.priority].populated++;\n }\n\n let score = 0;\n for (const priority of ['critical', 'important', 'optional'] as FieldPriority[]) {\n const tier = tiers[priority];\n if (tier.total > 0) {\n score += WEIGHTS[priority] * (tier.populated / tier.total);\n }\n }\n\n const populatedCount = fields.filter((f) => f.populated).length;\n const missing = fields.filter((f) => !f.populated);\n\n return {\n score: Math.round(score * 100),\n populated: populatedCount,\n total: fields.length,\n missing,\n fields,\n };\n}\n","/**\n * db/documents.ts — Document CRUD operations + camelCase mapper.\n *\n * Every function receives supabase client as parameter (from SessionContext).\n * Returns null for single-row lookups when not found (uses .maybeSingle()).\n */\n\nimport type { TypedSupabaseClient } from './client.js';\nimport type { DocumentClass } from '../domain/classification/types.js';\nimport type { Json } from '../../../../supabase/types/database.js';\n\n// --- Types ---\n\nexport interface DocumentRecord {\n id: string;\n companyId: string;\n userId: string;\n title: string;\n documentType: string | null;\n documentClass: DocumentClass;\n description: string | null;\n fileName: string;\n fileSize: number;\n mimeType: string;\n storagePath: string;\n extractedText: string | null;\n extractionStatus: 'ok' | 'text_unavailable';\n source: string;\n sourceMetadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface DocumentSearchResult {\n id: string;\n companyId: string;\n title: string;\n documentType: string | null;\n documentClass: DocumentClass;\n fileName: string;\n fileSize: number;\n mimeType: string;\n extractionStatus: 'ok' | 'text_unavailable';\n excerpt: string;\n createdAt: string;\n}\n\nexport interface DocumentSearchResults {\n items: DocumentSearchResult[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n query: string;\n}\n\nexport interface DocumentListResults {\n items: DocumentMetadata[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\nexport interface DocumentDeleteResult {\n title: string;\n fileName: string;\n}\n\nexport interface DocumentCreateInput {\n id: string;\n companyId: string;\n userId: string;\n title: string;\n documentType?: string;\n documentClass: DocumentClass;\n description?: string;\n fileName: string;\n fileSize: number;\n mimeType: string;\n storagePath: string;\n extractedText?: string;\n extractionStatus: 'ok' | 'text_unavailable';\n source?: string;\n sourceMetadata?: Record<string, unknown>;\n}\n\n/** Metadata view — everything except extractedText. */\nexport type DocumentMetadata = Omit<DocumentRecord, 'extractedText'>;\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): DocumentRecord {\n return {\n id: row.id as string,\n companyId: row.company_id as string,\n userId: row.user_id as string,\n title: row.title as string,\n documentType: (row.document_type as string) ?? null,\n documentClass: row.document_class as DocumentClass,\n description: (row.description as string) ?? null,\n fileName: row.file_name as string,\n fileSize: row.file_size as number,\n mimeType: row.mime_type as string,\n storagePath: row.storage_path as string,\n extractedText: (row.extracted_text as string) ?? null,\n extractionStatus: row.extraction_status as 'ok' | 'text_unavailable',\n source: row.source as string,\n sourceMetadata: (row.source_metadata as Record<string, unknown>) ?? {},\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\nfunction mapMetadata(row: Record<string, unknown>): DocumentMetadata {\n return {\n id: row.id as string,\n companyId: row.company_id as string,\n userId: row.user_id as string,\n title: row.title as string,\n documentType: (row.document_type as string) ?? null,\n documentClass: row.document_class as DocumentClass,\n description: (row.description as string) ?? null,\n fileName: row.file_name as string,\n fileSize: row.file_size as number,\n mimeType: row.mime_type as string,\n storagePath: row.storage_path as string,\n extractionStatus: row.extraction_status as 'ok' | 'text_unavailable',\n source: row.source as string,\n sourceMetadata: (row.source_metadata as Record<string, unknown>) ?? {},\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\nexport async function createDocument(\n supabase: TypedSupabaseClient,\n input: DocumentCreateInput,\n): Promise<DocumentRecord> {\n const { data, error } = await supabase\n .from('documents')\n .insert({\n id: input.id,\n company_id: input.companyId,\n user_id: input.userId,\n title: input.title,\n document_type: input.documentType ?? null,\n document_class: input.documentClass,\n description: input.description ?? null,\n file_name: input.fileName,\n file_size: input.fileSize,\n mime_type: input.mimeType,\n storage_path: input.storagePath,\n extracted_text: input.extractedText ?? null,\n extraction_status: input.extractionStatus,\n source: input.source ?? 'local',\n source_metadata: (input.sourceMetadata ?? {}) as Json,\n })\n .select()\n .single();\n\n if (error) throw error;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function getDocumentById(\n supabase: TypedSupabaseClient,\n documentId: string,\n): Promise<DocumentRecord | null> {\n const { data, error } = await supabase\n .from('documents')\n .select('*')\n .eq('id', documentId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\n/** Metadata columns — excludes extracted_text for lighter responses. */\nconst METADATA_COLUMNS =\n 'id, company_id, user_id, title, document_type, document_class, description, file_name, file_size, mime_type, storage_path, extraction_status, source, source_metadata, created_at, updated_at';\n\nexport async function getDocumentMetadata(\n supabase: TypedSupabaseClient,\n documentId: string,\n): Promise<DocumentMetadata | null> {\n const { data, error } = await supabase\n .from('documents')\n .select(METADATA_COLUMNS)\n .eq('id', documentId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapMetadata(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Build a storage path: `{userId}/{companyId}/{documentId}/{fileName}`\n * Pure helper — no I/O.\n */\nexport function buildStoragePath(\n userId: string,\n companyId: string,\n documentId: string,\n fileName: string,\n): string {\n return `${userId}/${companyId}/${documentId}/${fileName}`;\n}\n\n// --- Search ---\n\n/**\n * Build a 200-char excerpt centered on the first query term match.\n * Falls back to first 200 chars if no match found.\n * Pure function — no I/O.\n */\nexport function buildExcerpt(text: string, queryTerms: string[], maxLen = 200): string {\n if (!text) return '';\n const lowerText = text.toLowerCase();\n\n let matchIndex = -1;\n for (const term of queryTerms) {\n const idx = lowerText.indexOf(term.toLowerCase());\n if (idx !== -1) {\n matchIndex = idx;\n break;\n }\n }\n\n if (matchIndex === -1) {\n // No term found — return first maxLen chars\n const slice = text.slice(0, maxLen).trim();\n return text.length > maxLen ? slice + '...' : slice;\n }\n\n // Center the window around the match\n const halfWindow = Math.floor(maxLen / 2);\n let start = Math.max(0, matchIndex - halfWindow);\n let end = Math.min(text.length, start + maxLen);\n\n // If we hit the end, shift start back\n if (end === text.length && end - start < maxLen) {\n start = Math.max(0, end - maxLen);\n }\n\n let excerpt = text.slice(start, end).trim();\n if (start > 0) excerpt = '...' + excerpt;\n if (end < text.length) excerpt = excerpt + '...';\n\n return excerpt;\n}\n\nconst SEARCH_COLUMNS =\n 'id, company_id, title, document_type, document_class, file_name, file_size, mime_type, extraction_status, extracted_text, created_at';\n\nexport async function searchDocuments(\n supabase: TypedSupabaseClient,\n companyId: string,\n query: string,\n limit = 10,\n offset = 0,\n): Promise<DocumentSearchResults> {\n // Get matching documents using full-text search (GIN index)\n const { data, error, count } = await supabase\n .from('documents')\n .select(SEARCH_COLUMNS, { count: 'exact' })\n .eq('company_id', companyId)\n .textSearch('text_search_vector', query, { type: 'plain' })\n .order('created_at', { ascending: false })\n .range(offset, offset + limit - 1);\n\n if (error) throw error;\n\n const rows = (data ?? []) as unknown as Array<Record<string, unknown>>;\n const queryTerms = query.split(/\\s+/).filter(Boolean);\n\n const items: DocumentSearchResult[] = rows.map((row) => ({\n id: row.id as string,\n companyId: row.company_id as string,\n title: row.title as string,\n documentType: (row.document_type as string) ?? null,\n documentClass: row.document_class as DocumentClass,\n fileName: row.file_name as string,\n fileSize: row.file_size as number,\n mimeType: row.mime_type as string,\n extractionStatus: row.extraction_status as 'ok' | 'text_unavailable',\n excerpt: buildExcerpt((row.extracted_text as string) ?? '', queryTerms),\n createdAt: row.created_at as string,\n }));\n\n const total = count ?? 0;\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n query,\n };\n}\n\n// --- List ---\n\nexport async function listDocuments(\n supabase: TypedSupabaseClient,\n companyId: string,\n filters: { documentClass?: string },\n limit = 25,\n offset = 0,\n): Promise<DocumentListResults> {\n let query = supabase\n .from('documents')\n .select(METADATA_COLUMNS, { count: 'exact' })\n .eq('company_id', companyId);\n\n if (filters.documentClass) {\n query = query.eq('document_class', filters.documentClass);\n }\n\n const { data, error, count } = await query\n .order('created_at', { ascending: false })\n .range(offset, offset + limit - 1);\n\n if (error) throw error;\n\n const rows = (data ?? []) as unknown as Array<Record<string, unknown>>;\n const items = rows.map(mapMetadata);\n const total = count ?? 0;\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n}\n\n// --- Delete ---\n\nexport async function deleteDocument(\n supabase: TypedSupabaseClient,\n documentId: string,\n): Promise<DocumentDeleteResult> {\n // 1. Look up document for response info + storage path\n const { data: doc, error: lookupError } = await supabase\n .from('documents')\n .select('id, storage_path, file_name, title')\n .eq('id', documentId)\n .maybeSingle();\n\n if (lookupError) throw lookupError;\n if (!doc) return null as unknown as DocumentDeleteResult; // caller handles NOT_FOUND\n\n const storagePath = (doc as Record<string, unknown>).storage_path as string;\n const fileName = (doc as Record<string, unknown>).file_name as string;\n const title = (doc as Record<string, unknown>).title as string;\n\n // 2. Delete DB row (RLS enforces ownership)\n const { error: deleteError } = await supabase\n .from('documents')\n .delete()\n .eq('id', documentId);\n\n if (deleteError) throw deleteError;\n\n // 3. Delete Storage file (best-effort — orphaned file is better than ghost DB row)\n const { error: storageError } = await supabase.storage\n .from('documents')\n .remove([storagePath]);\n\n if (storageError) {\n // Log warning but don't fail — DB row is already gone\n // Caller's logger will pick this up\n console.warn(`Storage cleanup failed for ${storagePath}: ${storageError.message}`);\n }\n\n return { title, fileName };\n}\n","/**\n * domain/compliance/rule-loader.ts — Load compliance rules from Supabase static_content.\n *\n * Reads the _index.json at startup. Lazy-loaded and cached.\n * Rule files themselves are loaded on demand (not at startup).\n */\n\nimport type { IndexedRule } from './rule-evaluator.js';\nimport { loadStaticFile } from '../../utils/static-data.js';\nimport { logger } from '../../utils/logger.js';\n\ninterface RuleIndex {\n total_rules: number;\n full_tier_count: number;\n basic_tier_count: number;\n generated: string;\n rules: IndexedRule[];\n}\n\nlet cachedIndex: RuleIndex | null = null;\n\n/**\n * Load the compliance rule index. Cached after first call.\n */\nexport async function loadRuleIndex(): Promise<IndexedRule[]> {\n if (cachedIndex) return cachedIndex.rules;\n\n const raw = await loadStaticFile('compliance/_index.json');\n cachedIndex = JSON.parse(raw) as RuleIndex;\n\n logger.info('Compliance rules loaded', {\n total: cachedIndex.total_rules,\n full: cachedIndex.full_tier_count,\n basic: cachedIndex.basic_tier_count,\n });\n\n return cachedIndex.rules;\n}\n\n/**\n * Load a full rule file by rule_id.\n * Returns the parsed JSON rule object.\n */\nexport async function loadFullRule(ruleId: string): Promise<Record<string, unknown>> {\n const raw = await loadStaticFile(`compliance/rules/${ruleId}.json`);\n return JSON.parse(raw) as Record<string, unknown>;\n}\n\n/**\n * Reset cached index (for testing only).\n */\nexport function _resetRuleCache(): void {\n cachedIndex = null;\n}\n","/**\n * domain/compliance/rule-evaluator.ts — Compliance rule applicability engine.\n *\n * Evaluates the 157-rule set against a company profile.\n * Each rule is classified as: applicable, pending, or suppressed.\n *\n * \"pending\" = the rule *might* apply but trigger fields are missing.\n * \"suppressed\" = the rule definitively does not apply.\n */\n\nimport type { CompanyRecord } from '../../db/companies.js';\n\n/** A rule from the compiled index. */\nexport interface IndexedRule {\n rule_id: string;\n filing_name: string;\n jurisdiction: string;\n entity_types: string[];\n category: string;\n frequency: string;\n coverage_tier: 'full' | 'basic';\n file: string;\n}\n\nexport type ApplicabilityStatus = 'applicable' | 'pending' | 'suppressed';\n\nexport interface EvaluatedRule {\n ruleId: string;\n filingName: string;\n jurisdiction: string;\n category: string;\n frequency: string;\n coverageTier: 'full' | 'basic';\n status: ApplicabilityStatus;\n reason: string;\n}\n\n/** Categories that require employees or payroll to be relevant. */\nconst EMPLOYEE_GATED_CATEGORIES = new Set([\n 'federal_payroll',\n 'federal_information_return',\n 'state_payroll',\n]);\n\n/** Categories that always apply regardless of state (except explicit state/jurisdiction gates). */\nconst UNIVERSAL_CATEGORIES = new Set([\n 'federal_income_tax',\n 'federal_payroll',\n 'federal_information_return',\n 'federal_other',\n 'governance',\n]);\n\nconst CONDITIONAL_OPT_IN_CATEGORIES = new Set([\n 'intellectual_property',\n 'data_privacy',\n]);\n\n/**\n * Evaluate all rules against a company profile.\n * Returns classified rules sorted by status (applicable first, then pending, then suppressed).\n */\nexport function evaluateRules(\n rules: IndexedRule[],\n company: CompanyRecord,\n): EvaluatedRule[] {\n const companyStates = getCompanyStates(company);\n\n const results: EvaluatedRule[] = [];\n\n for (const rule of rules) {\n const result = evaluateSingleRule(rule, company, companyStates);\n results.push(result);\n }\n\n // Sort: applicable first, then pending, then suppressed\n const statusOrder: Record<ApplicabilityStatus, number> = {\n applicable: 0,\n pending: 1,\n suppressed: 2,\n };\n results.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);\n\n return results;\n}\n\n/**\n * Get all states where the company operates (formation + foreign registrations).\n */\nfunction getCompanyStates(company: CompanyRecord): Set<string> {\n const states = new Set<string>();\n states.add(company.stateOfFormation);\n for (const reg of company.foreignRegistrations) {\n states.add(reg.state);\n }\n return states;\n}\n\n/**\n * Evaluate a single rule against a company profile.\n */\nfunction evaluateSingleRule(\n rule: IndexedRule,\n company: CompanyRecord,\n companyStates: Set<string>,\n): EvaluatedRule {\n const base = {\n ruleId: rule.rule_id,\n filingName: rule.filing_name,\n jurisdiction: rule.jurisdiction,\n category: rule.category,\n frequency: rule.frequency,\n coverageTier: rule.coverage_tier,\n };\n\n // Check 1: Entity type match\n if (!rule.entity_types.includes(company.entityType)) {\n return {\n ...base,\n status: 'suppressed',\n reason: `Does not apply to ${company.entityType} entities.`,\n };\n }\n\n // Check 2: Jurisdiction match (skip for universal categories)\n if (!UNIVERSAL_CATEGORIES.has(rule.category)) {\n // Federal/internal rules are not tied to a specific state.\n const jurisdiction = (rule.jurisdiction ?? '').toUpperCase();\n const isGlobalJurisdiction = jurisdiction === 'FEDERAL' || jurisdiction === 'INTERNAL';\n\n // State-specific rules must match the company's states.\n if (!isGlobalJurisdiction && !companyStates.has(rule.jurisdiction)) {\n return {\n ...base,\n status: 'suppressed',\n reason: `Company does not operate in ${rule.jurisdiction}.`,\n };\n }\n }\n\n // Check 3: Special-case federal estimated tax cadence\n if (rule.rule_id === 'federal-estimated-tax') {\n return evaluateEstimatedTaxRule(base, company);\n }\n\n // Check 4: Optional/conditional categories (privacy/IP) default to pending until confirmed.\n if (CONDITIONAL_OPT_IN_CATEGORIES.has(rule.category)) {\n return evaluateOptionalCategoryRule(base, company);\n }\n\n // Check 5: Employee/payroll gating\n if (EMPLOYEE_GATED_CATEGORIES.has(rule.category)) {\n return evaluateEmployeeGatedRule(base, company);\n }\n\n // Check 6: State registration rules (blue sky notices, foreign registration)\n if (rule.category === 'state_registration') {\n // These rules need specific context — mark as applicable if the state matches\n return {\n ...base,\n status: 'applicable',\n reason: 'State registration requirement.',\n };\n }\n\n // Default: rule applies\n return {\n ...base,\n status: 'applicable',\n reason: 'Applies to this entity type and jurisdiction.',\n };\n}\n\nfunction evaluateEstimatedTaxRule(\n base: Omit<EvaluatedRule, 'status' | 'reason'>,\n company: CompanyRecord,\n): EvaluatedRule {\n const mode = readMetadataString(company, 'estimatedTaxCadence');\n\n if (mode === 'quarterly') {\n return {\n ...base,\n status: 'applicable',\n reason: 'Company profile indicates quarterly estimated tax payments.',\n };\n }\n\n if (mode === 'annual') {\n return {\n ...base,\n status: 'suppressed',\n reason: 'Company profile indicates annual filing only (no quarterly estimates).',\n };\n }\n\n // Local default: assume annual filing cadence unless profile or evidence says quarterly.\n return {\n ...base,\n status: 'suppressed',\n reason: 'Default assumption: annual filing cadence (no quarterly estimated payments) unless confirmed otherwise.',\n };\n}\n\nfunction evaluateOptionalCategoryRule(\n base: Omit<EvaluatedRule, 'status' | 'reason'>,\n company: CompanyRecord,\n): EvaluatedRule {\n const flagKey = base.category === 'data_privacy' ? 'dataPrivacyProgramRequired' : 'ipGovernanceRequired';\n const flag = readMetadataBoolean(company, flagKey);\n\n if (flag === true) {\n return {\n ...base,\n status: 'applicable',\n reason: 'Marked applicable in company profile.',\n };\n }\n\n if (flag === false) {\n return {\n ...base,\n status: 'suppressed',\n reason: 'Marked not applicable in company profile.',\n };\n }\n\n return {\n ...base,\n status: 'pending',\n reason: 'Confirm whether this category applies to your company.',\n };\n}\n\n/**\n * Evaluate employee/payroll-gated rules.\n * If trigger fields are null/unset, mark as \"pending\" (not suppressed).\n */\nfunction evaluateEmployeeGatedRule(\n base: Omit<EvaluatedRule, 'status' | 'reason'>,\n company: CompanyRecord,\n): EvaluatedRule {\n // 1099-NEC applies to companies with contractors (we gate on hasPayroll as proxy)\n const isContractorRule = base.ruleId === 'federal-form-1099-nec';\n\n if (isContractorRule) {\n const usesContractors = readMetadataBoolean(company, 'usesContractors');\n if (usesContractors === true) {\n return {\n ...base,\n status: 'applicable',\n reason: 'Company profile indicates contractor payments.',\n };\n }\n if (usesContractors === false) {\n return {\n ...base,\n status: 'suppressed',\n reason: 'No independent contractors reported.',\n };\n }\n return {\n ...base,\n status: 'pending',\n reason: 'Confirm whether the company pays independent contractors.',\n };\n }\n\n // For payroll rules: check has_employees and has_payroll\n // Both default to false in the schema, so we treat false as \"explicitly no\"\n if (!company.hasEmployees && !company.hasPayroll) {\n return {\n ...base,\n status: 'suppressed',\n reason: 'No employees or payroll reported.',\n };\n }\n\n if (company.hasEmployees || company.hasPayroll) {\n return {\n ...base,\n status: 'applicable',\n reason: 'Company has employees or payroll.',\n };\n }\n\n // Should not reach here, but handle gracefully\n return {\n ...base,\n status: 'pending',\n reason: 'Complete your profile for accurate payroll obligations.',\n };\n}\n\nfunction readMetadataBoolean(company: CompanyRecord, key: string): boolean | undefined {\n const value = company.metadata?.[key];\n if (typeof value === 'boolean') return value;\n return undefined;\n}\n\nfunction readMetadataString(company: CompanyRecord, key: string): string | undefined {\n const value = company.metadata?.[key];\n if (typeof value === 'string') return value.toLowerCase();\n return undefined;\n}\n\n/**\n * Summarize evaluation results.\n */\nexport function summarizeEvaluation(results: EvaluatedRule[]): {\n applicable: number;\n pending: number;\n suppressed: number;\n total: number;\n} {\n let applicable = 0;\n let pending = 0;\n let suppressed = 0;\n\n for (const r of results) {\n if (r.status === 'applicable') applicable++;\n else if (r.status === 'pending') pending++;\n else suppressed++;\n }\n\n return { applicable, pending, suppressed, total: results.length };\n}\n","/**\n * domain/compliance/deadline-generator.ts — Compute concrete deadline dates from rules.\n *\n * Takes applicable rules + company profile → produces dated deadline objects.\n * Pure domain logic — no database writes (persistence is Phase 2).\n *\n * Supports all 6 deadline types:\n * fixed_date — same calendar date every year (e.g., DE franchise tax Mar 1)\n * anniversary_month — based on formation month (e.g., CO periodic report)\n * fiscal_year_relative — offset from fiscal year end (e.g., Form 1120 = FYE + 4 months + 15th)\n * quarterly_fixed_dates — 4 fixed dates per year (e.g., CA EDD quarterly)\n * relative_days — N days after a trigger event (e.g., 83(b) election = 30 days after grant)\n * event_triggered — one-time/no-deadline rules (e.g., OH no annual report)\n */\n\nimport type { EvaluatedRule } from './rule-evaluator.js';\n\n/** Full rule data loaded from JSON (the deadline portion). */\nexport interface RuleDeadline {\n type: 'fixed_date' | 'anniversary_month' | 'fiscal_year_relative' | 'quarterly_fixed_dates' | 'relative_days' | 'event_triggered';\n due_date_month: number | null;\n due_date_day: number | string | null;\n due_date_offset_months: number | null;\n frequency: 'annual' | 'biennial' | 'quarterly' | 'one_time' | 'event_triggered';\n grace_period_days: number | null;\n quarterly_dates: { q1: string; q2: string; q3: string; q4: string } | null;\n trigger_event: string | null;\n trigger_offset_days: number | null;\n}\n\nexport interface CompanyContext {\n formationDate: string | null;\n fiscalYearEnd: string | null; // \"MM-DD\" format (e.g., \"12-31\")\n}\n\nexport interface GeneratedDeadline {\n ruleId: string;\n filingName: string;\n jurisdiction: string;\n category: string;\n dueDate: string; // ISO date \"YYYY-MM-DD\"\n frequency: string;\n coverageTier: 'full' | 'basic';\n status: 'upcoming' | 'unknown'; // 'unknown' for historical, 'upcoming' for future\n isHistorical: boolean;\n}\n\nexport interface DeadlineGenerationResult {\n deadlines: GeneratedDeadline[];\n skipped: Array<{ ruleId: string; reason: string }>;\n summary: {\n totalGenerated: number;\n historicalCount: number;\n upcomingCount: number;\n skippedCount: number;\n };\n}\n\n/** Default lookback cap: 3 years of historical deadlines. */\nconst DEFAULT_HISTORICAL_YEARS = 3;\n\n/** Default lookahead: 1 year of future deadlines. */\nconst DEFAULT_FUTURE_YEARS = 1;\n\n/**\n * Generate concrete deadline dates for applicable rules.\n *\n * @param evaluatedRules - Rules that passed applicability (status === 'applicable')\n * @param ruleDetails - Full rule data keyed by rule_id (must include deadline object)\n * @param company - Company formation date and fiscal year\n * @param now - Reference date (defaults to today)\n */\nexport function generateDeadlines(\n evaluatedRules: EvaluatedRule[],\n ruleDetails: Map<string, { deadline: RuleDeadline }>,\n company: CompanyContext,\n now: Date = new Date(),\n): DeadlineGenerationResult {\n const deadlines: GeneratedDeadline[] = [];\n const skipped: Array<{ ruleId: string; reason: string }> = [];\n\n const applicableRules = evaluatedRules.filter((r) => r.status === 'applicable');\n\n for (const rule of applicableRules) {\n const detail = ruleDetails.get(rule.ruleId);\n if (!detail) {\n skipped.push({ ruleId: rule.ruleId, reason: 'Rule detail not found.' });\n continue;\n }\n\n const generated = generateForRule(rule, detail.deadline, company, now);\n if (generated.length === 0) {\n skipped.push({ ruleId: rule.ruleId, reason: skipReasonForType(detail.deadline) });\n } else {\n deadlines.push(...generated);\n }\n }\n\n // Sort by dueDate ascending\n deadlines.sort((a, b) => a.dueDate.localeCompare(b.dueDate));\n\n const historicalCount = deadlines.filter((d) => d.isHistorical).length;\n\n return {\n deadlines,\n skipped,\n summary: {\n totalGenerated: deadlines.length,\n historicalCount,\n upcomingCount: deadlines.length - historicalCount,\n skippedCount: skipped.length,\n },\n };\n}\n\nfunction skipReasonForType(deadline: RuleDeadline): string {\n if (deadline.type === 'event_triggered') {\n return 'Event-triggered rule with no recurring deadline.';\n }\n if (deadline.type === 'relative_days') {\n return 'Relative-days rule requires a trigger event date.';\n }\n return 'Could not compute deadline dates.';\n}\n\n/**\n * Generate deadline dates for a single rule.\n */\nfunction generateForRule(\n rule: EvaluatedRule,\n deadline: RuleDeadline,\n company: CompanyContext,\n now: Date,\n): GeneratedDeadline[] {\n switch (deadline.type) {\n case 'fixed_date':\n return generateFixedDate(rule, deadline, company, now);\n case 'anniversary_month':\n return generateAnniversaryMonth(rule, deadline, company, now);\n case 'fiscal_year_relative':\n return generateFiscalYearRelative(rule, deadline, company, now);\n case 'quarterly_fixed_dates':\n return generateQuarterlyFixedDates(rule, deadline, company, now);\n case 'relative_days':\n // Relative-days rules (like 83(b) election) need a specific trigger date\n // which we don't have at generation time — skip\n return [];\n case 'event_triggered':\n // Event-triggered rules with trigger_event \"none\" = no filing required\n // Other event-triggered rules need a trigger date — skip\n return [];\n default:\n return [];\n }\n}\n\n// --- Date computation helpers ---\n\n/**\n * Get the start date for deadline generation.\n * Earliest of: formation date or (now - historical years).\n */\nfunction getStartDate(company: CompanyContext, now: Date): Date {\n const historicalLimit = new Date(now.getFullYear() - DEFAULT_HISTORICAL_YEARS, now.getMonth(), now.getDate());\n\n if (company.formationDate) {\n const formation = parseDate(company.formationDate);\n // Use the later of formation date or historical limit\n return formation > historicalLimit ? formation : historicalLimit;\n }\n\n return historicalLimit;\n}\n\nfunction getEndDate(now: Date): Date {\n return new Date(now.getFullYear() + DEFAULT_FUTURE_YEARS, now.getMonth(), now.getDate());\n}\n\n/** Parse ISO date string to Date (UTC). */\nfunction parseDate(dateStr: string): Date {\n const [y, m, d] = dateStr.split('-').map(Number) as [number, number, number];\n return new Date(y, m - 1, d);\n}\n\n/** Format Date to ISO date string \"YYYY-MM-DD\". */\nfunction formatDate(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n\n/** Get last day of a given month/year. Handles leap years. */\nfunction lastDayOfMonth(year: number, month: number): number {\n // month is 1-indexed here\n return new Date(year, month, 0).getDate();\n}\n\n/** Clamp a day to the valid range for a month. */\nfunction clampDay(year: number, month: number, day: number): number {\n const maxDay = lastDayOfMonth(year, month);\n return Math.min(day, maxDay);\n}\n\nfunction makeDeadline(\n rule: EvaluatedRule,\n dueDate: string,\n now: Date,\n): GeneratedDeadline {\n const isHistorical = dueDate < formatDate(now);\n return {\n ruleId: rule.ruleId,\n filingName: rule.filingName,\n jurisdiction: rule.jurisdiction,\n category: rule.category,\n dueDate,\n frequency: rule.frequency,\n coverageTier: rule.coverageTier,\n status: isHistorical ? 'unknown' : 'upcoming',\n isHistorical,\n };\n}\n\n// --- Type-specific generators ---\n\n/**\n * fixed_date: Same calendar date every year.\n * Uses due_date_month and due_date_day.\n * Example: DE franchise tax = March 1 every year.\n */\nfunction generateFixedDate(\n rule: EvaluatedRule,\n deadline: RuleDeadline,\n company: CompanyContext,\n now: Date,\n): GeneratedDeadline[] {\n if (deadline.due_date_month == null || deadline.due_date_day == null) return [];\n\n const day = typeof deadline.due_date_day === 'number' ? deadline.due_date_day : parseInt(deadline.due_date_day, 10);\n if (isNaN(day)) return [];\n\n const month = deadline.due_date_month; // 1-indexed\n\n const start = getStartDate(company, now);\n const end = getEndDate(now);\n const results: GeneratedDeadline[] = [];\n\n const step = deadline.frequency === 'biennial' ? 2 : 1;\n\n for (let year = start.getFullYear(); year <= end.getFullYear(); year += step) {\n const clampedDay = clampDay(year, month, day);\n const date = new Date(year, month - 1, clampedDay);\n\n if (date >= start && date <= end) {\n results.push(makeDeadline(rule, formatDate(date), now));\n }\n }\n\n return results;\n}\n\n/**\n * anniversary_month: Due in the same month as formation, every year (or biennially).\n * due_date_day can be a number or \"last_day\".\n * Example: CO periodic report = last day of anniversary month.\n */\nfunction generateAnniversaryMonth(\n rule: EvaluatedRule,\n deadline: RuleDeadline,\n company: CompanyContext,\n now: Date,\n): GeneratedDeadline[] {\n if (!company.formationDate) return [];\n\n const formation = parseDate(company.formationDate);\n const anniversaryMonth = formation.getMonth() + 1; // 1-indexed\n\n const start = getStartDate(company, now);\n const end = getEndDate(now);\n const results: GeneratedDeadline[] = [];\n\n const step = deadline.frequency === 'biennial' ? 2 : 1;\n\n // Start from formation year + step (first deadline is step years after formation)\n const firstYear = formation.getFullYear() + step;\n\n for (let year = firstYear; year <= end.getFullYear(); year += step) {\n const day = resolveDay(deadline.due_date_day, year, anniversaryMonth);\n const date = new Date(year, anniversaryMonth - 1, day);\n\n if (date >= start && date <= end) {\n results.push(makeDeadline(rule, formatDate(date), now));\n }\n }\n\n return results;\n}\n\n/**\n * fiscal_year_relative: Offset from fiscal year end.\n * due_date_offset_months = months after FYE, due_date_day = day of that month.\n * Example: Form 1120 = 4 months + 15th day after fiscal year end.\n *\n * For calendar-year companies (FYE = 12-31), Form 1120 is due April 15.\n * For June 30 FYE, it's due October 15.\n */\nfunction generateFiscalYearRelative(\n rule: EvaluatedRule,\n deadline: RuleDeadline,\n company: CompanyContext,\n now: Date,\n): GeneratedDeadline[] {\n if (deadline.due_date_offset_months == null || deadline.due_date_day == null) return [];\n\n const day = typeof deadline.due_date_day === 'number' ? deadline.due_date_day : parseInt(deadline.due_date_day, 10);\n if (isNaN(day)) return [];\n\n // Parse fiscal year end (default to 12-31 if not set)\n const fyeStr = company.fiscalYearEnd ?? '12-31';\n const [fyeMonth] = fyeStr.split('-').map(Number) as [number, number];\n\n const offsetMonths = deadline.due_date_offset_months;\n\n const start = getStartDate(company, now);\n const end = getEndDate(now);\n const results: GeneratedDeadline[] = [];\n\n // For each fiscal year, compute the deadline\n // Fiscal year Y ends on fyeMonth/fyeDay of year Y (for calendar year, Dec 31 of Y)\n // The deadline is offsetMonths later\n for (let fyYear = start.getFullYear() - 1; fyYear <= end.getFullYear(); fyYear++) {\n // The fiscal year ending in fyYear\n const deadlineMonth = ((fyeMonth - 1 + offsetMonths) % 12) + 1;\n const deadlineYear = fyYear + Math.floor((fyeMonth - 1 + offsetMonths) / 12);\n const clampedDay = clampDay(deadlineYear, deadlineMonth, day);\n const date = new Date(deadlineYear, deadlineMonth - 1, clampedDay);\n\n if (date >= start && date <= end) {\n results.push(makeDeadline(rule, formatDate(date), now));\n }\n }\n\n return results;\n}\n\n/**\n * quarterly_fixed_dates: 4 fixed dates per year.\n * quarterly_dates = { q1: \"MM-DD\", q2: \"MM-DD\", q3: \"MM-DD\", q4: \"MM-DD\" }\n * Example: CA EDD = 04-30, 07-31, 10-31, 01-31\n */\nfunction generateQuarterlyFixedDates(\n rule: EvaluatedRule,\n deadline: RuleDeadline,\n company: CompanyContext,\n now: Date,\n): GeneratedDeadline[] {\n if (!deadline.quarterly_dates) return [];\n\n const start = getStartDate(company, now);\n const end = getEndDate(now);\n const results: GeneratedDeadline[] = [];\n\n const quarters = [\n deadline.quarterly_dates.q1,\n deadline.quarterly_dates.q2,\n deadline.quarterly_dates.q3,\n deadline.quarterly_dates.q4,\n ];\n\n for (let year = start.getFullYear(); year <= end.getFullYear(); year++) {\n for (const qDate of quarters) {\n const [qMonth, qDay] = qDate.split('-').map(Number) as [number, number];\n // Q4 due date (e.g., 01-31) falls in the next year\n // Heuristic: if Q4 month < Q1 month, it's next year\n let dateYear = year;\n if (qDate === deadline.quarterly_dates.q4) {\n const q1Month = parseInt(deadline.quarterly_dates.q1.split('-')[0]!, 10);\n if (qMonth < q1Month) {\n dateYear = year + 1;\n }\n }\n\n const clampedDay = clampDay(dateYear, qMonth, qDay);\n const date = new Date(dateYear, qMonth - 1, clampedDay);\n\n if (date >= start && date <= end) {\n results.push(makeDeadline(rule, formatDate(date), now));\n }\n }\n }\n\n return results;\n}\n\n/** Resolve due_date_day which can be a number or \"last_day\". */\nfunction resolveDay(dueDateDay: number | string | null, year: number, month: number): number {\n if (dueDateDay === 'last_day') {\n return lastDayOfMonth(year, month);\n }\n if (typeof dueDateDay === 'number') {\n return clampDay(year, month, dueDateDay);\n }\n if (typeof dueDateDay === 'string') {\n const parsed = parseInt(dueDateDay, 10);\n if (!isNaN(parsed)) return clampDay(year, month, parsed);\n }\n // Default to last day if unspecified\n return lastDayOfMonth(year, month);\n}\n","/**\n * domain/compliance/severity.ts — Overdue severity computation.\n *\n * Pure functions (no DB). Severity is computed at query time so it's always fresh.\n *\n * Thresholds:\n * <7 days overdue → 'medium'\n * 7-30 days overdue → 'high'\n * >30 days overdue → 'critical'\n */\n\nimport type { DeadlineRecord } from '../../db/deadlines.js';\n\nexport type Severity = 'critical' | 'high' | 'medium';\n\nexport interface PenaltyInfo {\n lateFiling: string | null;\n interest: string | null;\n escalation: string | null;\n}\n\nexport interface OverdueInfo {\n severity: Severity;\n daysOverdue: number;\n penalties: PenaltyInfo | null;\n portalUrl: string | null;\n remediationSummary: string | null;\n upgradePrompt: string | null;\n}\n\n/**\n * Check if a due date is in the past (strictly before today).\n * \"Today\" means the deadline is not yet overdue.\n */\nexport function isOverdue(dueDate: string, now?: Date): boolean {\n const today = now ?? new Date();\n const due = new Date(dueDate + 'T00:00:00');\n // Strip time from \"now\" to compare dates only\n const todayDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());\n return due < todayDate;\n}\n\n/**\n * Compute the number of calendar days a deadline is overdue.\n * Returns 0 if not overdue.\n */\nexport function daysOverdue(dueDate: string, now?: Date): number {\n const today = now ?? new Date();\n const due = new Date(dueDate + 'T00:00:00');\n const todayDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());\n const diffMs = todayDate.getTime() - due.getTime();\n if (diffMs <= 0) return 0;\n return Math.floor(diffMs / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Compute severity level from a due date.\n * Returns null if the deadline is not overdue.\n */\nexport function computeSeverity(dueDate: string, now?: Date): Severity | null {\n const days = daysOverdue(dueDate, now);\n if (days <= 0) return null;\n if (days > 30) return 'critical';\n if (days >= 7) return 'high';\n return 'medium';\n}\n\n/**\n * Numeric severity rank for sorting (higher = more severe).\n */\nexport function severityRank(severity: Severity): number {\n switch (severity) {\n case 'critical': return 3;\n case 'high': return 2;\n case 'medium': return 1;\n }\n}\n\n/**\n * Extract penalty info from a rule JSON's penalties object.\n */\nfunction extractPenalties(rule: Record<string, unknown>): PenaltyInfo | null {\n const penalties = rule.penalties as Record<string, unknown> | undefined;\n if (!penalties) return null;\n\n const lateFiling = penalties.late_filing as Record<string, unknown> | undefined;\n const interest = penalties.interest as Record<string, unknown> | undefined;\n const escalation = penalties.escalation as Array<Record<string, unknown>> | undefined;\n\n const lateFilingDesc = (lateFiling?.description as string) ?? null;\n const interestDesc = (interest?.description as string) ?? null;\n const escalationDesc = escalation?.[0]?.consequence as string ?? null;\n\n if (!lateFilingDesc && !interestDesc && !escalationDesc) return null;\n\n return {\n lateFiling: lateFilingDesc,\n interest: interestDesc,\n escalation: escalationDesc,\n };\n}\n\n/**\n * Extract portal URL from a rule JSON's filing_portal object.\n */\nfunction extractPortalUrl(rule: Record<string, unknown>): string | null {\n const portal = rule.filing_portal as Record<string, unknown> | undefined;\n return (portal?.url as string) ?? null;\n}\n\n/**\n * Extract remediation summary from a rule JSON.\n * Uses the linked_remediation_guide filename as a brief reference.\n */\nfunction extractRemediationSummary(rule: Record<string, unknown>): string | null {\n const guide = rule.linked_remediation_guide as string | undefined;\n if (!guide) return null;\n\n const hasCure = (rule.penalties as Record<string, unknown> | undefined)?.has_cure;\n const prefix = hasCure ? 'Cure available.' : '';\n return `${prefix} See remediation guide: ${guide}`.trim();\n}\n\n/**\n * Build enriched overdue info for a deadline.\n * Free-tier users get an upgrade prompt instead of remediation details.\n */\nexport function computeOverdueInfo(\n deadline: DeadlineRecord,\n rule: Record<string, unknown>,\n tier: 'free' | 'paid',\n now?: Date,\n): OverdueInfo | null {\n const severity = computeSeverity(deadline.dueDate, now);\n if (!severity) return null;\n\n const days = daysOverdue(deadline.dueDate, now);\n const penalties = extractPenalties(rule);\n const portalUrl = extractPortalUrl(rule);\n\n const isPaid = tier === 'paid';\n const remediationSummary = isPaid ? extractRemediationSummary(rule) : null;\n\n let upgradePrompt: string | null = null;\n if (!isPaid) {\n const penaltyHint = penalties?.lateFiling\n ? ` Late fee: ${penalties.lateFiling}.`\n : '';\n upgradePrompt = `This filing is ${days} day${days !== 1 ? 's' : ''} overdue.${penaltyHint} Upgrade to get step-by-step remediation guidance.`;\n }\n\n return {\n severity,\n daysOverdue: days,\n penalties,\n portalUrl,\n remediationSummary,\n upgradePrompt,\n };\n}\n","/**\n * db/deadlines.ts — Compliance deadline + filing history CRUD.\n *\n * Follows companies.ts pattern: camelCase mapped from snake_case,\n * supabase client passed as parameter, null for not-found.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface DeadlineRecord {\n id: string;\n userId: string;\n companyId: string;\n ruleId: string;\n ruleVersion: string | null;\n filingName: string;\n jurisdiction: string;\n category: string;\n dueDate: string;\n frequency: string;\n coverageTier: string;\n status: string;\n severity: string | null;\n suppressionReason: string | null;\n completedAt: string | null;\n calendarEventId: string | null;\n calendarSyncedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface FilingHistoryRecord {\n id: string;\n userId: string;\n companyId: string;\n deadlineId: string;\n filingDate: string;\n confirmationNumber: string | null;\n amountPaid: number | null;\n documentId: string | null;\n notes: string | null;\n voided: boolean;\n createdAt: string;\n}\n\nexport interface DeadlineUpsertInput {\n userId: string;\n companyId: string;\n ruleId: string;\n ruleVersion?: string;\n filingName: string;\n jurisdiction: string;\n category: string;\n dueDate: string;\n frequency: string;\n coverageTier: string;\n status?: string;\n}\n\nexport interface DeadlineFilters {\n status?: string;\n category?: string;\n severity?: string;\n}\n\nexport interface DeadlineListResult {\n items: DeadlineRecord[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\nexport interface FilingHistoryCreateInput {\n userId: string;\n companyId: string;\n deadlineId: string;\n filingDate: string;\n confirmationNumber?: string;\n amountPaid?: number;\n documentId?: string;\n notes?: string;\n}\n\nexport interface FilingHistoryListResult {\n items: FilingHistoryRecord[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n// --- Mappers ---\n\nfunction mapDeadlineRow(row: Record<string, unknown>): DeadlineRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n companyId: row.company_id as string,\n ruleId: row.rule_id as string,\n ruleVersion: (row.rule_version as string) ?? null,\n filingName: row.filing_name as string,\n jurisdiction: row.jurisdiction as string,\n category: row.category as string,\n dueDate: row.due_date as string,\n frequency: row.frequency as string,\n coverageTier: row.coverage_tier as string,\n status: row.status as string,\n severity: (row.severity as string) ?? null,\n suppressionReason: (row.suppression_reason as string) ?? null,\n completedAt: (row.completed_at as string) ?? null,\n calendarEventId: (row.calendar_event_id as string) ?? null,\n calendarSyncedAt: (row.calendar_synced_at as string) ?? null,\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\nfunction mapFilingHistoryRow(row: Record<string, unknown>): FilingHistoryRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n companyId: row.company_id as string,\n deadlineId: row.deadline_id as string,\n filingDate: row.filing_date as string,\n confirmationNumber: (row.confirmation_number as string) ?? null,\n amountPaid: (row.amount_paid as number) ?? null,\n documentId: (row.document_id as string) ?? null,\n notes: (row.notes as string) ?? null,\n voided: row.voided as boolean,\n createdAt: row.created_at as string,\n };\n}\n\n// --- Deadline CRUD ---\n\n/**\n * Bulk upsert deadlines. Uses ON CONFLICT to skip duplicates.\n * Supabase JS .upsert() with onConflict maps to INSERT ... ON CONFLICT DO UPDATE.\n */\nexport async function upsertDeadlines(\n supabase: TypedSupabaseClient,\n deadlines: DeadlineUpsertInput[],\n): Promise<DeadlineRecord[]> {\n if (deadlines.length === 0) return [];\n\n const rows = deadlines.map((d) => ({\n user_id: d.userId,\n company_id: d.companyId,\n rule_id: d.ruleId,\n rule_version: d.ruleVersion ?? null,\n filing_name: d.filingName,\n jurisdiction: d.jurisdiction,\n category: d.category,\n due_date: d.dueDate,\n frequency: d.frequency,\n coverage_tier: d.coverageTier,\n status: d.status ?? 'pending',\n }));\n\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .upsert(rows, { onConflict: 'company_id,rule_id,due_date' })\n .select();\n\n if (error) throw error;\n\n return (data ?? []).map((row) => mapDeadlineRow(row as unknown as Record<string, unknown>));\n}\n\nexport async function getDeadlineById(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n): Promise<DeadlineRecord | null> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .select('*')\n .eq('id', deadlineId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapDeadlineRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function listDeadlines(\n supabase: TypedSupabaseClient,\n companyId: string,\n filters: DeadlineFilters = {},\n limit = 25,\n offset = 0,\n): Promise<DeadlineListResult> {\n let query = supabase\n .from('compliance_deadlines')\n .select('*', { count: 'exact' })\n .eq('company_id', companyId);\n\n if (filters.status && filters.status !== 'all') {\n query = query.eq('status', filters.status);\n }\n if (filters.category) {\n query = query.eq('category', filters.category);\n }\n if (filters.severity) {\n query = query.eq('severity', filters.severity);\n }\n\n const { data, error, count } = await query\n .order('due_date', { ascending: true })\n .range(offset, offset + limit - 1);\n\n if (error) throw error;\n\n const items = (data ?? []).map((row) => mapDeadlineRow(row as unknown as Record<string, unknown>));\n const total = count ?? items.length;\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n}\n\n/**\n * Mark a deadline as completed. Sets status + completed_at.\n * Returns the updated row.\n */\nexport async function completeDeadline(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n completedAt: string,\n): Promise<DeadlineRecord> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .update({ status: 'completed', completed_at: completedAt })\n .eq('id', deadlineId)\n .select()\n .single();\n\n if (error) throw error;\n\n return mapDeadlineRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Delete all deadlines for a company (for forceRegenerate).\n */\nexport async function deleteDeadlinesByCompany(\n supabase: TypedSupabaseClient,\n companyId: string,\n): Promise<void> {\n const { error } = await supabase\n .from('compliance_deadlines')\n .delete()\n .eq('company_id', companyId)\n .eq('status', 'pending');\n\n if (error) throw error;\n}\n\n/**\n * Update a single deadline's status and severity.\n */\nexport async function updateDeadlineSeverity(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n status: string,\n severity: string,\n): Promise<DeadlineRecord> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .update({ status, severity })\n .eq('id', deadlineId)\n .select()\n .single();\n\n if (error) throw error;\n\n return mapDeadlineRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Batch-mark deadlines as overdue with severity.\n * Returns count of rows updated.\n */\nexport async function markOverdueDeadlines(\n supabase: TypedSupabaseClient,\n companyId: string,\n updates: Array<{ id: string; severity: string }>,\n): Promise<number> {\n if (updates.length === 0) return 0;\n\n // Update each deadline individually (Supabase JS doesn't support batch conditional updates).\n // For MVP volumes (<100 deadlines per company) this is acceptable.\n let count = 0;\n for (const u of updates) {\n const { error } = await supabase\n .from('compliance_deadlines')\n .update({ status: 'overdue', severity: u.severity })\n .eq('id', u.id)\n .eq('company_id', companyId);\n\n if (error) throw error;\n count++;\n }\n\n return count;\n}\n\n// --- Suppression & Reopen ---\n\n/**\n * Suppress all pending/overdue deadlines for a given company+rule.\n * Sets status='suppressed' and stores the reason.\n * Returns count of affected rows.\n */\nexport async function suppressDeadlinesByRule(\n supabase: TypedSupabaseClient,\n companyId: string,\n ruleId: string,\n reason: string,\n): Promise<{ suppressed: true; count: number }> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .update({ status: 'suppressed', suppression_reason: reason })\n .eq('company_id', companyId)\n .eq('rule_id', ruleId)\n .in('status', ['pending', 'overdue'])\n .select('id');\n\n if (error) throw error;\n\n return { suppressed: true, count: (data ?? []).length };\n}\n\n/**\n * Restore all suppressed deadlines for a given company+rule.\n * Sets status='pending' and clears suppression_reason.\n * Returns count of affected rows.\n */\nexport async function unsuppressDeadlinesByRule(\n supabase: TypedSupabaseClient,\n companyId: string,\n ruleId: string,\n): Promise<{ restored: true; count: number }> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .update({ status: 'pending', suppression_reason: null })\n .eq('company_id', companyId)\n .eq('rule_id', ruleId)\n .eq('status', 'suppressed')\n .select('id');\n\n if (error) throw error;\n\n return { restored: true, count: (data ?? []).length };\n}\n\n/**\n * Suppress a single deadline by ID.\n * Used to suppress auto-generated next occurrence after reopen.\n */\nexport async function suppressDeadline(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n reason: string,\n): Promise<DeadlineRecord> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .update({ status: 'suppressed', suppression_reason: reason })\n .eq('id', deadlineId)\n .select()\n .single();\n\n if (error) throw error;\n\n return mapDeadlineRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Reopen a completed deadline: set pending, clear completed_at/severity/suppression_reason.\n */\nexport async function reopenDeadline(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n): Promise<DeadlineRecord> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .update({\n status: 'pending',\n completed_at: null,\n severity: null,\n suppression_reason: null,\n })\n .eq('id', deadlineId)\n .select()\n .single();\n\n if (error) throw error;\n\n return mapDeadlineRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Void all non-voided filing history records for a deadline.\n * Returns count of voided records.\n */\nexport async function voidFilingHistory(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n): Promise<number> {\n const { data, error } = await supabase\n .from('filing_history')\n .update({ voided: true })\n .eq('deadline_id', deadlineId)\n .eq('voided', false)\n .select('id');\n\n if (error) throw error;\n\n return (data ?? []).length;\n}\n\n/**\n * Find the next pending deadline for the same company+rule after a given due date.\n * Used by reopen to suppress auto-generated next occurrence.\n */\nexport async function findNextOccurrenceDeadline(\n supabase: TypedSupabaseClient,\n companyId: string,\n ruleId: string,\n afterDueDate: string,\n): Promise<DeadlineRecord | null> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .select('*')\n .eq('company_id', companyId)\n .eq('rule_id', ruleId)\n .eq('status', 'pending')\n .gt('due_date', afterDueDate)\n .order('due_date', { ascending: true })\n .limit(1)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapDeadlineRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Get distinct active (pending/overdue) rule IDs for a company.\n * Used to diff before/after reevaluation for stale deadline suppression.\n */\nexport async function getActiveRuleIdsForCompany(\n supabase: TypedSupabaseClient,\n companyId: string,\n): Promise<Set<string>> {\n const { data, error } = await supabase\n .from('compliance_deadlines')\n .select('rule_id')\n .eq('company_id', companyId)\n .in('status', ['pending', 'overdue']);\n\n if (error) throw error;\n\n const ruleIds = new Set<string>();\n for (const row of data ?? []) {\n ruleIds.add(row.rule_id as string);\n }\n return ruleIds;\n}\n\n// --- Calendar Sync ---\n\n/**\n * Set calendar_event_id and calendar_synced_at on a deadline.\n */\nexport async function updateCalendarEventId(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n calendarEventId: string,\n): Promise<void> {\n const { error } = await supabase\n .from('compliance_deadlines')\n .update({\n calendar_event_id: calendarEventId,\n calendar_synced_at: new Date().toISOString(),\n })\n .eq('id', deadlineId);\n\n if (error) throw error;\n}\n\n/**\n * Clear calendar_event_id and calendar_synced_at on a deadline.\n */\nexport async function clearCalendarEventId(\n supabase: TypedSupabaseClient,\n deadlineId: string,\n): Promise<void> {\n const { error } = await supabase\n .from('compliance_deadlines')\n .update({\n calendar_event_id: null,\n calendar_synced_at: null,\n })\n .eq('id', deadlineId);\n\n if (error) throw error;\n}\n\n// --- Filing History CRUD ---\n\nexport async function createFilingHistory(\n supabase: TypedSupabaseClient,\n input: FilingHistoryCreateInput,\n): Promise<FilingHistoryRecord> {\n const { data, error } = await supabase\n .from('filing_history')\n .insert({\n user_id: input.userId,\n company_id: input.companyId,\n deadline_id: input.deadlineId,\n filing_date: input.filingDate,\n confirmation_number: input.confirmationNumber ?? null,\n amount_paid: input.amountPaid ?? null,\n document_id: input.documentId ?? null,\n notes: input.notes ?? null,\n })\n .select()\n .single();\n\n if (error) throw error;\n\n return mapFilingHistoryRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function listFilingHistory(\n supabase: TypedSupabaseClient,\n companyId: string,\n limit = 25,\n offset = 0,\n): Promise<FilingHistoryListResult> {\n const { data, error, count } = await supabase\n .from('filing_history')\n .select('*', { count: 'exact' })\n .eq('company_id', companyId)\n .eq('voided', false)\n .order('filing_date', { ascending: false })\n .range(offset, offset + limit - 1);\n\n if (error) throw error;\n\n const items = (data ?? []).map((row) => mapFilingHistoryRow(row as unknown as Record<string, unknown>));\n const total = count ?? items.length;\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n}\n","/**\n * tools/ff-company.ts — ff_company tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n */\n\nimport type { SessionContext } from '../auth/session.js';\nimport { FfCompanySchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { validateCompanyOwnership } from '../auth/ownership.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport {\n createCompany,\n getCompanyById,\n listCompanies,\n updateCompany,\n addCompanyNote,\n type CompanyRecord,\n} from '../db/companies.js';\nimport { computeCompleteness } from '../db/completeness.js';\nimport { getDocumentById } from '../db/documents.js';\nimport { loadRuleIndex, loadFullRule } from '../domain/compliance/rule-loader.js';\nimport { evaluateRules } from '../domain/compliance/rule-evaluator.js';\nimport {\n generateDeadlines,\n type RuleDeadline,\n type CompanyContext,\n} from '../domain/compliance/deadline-generator.js';\nimport { isOverdue, computeSeverity } from '../domain/compliance/severity.js';\nimport {\n upsertDeadlines,\n markOverdueDeadlines,\n suppressDeadlinesByRule,\n getActiveRuleIdsForCompany,\n} from '../db/deadlines.js';\n\nconst TOOL_NAME = 'ff_company';\n\n/** Trigger fields that affect compliance calendar when changed. */\nconst TRIGGER_FIELDS = new Set([\n 'stateOfFormation',\n 'entityType',\n 'hasEmployees',\n 'hasPayroll',\n 'foreignRegistrations',\n 'fiscalYearEnd',\n 'estimatedTaxCadence',\n 'usesContractors',\n 'dataPrivacyProgramRequired',\n 'ipGovernanceRequired',\n]);\n\n/** Fields whose changes require filing amendments (FR83). */\nconst AMENDMENT_TRIGGER_FIELDS: Record<string, string> = {\n registeredAgentName: 'File Statement of Change with SOS within 30 days.',\n registeredAgentAddress: 'File Statement of Change with SOS within 30 days.',\n fiscalYearEnd: 'May require IRS notification (Form 1128/8716).',\n};\n\n/** Detect amendment notices for changed fields. Pure function. */\nexport function detectAmendmentNotices(changedFields: string[]): string[] {\n const notices: string[] = [];\n const seen = new Set<string>();\n for (const field of changedFields) {\n const notice = AMENDMENT_TRIGGER_FIELDS[field];\n if (notice && !seen.has(notice)) {\n seen.add(notice);\n notices.push(notice);\n }\n }\n return notices;\n}\n\n/** Result of automatic reevaluation after trigger field changes. */\nexport interface ReevaluationResult {\n generated: number;\n suppressed: number;\n overdueMarked: number;\n newlyApplicableRuleIds: string[];\n newlySuppressedRuleIds: string[];\n}\n\n/**\n * Execute the full reevaluation pipeline after a company update changes trigger fields.\n * 1. Snapshot active rule IDs before\n * 2. Run evaluation pipeline (loadRuleIndex → evaluateRules → loadFullRules → generateDeadlines)\n * 3. Upsert new deadlines (ON CONFLICT = no duplicates)\n * 4. Overdue detection pass\n * 5. Stale suppression: suppress rules that went from applicable → suppressed\n */\nasync function executeReevaluation(\n ctx: SessionContext,\n company: CompanyRecord,\n changedFields: string[],\n): Promise<ReevaluationResult> {\n // 1. Snapshot active rule IDs before reevaluation\n const beforeRuleIds = await getActiveRuleIdsForCompany(ctx.supabase, company.id);\n\n // 2. Run full evaluation pipeline\n const ruleIndex = await loadRuleIndex();\n const evaluated = evaluateRules(ruleIndex, company);\n\n // Load full rule details for applicable rules\n const applicableRules = evaluated.filter((r) => r.status === 'applicable');\n const ruleDetails = new Map<string, { deadline: RuleDeadline }>();\n\n await Promise.all(\n applicableRules.map(async (r) => {\n try {\n const fullRule = await loadFullRule(r.ruleId);\n if (fullRule.deadline) {\n ruleDetails.set(r.ruleId, { deadline: fullRule.deadline as RuleDeadline });\n }\n } catch {\n // Rule file missing — skip\n }\n }),\n );\n\n // Generate deadlines\n const companyContext: CompanyContext = {\n formationDate: company.formationDate,\n fiscalYearEnd: company.fiscalYearEnd,\n };\n const result = generateDeadlines(evaluated, ruleDetails, companyContext);\n\n // 3. Upsert new deadlines (ON CONFLICT prevents duplicates)\n const upsertInput = result.deadlines.map((d) => ({\n userId: ctx.userId,\n companyId: company.id,\n ruleId: d.ruleId,\n filingName: d.filingName,\n jurisdiction: d.jurisdiction,\n category: d.category,\n dueDate: d.dueDate,\n frequency: d.frequency,\n coverageTier: d.coverageTier,\n status: d.status === 'unknown' ? 'pending' : d.status,\n }));\n\n const persisted = await upsertDeadlines(ctx.supabase, upsertInput);\n\n // 4. Overdue detection pass\n const overdueUpdates: Array<{ id: string; severity: string }> = [];\n for (const d of persisted) {\n if (d.status === 'pending' && isOverdue(d.dueDate)) {\n const severity = computeSeverity(d.dueDate);\n if (severity) {\n overdueUpdates.push({ id: d.id, severity });\n }\n }\n }\n const overdueMarked = await markOverdueDeadlines(ctx.supabase, company.id, overdueUpdates);\n\n // 5. Stale suppression: find rules that were active before but are now suppressed\n const nowSuppressedRuleIds = new Set(\n evaluated.filter((r) => r.status === 'suppressed').map((r) => r.ruleId),\n );\n\n const newlySuppressedRuleIds: string[] = [];\n for (const ruleId of beforeRuleIds) {\n if (nowSuppressedRuleIds.has(ruleId)) {\n const reason = `Trigger field change (${changedFields.join(', ')}) — rule no longer applicable.`;\n await suppressDeadlinesByRule(ctx.supabase, company.id, ruleId, reason);\n newlySuppressedRuleIds.push(ruleId);\n }\n }\n\n // Compute newly applicable rule IDs (weren't in before snapshot)\n const afterApplicableRuleIds = new Set(applicableRules.map((r) => r.ruleId));\n const newlyApplicableRuleIds: string[] = [];\n for (const ruleId of afterApplicableRuleIds) {\n if (!beforeRuleIds.has(ruleId)) {\n newlyApplicableRuleIds.push(ruleId);\n }\n }\n\n return {\n generated: persisted.length,\n suppressed: newlySuppressedRuleIds.length,\n overdueMarked,\n newlyApplicableRuleIds,\n newlySuppressedRuleIds,\n };\n}\n\nexport async function handleFfCompany(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n _extra?: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfCompanySchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Steps 3-5: Authorize → Execute → Respond (per action)\n switch (input.action) {\n case 'create': {\n const company = await createCompany(ctx.supabase, {\n userId: ctx.userId,\n name: input.name,\n entityType: input.entityType,\n stateOfFormation: input.stateOfFormation,\n formationDate: input.formationDate,\n timezone: input.timezone,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'create',\n companyId: company.id,\n input: { name: input.name, entityType: input.entityType, stateOfFormation: input.stateOfFormation },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: company,\n message: `Company \"${company.name}\" created successfully.`,\n nextAction: { tool: TOOL_NAME, action: 'update', params: { companyId: company.id } },\n prompt: 'Add officers, registered agent, and other details to complete the company profile.',\n });\n }\n\n case 'get': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const company = await getCompanyById(ctx.supabase, input.companyId);\n if (!company) throw ffError.notFound('company', input.companyId);\n\n const completeness = computeCompleteness(company);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'get',\n companyId: input.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse(\n {\n data: { ...company, completeness },\n message: `Company \"${company.name}\" retrieved. Profile ${completeness.score}% complete.`,\n ...(completeness.missing.length > 0 && {\n prompt: `Missing fields: ${completeness.missing.map((f) => f.label).join(', ')}. Use ff_company(action: update) to fill them in.`,\n }),\n },\n { showSensitive: input.showSensitive },\n );\n }\n\n case 'list': {\n const result = await listCompanies(ctx.supabase, input.limit, input.offset);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'list',\n input: { limit: input.limit, offset: input.offset },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: result,\n message: `Found ${result.total} ${result.total === 1 ? 'company' : 'companies'}.`,\n });\n }\n\n case 'update': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const updateFields = Object.fromEntries(\n Object.entries(input).filter(([k]) => k !== 'action' && k !== 'companyId'),\n );\n\n // Validate document link if setting formationDocumentId\n if (updateFields.formationDocumentId) {\n const doc = await getDocumentById(ctx.supabase, updateFields.formationDocumentId as string);\n if (!doc) throw ffError.notFound('document', updateFields.formationDocumentId as string);\n if (doc.companyId !== input.companyId) throw ffError.conflict('document', 'Document belongs to a different company.');\n }\n\n const changedTriggers = Object.keys(updateFields).filter((k) => TRIGGER_FIELDS.has(k));\n const amendmentNotices = detectAmendmentNotices(Object.keys(updateFields));\n\n const company = await updateCompany(ctx.supabase, input.companyId, updateFields);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'update',\n companyId: input.companyId,\n input: updateFields as Record<string, unknown>,\n result: 'success',\n });\n\n // Auto-reevaluation on trigger field changes\n let reevaluation: ReevaluationResult | undefined;\n let reevalFailed = false;\n if (changedTriggers.length > 0) {\n try {\n reevaluation = await executeReevaluation(ctx, company, changedTriggers);\n } catch (err) {\n logger.warn('Reevaluation failed, falling back to advisory', { error: err });\n reevalFailed = true;\n }\n }\n\n // Build response\n const responseData: Record<string, unknown> = { ...company };\n if (reevaluation) {\n responseData.reevaluation = reevaluation;\n }\n if (amendmentNotices.length > 0) {\n responseData.amendmentNotices = amendmentNotices;\n }\n\n const triggerNote =\n changedTriggers.length > 0\n ? ` Trigger fields changed: ${changedTriggers.join(', ')}.`\n : '';\n const reevalNote = reevaluation\n ? ` Reevaluation complete: ${reevaluation.generated} generated, ${reevaluation.suppressed} suppressed, ${reevaluation.overdueMarked} overdue.`\n : '';\n\n done();\n return formatToolResponse({\n data: responseData,\n message: `Company \"${company.name}\" updated.${triggerNote}${reevalNote}`,\n // On reevaluation success → next action is score. On failure → advisory fallback.\n ...(changedTriggers.length > 0 && !reevalFailed && {\n nextAction: {\n tool: 'ff_compliance',\n action: 'score',\n params: { companyId: company.id },\n },\n }),\n ...(reevalFailed && {\n nextAction: {\n tool: 'ff_compliance',\n action: 'generate_deadlines',\n params: { companyId: company.id },\n },\n prompt: 'Automatic reevaluation failed. Run compliance deadline generation manually to update the calendar.',\n }),\n });\n }\n\n case 'add_note': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const company = await addCompanyNote(ctx.supabase, input.companyId, input.note);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'add_note',\n companyId: input.companyId,\n input: { note: input.note },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: company,\n message: `Note added to \"${company.name}\".`,\n });\n }\n\n case 'cross_entity_status': {\n // No company_id needed — aggregates across all user's companies\n const allCompanies = await listCompanies(ctx.supabase, 100, 0);\n\n const entities = allCompanies.items.map((c) => {\n const comp = computeCompleteness(c);\n return {\n id: c.id,\n name: c.name,\n entityType: c.entityType,\n stateOfFormation: c.stateOfFormation,\n status: c.status,\n completenessScore: comp.score,\n missingFieldCount: comp.missing.length,\n };\n });\n\n const avgScore =\n entities.length > 0\n ? Math.round(entities.reduce((sum, e) => sum + e.completenessScore, 0) / entities.length)\n : 0;\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'cross_entity_status',\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: {\n entityCount: entities.length,\n averageCompleteness: avgScore,\n entities,\n },\n message: `${entities.length} ${entities.length === 1 ? 'entity' : 'entities'}. Average profile completeness: ${avgScore}%.`,\n });\n }\n }\n } catch (error) {\n done(true);\n\n // Convert TierRestrictionError to FfError for proper formatting\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: 'TIER_RESTRICTED',\n });\n return formatErrorResponse(ffErr);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: error instanceof Error ? error.constructor.name : 'unknown',\n });\n\n return formatErrorResponse(error);\n }\n}\n","/**\n * tools/ff-scan.ts — ff_scan tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n * Scans local files and folders. Does NOT auto-upload to vault.\n */\n\nimport { readdir, stat } from 'node:fs/promises';\nimport { join, basename } from 'node:path';\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type { ServerRequest, ServerNotification } from '@modelcontextprotocol/sdk/types.js';\nimport type { SessionContext } from '../auth/session.js';\nimport { FfScanSchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { validateCompanyOwnership } from '../auth/ownership.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport { validateScanPath, validateFileSize, validateDirectory, getMaxFolderSize } from '../domain/scanning/path-validator.js';\nimport { extractText, isSupportedExtension, type ExtractionResult } from '../domain/scanning/extract-text.js';\n\ntype McpExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;\n\nconst TOOL_NAME = 'ff_scan';\n\nexport async function handleFfScan(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n extra?: McpExtra,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfScanSchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Step 3: Authorize (verify company ownership)\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Steps 4-5: Execute → Respond (per action)\n switch (input.action) {\n case 'file': {\n const resolvedPath = await validateScanPath(input.filePath);\n await validateFileSize(resolvedPath);\n\n if (!isSupportedExtension(resolvedPath)) {\n throw ffError.scanFailed(\n input.filePath,\n `Unsupported file type. Supported: PDF, DOCX, RTF, HTML, CSV, MD, TXT.`,\n );\n }\n\n const result = await extractText(resolvedPath);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'file',\n companyId: input.companyId,\n input: { filePath: basename(resolvedPath) },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: {\n fileName: basename(resolvedPath),\n ...result,\n },\n message: buildFileMessage(basename(resolvedPath), result),\n nextAction: { tool: 'ff_vault', action: 'upload', params: { companyId: input.companyId } },\n prompt: 'Would you like to save this document to your vault?',\n });\n }\n\n case 'folder': {\n const resolvedPath = await validateScanPath(input.folderPath);\n await validateDirectory(resolvedPath);\n\n const files = await collectFiles(resolvedPath, input.recursive);\n const maxFolderSize = getMaxFolderSize();\n\n // Validate total size\n let totalSize = 0;\n const validFiles: Array<{ path: string; size: number; name: string }> = [];\n for (const file of files) {\n const stats = await stat(file);\n totalSize += stats.size;\n if (totalSize > maxFolderSize) {\n throw ffError.scanFailed(\n input.folderPath,\n `Folder too large (${formatSize(totalSize)} total). Maximum is ${formatSize(maxFolderSize)}. Try scanning a smaller subset.`,\n );\n }\n validFiles.push({ path: file, size: stats.size, name: basename(file) });\n }\n\n // Extract text from each file\n const results: Array<{\n fileName: string;\n wordCount: number;\n method: string;\n error?: string;\n }> = [];\n let totalWords = 0;\n\n const progressToken = extra?._meta?.progressToken;\n\n for (let i = 0; i < validFiles.length; i++) {\n const file = validFiles[i]!;\n\n // Send progress notification if client supports it\n if (progressToken != null) {\n await extra!.sendNotification({\n method: 'notifications/progress',\n params: {\n progressToken,\n progress: i,\n total: validFiles.length,\n message: `Scanning ${file.name} (${i + 1}/${validFiles.length})`,\n },\n });\n }\n\n try {\n const result = await extractText(file.path);\n results.push({\n fileName: file.name,\n wordCount: result.wordCount,\n method: result.method,\n });\n totalWords += result.wordCount;\n } catch (err) {\n results.push({\n fileName: file.name,\n wordCount: 0,\n method: 'error',\n error: err instanceof Error ? err.message : 'Unknown error',\n });\n }\n }\n\n // Final progress notification\n if (progressToken != null) {\n await extra!.sendNotification({\n method: 'notifications/progress',\n params: {\n progressToken,\n progress: validFiles.length,\n total: validFiles.length,\n message: `Scan complete (${validFiles.length} files)`,\n },\n });\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'folder',\n companyId: input.companyId,\n input: { folderPath: basename(resolvedPath), fileCount: validFiles.length },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: {\n folderName: basename(resolvedPath),\n fileCount: validFiles.length,\n totalSize: formatSize(totalSize),\n totalWords,\n files: results,\n },\n message: `Scanned ${basename(resolvedPath)}/ (${validFiles.length} files). Extracted ${totalWords.toLocaleString()} words, ${formatSize(totalSize)} total.`,\n nextAction: { tool: 'ff_vault', action: 'upload', params: { companyId: input.companyId } },\n prompt: 'Ready to analyze. Would you like to save these documents to your vault?',\n });\n }\n }\n } catch (error) {\n done(true);\n\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: 'TIER_RESTRICTED',\n });\n return formatErrorResponse(ffErr);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: error instanceof Error ? error.constructor.name : 'unknown',\n });\n\n return formatErrorResponse(error);\n }\n}\n\n/**\n * Recursively collect supported files from a directory.\n */\nasync function collectFiles(dirPath: string, recursive: boolean): Promise<string[]> {\n const entries = await readdir(dirPath, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n\n if (entry.isFile() && isSupportedExtension(entry.name)) {\n files.push(fullPath);\n } else if (entry.isDirectory() && recursive) {\n const subFiles = await collectFiles(fullPath, true);\n files.push(...subFiles);\n }\n }\n\n return files;\n}\n\nfunction buildFileMessage(fileName: string, result: ExtractionResult): string {\n const words = result.wordCount.toLocaleString();\n if (result.method === 'ocr') {\n return `Scanned ${fileName} (OCR, ${result.confidence}% confidence) — extracted ${words} words across ${result.pageCount} pages.`;\n }\n if (result.pageCount) {\n return `Scanned ${fileName} — extracted ${words} words across ${result.pageCount} pages.`;\n }\n return `Scanned ${fileName} — extracted ${words} words.`;\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${bytes}B`;\n}\n","/**\n * domain/scanning/path-validator.ts — Security validation for scan paths.\n *\n * Resolves symlinks, rejects system paths, enforces size limits.\n * Prevents MCP server from reading sensitive system files.\n */\n\nimport { realpath, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { resolve, normalize } from 'node:path';\nimport { ffError } from '../../errors/catalog.js';\n\nconst MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50MB\nconst MAX_FOLDER_SIZE_BYTES = 500 * 1024 * 1024; // 500MB\n\n/** System directories that must never be scanned. */\nconst BLOCKED_PREFIXES = [\n '/etc/',\n '/var/',\n '/usr/',\n '/bin/',\n '/sbin/',\n '/System/',\n '/Library/',\n '/private/etc/',\n '/private/var/',\n 'C:\\\\Windows\\\\',\n 'C:\\\\Program Files\\\\',\n 'C:\\\\Program Files (x86)\\\\',\n];\n\n/**\n * Validate and resolve a file path for scanning.\n * Resolves symlinks, rejects system paths and paths outside home directory.\n * Returns the resolved absolute path.\n */\nexport async function validateScanPath(inputPath: string): Promise<string> {\n // Normalize and resolve to absolute path\n const absolutePath = resolve(normalize(inputPath));\n\n // Resolve symlinks\n let resolvedPath: string;\n try {\n resolvedPath = await realpath(absolutePath);\n } catch {\n throw ffError.scanFailed(inputPath, 'Path does not exist or is not accessible.');\n }\n\n // Check against blocked system prefixes\n for (const prefix of BLOCKED_PREFIXES) {\n if (resolvedPath.startsWith(prefix)) {\n throw ffError.scanFailed(inputPath, 'Path resolves to a system directory. Forcefield only scans files you own.');\n }\n }\n\n // Must be within user's home directory\n const home = homedir();\n if (!resolvedPath.startsWith(home)) {\n throw ffError.scanFailed(inputPath, 'Path resolves outside your home directory. Forcefield only scans files you own.');\n }\n\n return resolvedPath;\n}\n\n/**\n * Validate file size against the per-file limit.\n */\nexport async function validateFileSize(filePath: string): Promise<number> {\n const stats = await stat(filePath);\n if (!stats.isFile()) {\n throw ffError.scanFailed(filePath, 'Not a regular file.');\n }\n if (stats.size > MAX_FILE_SIZE_BYTES) {\n throw ffError.scanFailed(\n filePath,\n `File too large (${formatSize(stats.size)}). Maximum is ${formatSize(MAX_FILE_SIZE_BYTES)}.`,\n );\n }\n return stats.size;\n}\n\n/**\n * Validate that a path is a directory.\n */\nexport async function validateDirectory(dirPath: string): Promise<void> {\n const stats = await stat(dirPath);\n if (!stats.isDirectory()) {\n throw ffError.scanFailed(dirPath, 'Not a directory.');\n }\n}\n\nexport function getMaxFolderSize(): number {\n return MAX_FOLDER_SIZE_BYTES;\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${bytes}B`;\n}\n","/**\n * domain/scanning/extract-text.ts — Multi-format text extraction.\n *\n * Supports: PDF (text + OCR), DOCX, RTF, HTML, CSV, MD, TXT.\n * PDF: pdfjs-dist for text extraction, mupdf.js + Tesseract.js for scanned PDFs.\n * Other formats: officeparser or direct read.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport { ffError } from '../../errors/catalog.js';\nimport { logger } from '../../utils/logger.js';\nimport { ocrPdfPage } from './ocr-worker.js';\n\n/** Smart detection threshold: pages with < this many chars are likely scanned. */\nconst SCANNED_THRESHOLD = 10;\n\nexport interface ExtractionResult {\n text: string;\n wordCount: number;\n pageCount: number | null;\n method: 'text' | 'ocr' | 'officeparser' | 'raw';\n confidence: number | null;\n}\n\n/** Supported file extensions (lowercase, with leading dot). */\nconst SUPPORTED_EXTENSIONS = new Set([\n '.pdf',\n '.docx',\n '.rtf',\n '.html',\n '.htm',\n '.csv',\n '.md',\n '.txt',\n]);\n\nexport function isSupportedExtension(filePath: string): boolean {\n return SUPPORTED_EXTENSIONS.has(extname(filePath).toLowerCase());\n}\n\n/**\n * Extract text from a file. Detects format from extension.\n */\nexport async function extractText(filePath: string): Promise<ExtractionResult> {\n const ext = extname(filePath).toLowerCase();\n\n if (!SUPPORTED_EXTENSIONS.has(ext)) {\n throw ffError.scanFailed(filePath, `Unsupported file type: ${ext}. Supported: PDF, DOCX, RTF, HTML, CSV, MD, TXT.`);\n }\n\n switch (ext) {\n case '.pdf':\n return extractPdf(filePath);\n case '.docx':\n case '.rtf':\n case '.html':\n case '.htm':\n return extractWithOfficeParser(filePath);\n case '.csv':\n case '.md':\n case '.txt':\n return extractRawText(filePath);\n default:\n throw ffError.scanFailed(filePath, `Unhandled extension: ${ext}`);\n }\n}\n\n/**\n * PDF extraction: fast path (text layer) or OCR path (scanned).\n */\nasync function extractPdf(filePath: string): Promise<ExtractionResult> {\n const buffer = await readFile(filePath);\n\n // Use pdfjs-dist to try text extraction\n const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');\n const doc = await pdfjs.getDocument({ data: new Uint8Array(buffer) }).promise;\n const pageCount = doc.numPages;\n\n // Extract text from all pages\n const pageTexts: string[] = [];\n for (let i = 1; i <= pageCount; i++) {\n const page = await doc.getPage(i);\n const content = await page.getTextContent();\n const text = content.items\n .filter((item) => 'str' in item)\n .map((item) => (item as { str: string }).str)\n .join(' ');\n pageTexts.push(text);\n }\n\n // Smart detection: check if this is a scanned PDF\n const totalChars = pageTexts.reduce((sum, t) => sum + t.length, 0);\n const avgCharsPerPage = pageCount > 0 ? totalChars / pageCount : 0;\n\n if (avgCharsPerPage >= SCANNED_THRESHOLD) {\n // Text-based PDF — fast path\n const fullText = pageTexts.join('\\n\\n');\n return {\n text: fullText,\n wordCount: countWords(fullText),\n pageCount,\n method: 'text',\n confidence: null,\n };\n }\n\n // Scanned PDF — OCR path\n logger.info('Scanned PDF detected, running OCR...', { filePath, avgCharsPerPage });\n\n const ocrResults: string[] = [];\n let totalConfidence = 0;\n\n for (let i = 0; i < pageCount; i++) {\n const result = await ocrPdfPage(buffer, i);\n ocrResults.push(result.text);\n totalConfidence += result.confidence;\n }\n\n const fullText = ocrResults.join('\\n\\n');\n const avgConfidence = pageCount > 0 ? totalConfidence / pageCount : 0;\n\n return {\n text: fullText,\n wordCount: countWords(fullText),\n pageCount,\n method: 'ocr',\n confidence: Math.round(avgConfidence),\n };\n}\n\n/**\n * Extract text using officeparser (DOCX, RTF, HTML).\n */\nasync function extractWithOfficeParser(filePath: string): Promise<ExtractionResult> {\n const officeparser = await import('officeparser');\n const text = await officeparser.parseOfficeAsync(filePath);\n\n return {\n text,\n wordCount: countWords(text),\n pageCount: null,\n method: 'officeparser',\n confidence: null,\n };\n}\n\n/**\n * Read plain text files directly.\n */\nasync function extractRawText(filePath: string): Promise<ExtractionResult> {\n const text = await readFile(filePath, 'utf-8');\n\n return {\n text,\n wordCount: countWords(text),\n pageCount: null,\n method: 'raw',\n confidence: null,\n };\n}\n\nfunction countWords(text: string): number {\n return text.split(/\\s+/).filter((w) => w.length > 0).length;\n}\n","/**\n * domain/scanning/ocr-worker.ts — Lazy Tesseract.js OCR worker lifecycle.\n *\n * Worker is created on first scan (saves ~400ms + 50MB on non-scanning sessions).\n * Reused across all scan calls within the session.\n */\n\nimport type Tesseract from 'tesseract.js';\nimport { logger } from '../../utils/logger.js';\n\nlet worker: Tesseract.Worker | null = null;\n\n/**\n * Get or create the Tesseract OCR worker (lazy singleton).\n */\nexport async function getOcrWorker(): Promise<Tesseract.Worker> {\n if (worker) return worker;\n\n logger.info('Initializing Tesseract OCR worker...');\n const { createWorker } = await import('tesseract.js');\n worker = await createWorker('eng');\n logger.info('Tesseract OCR worker ready.');\n return worker;\n}\n\n/**\n * Terminate the OCR worker (call on graceful shutdown).\n */\nexport async function terminateOcrWorker(): Promise<void> {\n if (worker) {\n await worker.terminate();\n worker = null;\n logger.info('Tesseract OCR worker terminated.');\n }\n}\n\n/**\n * Render a PDF page to PNG using mupdf.js, then OCR with Tesseract.\n * Returns extracted text and confidence score.\n */\nexport async function ocrPdfPage(\n pdfBuffer: Buffer | Uint8Array,\n pageIndex: number,\n): Promise<{ text: string; confidence: number }> {\n const mupdf = await import('mupdf');\n const ocrWorker = await getOcrWorker();\n\n // Render page to PNG\n const doc = mupdf.Document.openDocument(pdfBuffer, 'application/pdf');\n const page = doc.loadPage(pageIndex);\n const pixmap = page.toPixmap(\n mupdf.Matrix.scale(2, 2), // 2x scale for better OCR\n mupdf.ColorSpace.DeviceRGB,\n );\n const pngBuffer = pixmap.asPNG();\n\n // OCR the rendered page\n const result = await ocrWorker.recognize(Buffer.from(pngBuffer));\n\n return {\n text: result.data.text,\n confidence: result.data.confidence,\n };\n}\n","/**\n * domain/compliance/compliance-scorer.ts — Compute compliance score (0–100).\n *\n * Score is derived from generated deadlines only (not pending/suppressed rules).\n * Unknown-status historical deadlines count as potential gaps.\n * Per-category breakdown included.\n *\n * Scoring model:\n * - Start at 100\n * - Each overdue deadline: -10 points\n * - Each unknown-status historical deadline: -3 points (potential gap)\n * - Floor at 0\n *\n * Categories map from the compliance rule `category` field to display groups.\n */\n\nimport type { GeneratedDeadline } from './deadline-generator.js';\n\nexport interface CategoryScore {\n category: string;\n displayName: string;\n score: number;\n deductionCount: number;\n totalDeadlines: number;\n}\n\nexport interface Deduction {\n ruleId: string;\n filingName: string;\n dueDate: string;\n points: number;\n reason: string;\n}\n\nexport interface ComplianceScoreResult {\n score: number;\n categoryScores: CategoryScore[];\n deductions: Deduction[];\n pendingRuleCount: number;\n pendingMessage: string | null;\n summary: {\n totalDeadlines: number;\n upcomingCount: number;\n unknownCount: number;\n overdueCount: number;\n };\n}\n\n/** Points deducted per overdue deadline. */\nconst OVERDUE_PENALTY = 10;\n\n/** Points deducted per unknown-status historical deadline. */\nconst UNKNOWN_PENALTY = 3;\n\n/** Map rule categories to display groups. */\nconst CATEGORY_DISPLAY: Record<string, string> = {\n state_annual_report: 'Annual Reports',\n state_franchise_tax: 'Franchise Tax',\n state_payroll: 'State Payroll',\n state_registration: 'State Registration',\n state_publication: 'Publication',\n federal_income_tax: 'Federal Income Tax',\n federal_payroll: 'Federal Payroll',\n federal_information_return: 'Information Returns',\n federal_other: 'Federal Other',\n governance: 'Governance',\n intellectual_property: 'Intellectual Property',\n data_privacy: 'Data Privacy',\n};\n\n/**\n * Compute a compliance score from generated deadlines.\n *\n * @param deadlines - Generated deadline objects (from deadline-generator)\n * @param pendingRuleCount - Count of rules with status='pending' (incomplete profile)\n * @param now - Reference date (defaults to today)\n */\nexport function computeComplianceScore(\n deadlines: GeneratedDeadline[],\n pendingRuleCount = 0,\n now: Date = new Date(),\n): ComplianceScoreResult {\n const todayStr = formatDateStr(now);\n const deductions: Deduction[] = [];\n\n // Group deadlines by category\n const categoryMap = new Map<string, GeneratedDeadline[]>();\n for (const d of deadlines) {\n const existing = categoryMap.get(d.category) ?? [];\n existing.push(d);\n categoryMap.set(d.category, existing);\n }\n\n // Compute deductions\n let totalDeduction = 0;\n let overdueCount = 0;\n let unknownCount = 0;\n\n for (const d of deadlines) {\n if (d.status === 'unknown') {\n // Historical deadline with unknown filing status\n unknownCount++;\n const penalty = UNKNOWN_PENALTY;\n totalDeduction += penalty;\n deductions.push({\n ruleId: d.ruleId,\n filingName: d.filingName,\n dueDate: d.dueDate,\n points: penalty,\n reason: `Historical deadline (${d.dueDate}) — filing status unknown.`,\n });\n } else if (d.dueDate < todayStr) {\n // Past deadline that wasn't marked as completed — overdue\n overdueCount++;\n const penalty = OVERDUE_PENALTY;\n totalDeduction += penalty;\n deductions.push({\n ruleId: d.ruleId,\n filingName: d.filingName,\n dueDate: d.dueDate,\n points: penalty,\n reason: `Overdue since ${d.dueDate}.`,\n });\n }\n }\n\n const score = Math.max(0, 100 - totalDeduction);\n\n // Per-category scores\n const categoryScores: CategoryScore[] = [];\n for (const [cat, catDeadlines] of categoryMap) {\n let catDeduction = 0;\n let catDeductionCount = 0;\n\n for (const d of catDeadlines) {\n if (d.status === 'unknown') {\n catDeduction += UNKNOWN_PENALTY;\n catDeductionCount++;\n } else if (d.dueDate < todayStr) {\n catDeduction += OVERDUE_PENALTY;\n catDeductionCount++;\n }\n }\n\n categoryScores.push({\n category: cat,\n displayName: CATEGORY_DISPLAY[cat] ?? cat,\n score: Math.max(0, 100 - catDeduction),\n deductionCount: catDeductionCount,\n totalDeadlines: catDeadlines.length,\n });\n }\n\n // Sort categories by score ascending (worst first)\n categoryScores.sort((a, b) => a.score - b.score);\n\n const upcomingCount = deadlines.filter((d) => !d.isHistorical).length;\n\n const pendingMessage = pendingRuleCount > 0\n ? `${pendingRuleCount} additional obligation${pendingRuleCount > 1 ? 's' : ''} may apply — complete your profile for a full assessment.`\n : null;\n\n return {\n score,\n categoryScores,\n deductions,\n pendingRuleCount,\n pendingMessage,\n summary: {\n totalDeadlines: deadlines.length,\n upcomingCount,\n unknownCount,\n overdueCount,\n },\n };\n}\n\nfunction formatDateStr(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n","/**\n * domain/compliance/next-occurrence.ts — Compute next deadline for recurring rules.\n *\n * Pure function (no DB). Given a completed deadline and its rule definition,\n * returns the next due date anchored to the original schedule (not completion date).\n */\n\nimport type { DeadlineRecord } from '../../db/deadlines.js';\nimport type { RuleDeadline } from './deadline-generator.js';\n\nexport interface NextOccurrence {\n dueDate: string;\n}\n\n/**\n * Compute the next occurrence of a recurring deadline.\n * Returns null for one-time / event-triggered rules.\n *\n * Next date is always anchored to the original schedule, not the completion date.\n * For example: filed March 1, due March 15 → next is March 15 of the next cycle.\n */\nexport function computeNextOccurrence(\n deadline: DeadlineRecord,\n ruleDeadline: RuleDeadline,\n): NextOccurrence | null {\n if (ruleDeadline.frequency === 'one_time' || ruleDeadline.frequency === 'event_triggered') {\n return null;\n }\n\n const currentDue = parseDate(deadline.dueDate);\n\n switch (ruleDeadline.frequency) {\n case 'annual':\n return { dueDate: advanceByYears(currentDue, 1, ruleDeadline) };\n\n case 'biennial':\n return { dueDate: advanceByYears(currentDue, 2, ruleDeadline) };\n\n case 'quarterly':\n return computeNextQuarterly(deadline, ruleDeadline);\n\n default:\n return null;\n }\n}\n\n/**\n * Advance a date by N years, keeping the same month/day.\n * Handles month-end clamping (e.g., Feb 29 → Feb 28 in non-leap year).\n */\nfunction advanceByYears(current: Date, years: number, _rule: RuleDeadline): string {\n const nextYear = current.getFullYear() + years;\n const month = current.getMonth() + 1; // 1-indexed\n const day = clampDay(nextYear, month, current.getDate());\n return formatDate(new Date(nextYear, month - 1, day));\n}\n\n/**\n * Compute next quarterly due date from the quarterly_dates schedule.\n */\nfunction computeNextQuarterly(\n deadline: DeadlineRecord,\n ruleDeadline: RuleDeadline,\n): NextOccurrence | null {\n if (!ruleDeadline.quarterly_dates) return null;\n\n const quarters = [\n ruleDeadline.quarterly_dates.q1,\n ruleDeadline.quarterly_dates.q2,\n ruleDeadline.quarterly_dates.q3,\n ruleDeadline.quarterly_dates.q4,\n ];\n\n const currentDue = parseDate(deadline.dueDate);\n const currentYear = currentDue.getFullYear();\n\n // Find the next quarter date after the current due date\n // Check remaining quarters in the current year, then Q1 of next year\n for (let yearOffset = 0; yearOffset <= 1; yearOffset++) {\n for (const qDate of quarters) {\n const [qMonth, qDay] = qDate.split('-').map(Number) as [number, number];\n let dateYear = currentYear + yearOffset;\n\n // Handle Q4 wrapping (e.g., Q4 = \"01-31\" means next January)\n if (qDate === ruleDeadline.quarterly_dates.q4) {\n const q1Month = parseInt(ruleDeadline.quarterly_dates.q1.split('-')[0]!, 10);\n if (qMonth < q1Month) {\n dateYear = currentYear + yearOffset + 1;\n }\n }\n\n const clampedDay = clampDay(dateYear, qMonth, qDay);\n const candidate = new Date(dateYear, qMonth - 1, clampedDay);\n\n if (candidate > currentDue) {\n return { dueDate: formatDate(candidate) };\n }\n }\n }\n\n return null;\n}\n\n// --- Date helpers (shared with deadline-generator.ts) ---\n\nfunction parseDate(dateStr: string): Date {\n const [y, m, d] = dateStr.split('-').map(Number) as [number, number, number];\n return new Date(y, m - 1, d);\n}\n\nfunction formatDate(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n\nfunction lastDayOfMonth(year: number, month: number): number {\n return new Date(year, month, 0).getDate();\n}\n\nfunction clampDay(year: number, month: number, day: number): number {\n const maxDay = lastDayOfMonth(year, month);\n return Math.min(day, maxDay);\n}\n","/**\n * domain/compliance/tax-calculator.ts — Tax calculation engine.\n *\n * Pure computation module. Loads calculator JSON from data layer\n * and runs tier-based / formula-based calculations.\n *\n * Handles 7 state tax calculators: DE franchise, NY Article 9-A,\n * CA LLC income fee, TX franchise, WY license, NV annual list, CA EDD rates.\n */\n\nimport { loadStaticFile } from '../../utils/static-data.js';\n\n// --- Types ---\n\nexport interface CalculatorInputDef {\n field: string;\n type: 'integer' | 'number' | 'string' | 'boolean';\n required: boolean;\n}\n\nexport interface Tier {\n min: number;\n max: number | null;\n flat_amount: number | null;\n rate: number | null;\n per_unit: string | null;\n base: number | null;\n}\n\nexport interface CalculatorMethod {\n method_id: string;\n name: string;\n tiers: Tier[] | null;\n formula_description: string;\n minimum: number | null;\n maximum: number | null;\n}\n\nexport interface TaxCalculator {\n calculator_id: string;\n name: string;\n jurisdiction: string;\n inputs: CalculatorInputDef[];\n methods: CalculatorMethod[];\n selection_rule: 'lower_of' | 'higher_of' | null;\n notes: string[];\n}\n\nexport interface MethodResult {\n methodId: string;\n methodName: string;\n amount: number | null;\n formula: string;\n inputsUsed: Record<string, unknown>;\n skippedReason?: string;\n}\n\nexport interface TaxCalculationResult {\n calculatorId: string;\n calculatorName: string;\n jurisdiction: string;\n methods: MethodResult[];\n recommended: { methodId: string; methodName: string; amount: number } | null;\n selectionRule: string | null;\n notes: string[];\n}\n\n// --- Loader ---\n\n/**\n * Load a tax calculator JSON by ID.\n * Throws if calculator file not found.\n */\nexport async function loadCalculator(calculatorId: string): Promise<TaxCalculator> {\n const raw = await loadStaticFile(`tax-calculators/${calculatorId}.json`);\n return JSON.parse(raw) as TaxCalculator;\n}\n\n// --- Core Tier Engine ---\n\n/**\n * Find the matching tier for a given value and compute the amount.\n * Returns null if no matching tier found.\n */\nexport function calculateTieredAmount(tiers: Tier[], value: number): number | null {\n const tier = tiers.find(\n (t) => value >= t.min && (t.max === null || value <= t.max),\n );\n\n if (!tier) return null;\n\n // Case 1: Flat amount only (no rate)\n if (tier.flat_amount != null && tier.rate == null) {\n return tier.flat_amount;\n }\n\n // Case 2: Rate with base — incremental calculation above tier floor\n if (tier.rate != null && tier.base != null) {\n const unitSize = extractUnitSize(tier.per_unit);\n const excess = value - tier.min + 1;\n const units = Math.ceil(excess / unitSize);\n return tier.base + tier.rate * units;\n }\n\n // Case 3: Rate only — simple multiplication\n if (tier.rate != null) {\n return value * tier.rate;\n }\n\n return tier.flat_amount;\n}\n\n/**\n * Extract the unit size from a per_unit description string.\n * e.g., \"per 10,000 shares\" → 10000\n * \"per each additional $500,000\" → 500000\n */\nexport function extractUnitSize(perUnit: string | null): number {\n if (!perUnit) return 1;\n const match = perUnit.match(/[\\$]?([\\d,]+)/);\n if (match) {\n const parsed = parseInt(match[1]!.replace(/,/g, ''), 10);\n if (parsed > 0) return parsed;\n }\n return 1;\n}\n\n/**\n * Apply min/max clamps to a calculated amount.\n */\nfunction clamp(amount: number, method: CalculatorMethod): number {\n let result = amount;\n if (method.minimum != null && result < method.minimum) result = method.minimum;\n if (method.maximum != null && result > method.maximum) result = method.maximum;\n return result;\n}\n\n// --- Calculator-Specific Logic ---\n\n/**\n * DE Assumed Par Value Capital Method.\n * Formula: assumed_par = gross_assets / issued_shares\n * capital = assumed_par × authorized_shares\n * tax = ceil(capital / 1_000_000) × $400\n */\nfunction calculateDeAssumedParValue(\n inputs: Record<string, unknown>,\n): { amount: number; formula: string } | null {\n const authorizedShares = inputs.authorized_shares as number | undefined;\n const issuedShares = inputs.issued_shares as number | undefined;\n const grossAssets = inputs.gross_assets as number | undefined;\n\n if (!authorizedShares || !issuedShares || !grossAssets) return null;\n if (issuedShares === 0) return null;\n\n const assumedPar = grossAssets / issuedShares;\n const capital = assumedPar * authorizedShares;\n const tax = Math.ceil(capital / 1_000_000) * 400;\n\n return {\n amount: tax,\n formula: `Assumed par = $${grossAssets.toLocaleString()} / ${issuedShares.toLocaleString()} = $${assumedPar.toFixed(4)}; ` +\n `Capital = $${assumedPar.toFixed(4)} × ${authorizedShares.toLocaleString()} = $${capital.toLocaleString()}; ` +\n `Tax = ⌈$${capital.toLocaleString()} / $1,000,000⌉ × $400 = $${tax.toLocaleString()}.`,\n };\n}\n\n/**\n * NV authorized capital stock calculation.\n * Lookup value = authorized_shares × max(par_value, 1).\n * No-par shares treated as $1 each per NRS 78.150.\n */\nfunction nvLookupValue(inputs: Record<string, unknown>): number | null {\n const shares = inputs.authorized_shares as number | undefined;\n const parValue = inputs.par_value as number | undefined;\n if (shares == null) return null;\n const effectivePar = (parValue == null || parValue === 0) ? 1 : parValue;\n return shares * effectivePar;\n}\n\n/**\n * TX franchise tax margin calculation.\n * Margin = lesser of: 70% revenue, revenue - COGS, revenue - compensation.\n * Returns the appropriate rate method based on inputs.\n */\nfunction txCalculateMargin(inputs: Record<string, unknown>): {\n margin: number;\n methodId: string;\n formula: string;\n} | null {\n const totalRevenue = inputs.total_revenue as number | undefined;\n if (totalRevenue == null) return null;\n\n // No tax due threshold\n if (totalRevenue <= 2_650_000) {\n return { margin: 0, methodId: 'no_tax_due', formula: `Total revenue $${totalRevenue.toLocaleString()} ≤ $2,650,000 no-tax-due threshold.` };\n }\n\n // EZ computation option\n if (inputs.uses_ez_computation && totalRevenue <= 20_000_000) {\n return { margin: totalRevenue, methodId: 'ez_computation', formula: `EZ computation: total revenue $${totalRevenue.toLocaleString()} × 0.331%.` };\n }\n\n // Compute margin candidates\n const seventyPercent = totalRevenue * 0.7;\n const cogs = inputs.cogs as number | undefined;\n const compensation = inputs.compensation as number | undefined;\n const compensationCapped = compensation != null ? Math.min(compensation, 480_000) : undefined;\n\n const candidates: Array<{ label: string; value: number }> = [\n { label: '70% of total revenue', value: seventyPercent },\n ];\n if (cogs != null) candidates.push({ label: 'Revenue minus COGS', value: totalRevenue - cogs });\n if (compensationCapped != null) candidates.push({ label: 'Revenue minus compensation', value: totalRevenue - compensationCapped });\n\n const best = candidates.reduce((a, b) => (a.value < b.value ? a : b));\n\n // Determine rate method\n const entityType = inputs.entity_type as string | undefined;\n const isRetail = entityType === 'retail' || entityType === 'wholesale';\n const methodId = isRetail ? 'retail_wholesale' : 'other';\n\n return {\n margin: best.value,\n methodId,\n formula: `Margin = ${best.label} = $${best.value.toLocaleString()}.`,\n };\n}\n\n// --- Main Entry Point ---\n\n/**\n * Run all calculator methods and return results with recommendation.\n */\nexport function calculateTax(\n calculator: TaxCalculator,\n inputs: Record<string, unknown>,\n): TaxCalculationResult {\n // Validate required inputs\n const missingRequired = calculator.inputs\n .filter((i) => i.required && inputs[i.field] == null)\n .map((i) => i.field);\n\n if (missingRequired.length > 0) {\n return {\n calculatorId: calculator.calculator_id,\n calculatorName: calculator.name,\n jurisdiction: calculator.jurisdiction,\n methods: [],\n recommended: null,\n selectionRule: calculator.selection_rule,\n notes: [`Missing required inputs: ${missingRequired.join(', ')}.`],\n };\n }\n\n const methodResults: MethodResult[] = [];\n\n // Special handling per calculator\n const calcId = calculator.calculator_id;\n\n for (const method of calculator.methods) {\n const result = calculateSingleMethod(calcId, method, inputs);\n methodResults.push(result);\n }\n\n // Apply selection rule to pick recommended method\n const calculated = methodResults.filter((m) => m.amount != null);\n let recommended: TaxCalculationResult['recommended'] = null;\n\n if (calculated.length > 0) {\n if (calculator.selection_rule === 'lower_of') {\n const best = calculated.reduce((a, b) => (a.amount! < b.amount! ? a : b));\n recommended = { methodId: best.methodId, methodName: best.methodName, amount: best.amount! };\n } else if (calculator.selection_rule === 'higher_of') {\n const best = calculated.reduce((a, b) => (a.amount! > b.amount! ? a : b));\n recommended = { methodId: best.methodId, methodName: best.methodName, amount: best.amount! };\n } else if (calculated.length === 1) {\n // Single-method calculator — recommend the only result\n const only = calculated[0]!;\n recommended = { methodId: only.methodId, methodName: only.methodName, amount: only.amount! };\n }\n }\n\n return {\n calculatorId: calculator.calculator_id,\n calculatorName: calculator.name,\n jurisdiction: calculator.jurisdiction,\n methods: methodResults,\n recommended,\n selectionRule: calculator.selection_rule,\n notes: calculator.notes,\n };\n}\n\nfunction calculateSingleMethod(\n calcId: string,\n method: CalculatorMethod,\n inputs: Record<string, unknown>,\n): MethodResult {\n const base: Omit<MethodResult, 'amount' | 'formula'> = {\n methodId: method.method_id,\n methodName: method.name,\n inputsUsed: inputs,\n };\n\n // --- DE Assumed Par Value (special: no tiers, custom formula) ---\n if (calcId === 'de-franchise-tax' && method.method_id === 'assumed_par_value') {\n const result = calculateDeAssumedParValue(inputs);\n if (!result) {\n return {\n ...base,\n amount: null,\n formula: method.formula_description,\n skippedReason: 'Requires issued_shares and gross_assets inputs.',\n };\n }\n return {\n ...base,\n amount: clamp(result.amount, method),\n formula: result.formula,\n };\n }\n\n // --- NV authorized capital (lookup = shares × par_value) ---\n if (calcId === 'nv-corp-annual-list' && method.method_id === 'authorized_capital') {\n const lookupValue = nvLookupValue(inputs);\n if (lookupValue == null || !method.tiers) {\n return { ...base, amount: null, formula: method.formula_description, skippedReason: 'Missing authorized_shares.' };\n }\n const amount = calculateTieredAmount(method.tiers, lookupValue);\n if (amount == null) {\n return { ...base, amount: null, formula: method.formula_description, skippedReason: 'Value out of tier range.' };\n }\n return {\n ...base,\n amount: clamp(amount, method),\n formula: `Authorized capital = ${(inputs.authorized_shares as number).toLocaleString()} shares × $${(Math.max((inputs.par_value as number) ?? 1, 1)).toFixed(4)} par = $${lookupValue.toLocaleString()}. ${method.formula_description}`,\n };\n }\n\n // --- TX franchise tax (margin-based, multiple rate methods) ---\n if (calcId === 'tx-franchise-tax') {\n const marginResult = txCalculateMargin(inputs);\n if (!marginResult) {\n return { ...base, amount: null, formula: method.formula_description, skippedReason: 'Missing total_revenue.' };\n }\n // No tax due\n if (marginResult.margin === 0 && marginResult.methodId === 'no_tax_due') {\n return { ...base, amount: 0, formula: marginResult.formula };\n }\n // EZ computation: only applies to ez_computation method\n if (marginResult.methodId === 'ez_computation' && method.method_id === 'ez_computation') {\n if (!method.tiers) return { ...base, amount: null, formula: method.formula_description };\n const amount = calculateTieredAmount(method.tiers, marginResult.margin);\n return { ...base, amount: amount != null ? Math.round(amount * 100) / 100 : null, formula: marginResult.formula };\n }\n // For non-EZ methods, only calculate the matching method\n if (marginResult.methodId === method.method_id && method.tiers) {\n const amount = calculateTieredAmount(method.tiers, marginResult.margin);\n return { ...base, amount: amount != null ? Math.round(amount * 100) / 100 : null, formula: `${marginResult.formula} Rate: ${method.formula_description}` };\n }\n // Skip non-matching TX methods (e.g., retail method when entity is not retail)\n if (marginResult.methodId !== method.method_id && marginResult.methodId !== 'no_tax_due') {\n return { ...base, amount: null, formula: method.formula_description, skippedReason: `Not applicable (using ${marginResult.methodId} rate).` };\n }\n // No tax due — set 0 on all methods\n return { ...base, amount: 0, formula: marginResult.formula };\n }\n\n // --- Generic tier-based calculation ---\n if (!method.tiers || method.tiers.length === 0) {\n return {\n ...base,\n amount: null,\n formula: method.formula_description,\n skippedReason: 'No tiers defined and no special handler.',\n };\n }\n\n // Determine lookup value: use first required input\n const primaryInput = findPrimaryInput(inputs, method);\n if (primaryInput == null) {\n return { ...base, amount: null, formula: method.formula_description, skippedReason: 'Primary input not provided.' };\n }\n\n const amount = calculateTieredAmount(method.tiers, primaryInput);\n if (amount == null) {\n return { ...base, amount: null, formula: method.formula_description, skippedReason: 'Value out of tier range.' };\n }\n\n return {\n ...base,\n amount: clamp(Math.round(amount * 100) / 100, method),\n formula: method.formula_description,\n };\n}\n\n/**\n * Determine the primary numeric input value for a generic tier lookup.\n * Uses the first numeric key in inputs that exists.\n */\nfunction findPrimaryInput(\n inputs: Record<string, unknown>,\n _method: CalculatorMethod,\n): number | null {\n for (const [, value] of Object.entries(inputs)) {\n if (typeof value === 'number') return value;\n }\n return null;\n}\n","/**\n * domain/compliance/disclaimers.ts — Legal disclaimers, verification guidance, and glossary.\n *\n * Pure module with no external dependencies.\n * FR20: \"not legal advice\" disclaimers in all ff_compliance responses.\n * FR21: \"verify with official sources\" guidance in filing/remediation guidance.\n * FR86: Plain-language explanations of compliance terms.\n */\n\nexport const LEGAL_DISCLAIMER =\n 'This is not legal advice. Forcefield provides informational compliance guidance only. Consult a qualified attorney for legal counsel specific to your situation.';\n\n/**\n * Full-tier state authority overrides.\n * These get specific authority names instead of generic \"Secretary of State\".\n */\nconst AUTHORITY_OVERRIDES: Record<string, string> = {\n DE: 'the Delaware Division of Corporations (corp.delaware.gov)',\n CA: 'the California Secretary of State and Franchise Tax Board (ftb.ca.gov)',\n NY: 'the New York Department of State (dos.ny.gov)',\n TX: 'the Texas Comptroller of Public Accounts (comptroller.texas.gov)',\n WY: 'the Wyoming Secretary of State (sos.wyo.gov)',\n NV: 'the Nevada Secretary of State (nvsos.gov)',\n NM: 'the New Mexico Secretary of State (sos.nm.gov)',\n WA: 'the Washington Secretary of State (sos.wa.gov)',\n FL: 'the Florida Division of Corporations (dos.myflorida.com)',\n IL: 'the Illinois Secretary of State (ilsos.gov)',\n MA: 'the Massachusetts Secretary of the Commonwealth (sec.state.ma.us)',\n CO: 'the Colorado Secretary of State (sos.state.co.us)',\n Federal: 'the IRS (irs.gov)',\n};\n\nconst STATE_NAMES: Record<string, string> = {\n AL: 'Alabama', AK: 'Alaska', AZ: 'Arizona', AR: 'Arkansas',\n CA: 'California', CO: 'Colorado', CT: 'Connecticut', DE: 'Delaware',\n FL: 'Florida', GA: 'Georgia', HI: 'Hawaii', ID: 'Idaho',\n IL: 'Illinois', IN: 'Indiana', IA: 'Iowa', KS: 'Kansas',\n KY: 'Kentucky', LA: 'Louisiana', ME: 'Maine', MD: 'Maryland',\n MA: 'Massachusetts', MI: 'Michigan', MN: 'Minnesota', MS: 'Mississippi',\n MO: 'Missouri', MT: 'Montana', NE: 'Nebraska', NV: 'Nevada',\n NH: 'New Hampshire', NJ: 'New Jersey', NM: 'New Mexico', NY: 'New York',\n NC: 'North Carolina', ND: 'North Dakota', OH: 'Ohio', OK: 'Oklahoma',\n OR: 'Oregon', PA: 'Pennsylvania', RI: 'Rhode Island', SC: 'South Carolina',\n SD: 'South Dakota', TN: 'Tennessee', TX: 'Texas', UT: 'Utah',\n VT: 'Vermont', VA: 'Virginia', WA: 'Washington', WV: 'West Virginia',\n WI: 'Wisconsin', WY: 'Wyoming', DC: 'District of Columbia', PR: 'Puerto Rico',\n};\n\n/**\n * Returns verification guidance with jurisdiction-specific authority.\n * Full-tier states get named authorities; others get generic \"Secretary of State\".\n */\nexport function getVerificationGuidance(jurisdiction: string): string {\n const override = AUTHORITY_OVERRIDES[jurisdiction];\n if (override) {\n return `Verify all deadlines, fees, and filing requirements with ${override} before filing.`;\n }\n\n const stateName = STATE_NAMES[jurisdiction];\n if (stateName) {\n return `Verify all deadlines, fees, and filing requirements with the ${stateName} Secretary of State before filing.`;\n }\n\n return `Verify all deadlines, fees, and filing requirements with the relevant state authority before filing.`;\n}\n\nexport interface GlossaryEntry {\n term: string;\n definition: string;\n}\n\nconst GLOSSARY: Record<string, GlossaryEntry[]> = {\n state_franchise_tax: [\n { term: 'Franchise Tax', definition: 'A tax charged by some states for the privilege of being incorporated or organized there — not based on income.' },\n { term: 'Authorized Shares Method', definition: 'One way Delaware calculates franchise tax, based on the total number of shares your company is authorized to issue.' },\n { term: 'Assumed Par Value Method', definition: 'An alternative Delaware franchise tax calculation that factors in total gross assets — often results in a lower tax.' },\n ],\n federal_income_tax: [\n { term: 'Form 1120', definition: 'The annual U.S. corporate income tax return that C-Corps must file with the IRS.' },\n { term: 'Form 1120-S', definition: 'The annual tax return for S-Corps, which pass income through to shareholders.' },\n { term: 'Fiscal Year End', definition: 'The last day of your company\\'s 12-month accounting period — tax deadlines are calculated from this date.' },\n ],\n state_annual_report: [\n { term: 'Annual Report', definition: 'A periodic filing required by your state to confirm your company\\'s current officers, address, and registered agent.' },\n { term: 'Registered Agent', definition: 'A person or service designated to receive legal documents and official state correspondence on behalf of your company.' },\n ],\n governance: [\n { term: 'Annual Meeting', definition: 'A yearly meeting of shareholders and/or directors required by corporate law to approve key decisions.' },\n { term: '83(b) Election', definition: 'An IRS filing that lets founders pay tax on stock at its current (low) value instead of when it vests at a potentially higher value.' },\n { term: 'Bylaws', definition: 'The internal rules governing how your corporation operates — covering meetings, voting, officers, and more.' },\n ],\n federal_payroll_tax: [\n { term: 'Form 941', definition: 'A quarterly IRS form reporting income taxes, Social Security, and Medicare taxes withheld from employee paychecks.' },\n { term: 'Form 940', definition: 'An annual IRS form for reporting Federal Unemployment Tax (FUTA) obligations.' },\n ],\n state_payroll_tax: [\n { term: 'State Unemployment Insurance (SUI)', definition: 'A state-level tax employers pay to fund unemployment benefits for workers who lose their jobs.' },\n { term: 'Quarterly Payroll Filing', definition: 'A periodic state report of wages paid and taxes withheld for each employee.' },\n ],\n federal_information_return: [\n { term: 'Form 1099-NEC', definition: 'An IRS form reporting payments of $600 or more to independent contractors during the year.' },\n { term: 'Form W-2', definition: 'An annual form reporting an employee\\'s wages and the taxes withheld from their paychecks.' },\n ],\n state_business_tax: [\n { term: 'Business & Occupation Tax', definition: 'A gross receipts tax (like Washington\\'s B&O tax) charged on total revenue, not just profit.' },\n { term: 'Privilege Tax', definition: 'A state tax levied for the privilege of doing business in that state — similar to a franchise tax.' },\n ],\n intellectual_property: [\n { term: 'IP Assignment', definition: 'A legal transfer of intellectual property rights (patents, code, designs) from a founder or contractor to the company.' },\n { term: 'PIIA', definition: 'Proprietary Information and Inventions Assignment — an agreement ensuring employees assign work-related inventions to the company.' },\n { term: 'Trade Secret', definition: 'Confidential business information (formulas, processes, customer lists) that derives value from not being publicly known.' },\n ],\n data_privacy: [\n { term: 'CCPA', definition: 'California Consumer Privacy Act — gives California residents rights over their personal data, including the right to know, delete, and opt out of sale.' },\n { term: 'Data Breach Notification', definition: 'A legal requirement to notify affected individuals and authorities when personal data is compromised.' },\n { term: 'Privacy Policy', definition: 'A public document explaining what personal data your company collects, how it\\'s used, and how it\\'s protected.' },\n ],\n state_registration: [\n { term: 'Form D', definition: 'An SEC filing required when a company sells securities (like stock to investors) under a Regulation D exemption.' },\n { term: 'Blue Sky Notice', definition: 'A state-level securities filing required in addition to the federal Form D — named after early laws protecting investors from fraud.' },\n { term: 'Foreign Registration', definition: 'Registering your company to do business in a state other than where it was originally formed.' },\n ],\n s_corp_election: [\n { term: 'Form 2553', definition: 'The IRS form to elect S-Corporation status, which allows corporate income to pass through to shareholders\\' personal tax returns.' },\n { term: 'S-Corp Election', definition: 'A tax classification that avoids double taxation by passing corporate income directly to shareholders.' },\n ],\n};\n\n/**\n * Returns plain-language glossary entries for the given compliance category.\n * Returns empty array for unknown categories.\n */\nexport function getGlossaryTerms(category: string): GlossaryEntry[] {\n return GLOSSARY[category] ?? [];\n}\n\nexport interface ComplianceMeta {\n disclaimer: string;\n verificationGuidance: string | null;\n glossary: GlossaryEntry[];\n}\n\n/**\n * Compose all three disclaimer components into a single metadata object.\n * - disclaimer is always present\n * - verificationGuidance is present only when jurisdiction is provided\n * - glossary is present only when category matches a known category\n */\nexport function buildComplianceMeta(\n jurisdiction: string | null,\n category: string | null,\n): ComplianceMeta {\n return {\n disclaimer: LEGAL_DISCLAIMER,\n verificationGuidance: jurisdiction ? getVerificationGuidance(jurisdiction) : null,\n glossary: category ? getGlossaryTerms(category) : [],\n };\n}\n","/**\n * domain/templates/template-loader.ts — Load document templates from Supabase static_content.\n *\n * Reads the _index.json at startup. Lazy-loaded and cached.\n * Template files and previews are loaded on demand.\n */\n\nimport { loadStaticFile, loadStaticFilesByCategory } from '../../utils/static-data.js';\nimport { logger } from '../../utils/logger.js';\n\nexport interface TemplateIndexEntry {\n template_id: string;\n name: string;\n category: string;\n file: string;\n}\n\ninterface TemplateIndex {\n generated: string;\n total_templates: number;\n templates: TemplateIndexEntry[];\n}\n\nlet cachedIndex: TemplateIndex | null = null;\n\n/** Preview filename → templateId mapping, built on first call. */\nlet previewMap: Map<string, string> | null = null;\n\n/** Stored warnings from the last validateTemplates() run. */\nlet storedWarnings: string[] = [];\n\n/**\n * Load the template index. Cached after first call.\n */\nexport async function loadTemplateIndex(): Promise<TemplateIndexEntry[]> {\n if (cachedIndex) return cachedIndex.templates;\n\n const raw = await loadStaticFile('templates/_index.json');\n cachedIndex = JSON.parse(raw) as TemplateIndex;\n\n logger.info('Templates loaded', {\n total: cachedIndex.total_templates,\n });\n\n return cachedIndex.templates;\n}\n\n/**\n * Load a full template file by template_id.\n */\nexport async function loadFullTemplate(templateId: string): Promise<Record<string, unknown>> {\n const templates = await loadTemplateIndex();\n const entry = templates.find((t) => t.template_id === templateId);\n if (!entry) {\n throw new Error(`Template \"${templateId}\" not found in index.`);\n }\n\n const raw = await loadStaticFile(`templates/${entry.file}`);\n return JSON.parse(raw) as Record<string, unknown>;\n}\n\n/**\n * Build the preview filename → templateId map from static_content category.\n */\nasync function buildPreviewMap(): Promise<Map<string, string>> {\n if (previewMap) return previewMap;\n\n const files = await loadStaticFilesByCategory('template_preview');\n previewMap = new Map();\n\n for (const file of files) {\n // path is like \"templates/previews/01-directors-meeting-minutes.md\"\n const fileName = file.path.split('/').pop() ?? '';\n if (!fileName.endsWith('.md')) continue;\n // Format: NN-template-id.md — extract template_id by stripping prefix number and .md\n const withoutExt = fileName.replace(/\\.md$/, '');\n const dashIdx = withoutExt.indexOf('-');\n if (dashIdx >= 0) {\n const templateId = withoutExt.slice(dashIdx + 1);\n previewMap.set(templateId, fileName);\n }\n }\n\n return previewMap;\n}\n\n/**\n * Load preview markdown for a template by template_id.\n */\nexport async function loadTemplatePreview(templateId: string): Promise<string> {\n const map = await buildPreviewMap();\n const fileName = map.get(templateId);\n if (!fileName) {\n throw new Error(`Preview not found for template \"${templateId}\".`);\n }\n\n return loadStaticFile(`templates/previews/${fileName}`);\n}\n\nexport interface TemplateWarning {\n templateId: string;\n warnings: string[];\n}\n\n/**\n * Validate all templates: cross-check required_variables vs question_flow.\n * Returns only entries that have warnings.\n */\nexport async function validateTemplates(): Promise<TemplateWarning[]> {\n const templates = await loadTemplateIndex();\n const results: TemplateWarning[] = [];\n\n for (const entry of templates) {\n try {\n const full = await loadFullTemplate(entry.template_id);\n const warnings: string[] = [];\n\n const requiredVars = full.required_variables as Array<{ name: string; required?: boolean }> | undefined;\n const questionFlow = full.question_flow as Array<Record<string, unknown>> | undefined;\n\n if (!requiredVars || !questionFlow) {\n warnings.push('Missing required_variables or question_flow');\n } else {\n const requiredNames = new Set(\n requiredVars.filter((v) => v.required).map((v) => v.name),\n );\n const allVarNames = new Set(requiredVars.map((v) => v.name));\n\n // Extract variable names from question_flow — supports two formats:\n // Format 1: { variable_name: string } — single variable per step\n // Format 2: { variables: string[] } — multiple variables per step\n const flowNames = new Set<string>();\n for (const step of questionFlow) {\n if (typeof step.variable_name === 'string') {\n flowNames.add(step.variable_name);\n }\n if (Array.isArray(step.variables)) {\n for (const v of step.variables) {\n if (typeof v === 'string') flowNames.add(v);\n }\n }\n }\n\n // Every required variable should have a question_flow entry\n for (const name of requiredNames) {\n if (!flowNames.has(name)) {\n warnings.push(`Required variable \"${name}\" has no question_flow entry`);\n }\n }\n\n // Every question_flow entry should reference a known variable\n for (const name of flowNames) {\n if (!allVarNames.has(name)) {\n warnings.push(`Question flow references unknown variable \"${name}\"`);\n }\n }\n }\n\n if (warnings.length > 0) {\n for (const w of warnings) {\n logger.warn(`Template validation: ${entry.template_id}: ${w}`);\n }\n results.push({ templateId: entry.template_id, warnings });\n }\n } catch (err) {\n const msg = `Failed to load template: ${err instanceof Error ? err.message : String(err)}`;\n logger.warn(`Template validation: ${entry.template_id}: ${msg}`);\n results.push({ templateId: entry.template_id, warnings: [msg] });\n }\n }\n\n return results;\n}\n\n/**\n * Run validation and store warnings for retrieval via getTemplateWarnings().\n * Intended for fire-and-forget startup validation.\n */\nexport async function validateAndStoreWarnings(): Promise<void> {\n try {\n const results = await validateTemplates();\n storedWarnings = results.flatMap((r) =>\n r.warnings.map((w) => `${r.templateId}: ${w}`),\n );\n if (storedWarnings.length > 0) {\n logger.warn('Template validation completed with warnings', {\n count: storedWarnings.length,\n });\n } else {\n logger.info('Template validation passed — no warnings');\n }\n } catch (err) {\n const msg = `Template validation failed: ${err instanceof Error ? err.message : String(err)}`;\n logger.error(msg);\n storedWarnings = [msg];\n }\n}\n\n/**\n * Get stored warnings from the last validateAndStoreWarnings() run.\n */\nexport function getTemplateWarnings(): string[] {\n return storedWarnings;\n}\n\n/**\n * Reset all caches (for testing only).\n */\nexport function _resetTemplateCache(): void {\n cachedIndex = null;\n previewMap = null;\n storedWarnings = [];\n}\n","/**\n * domain/compliance/template-linker.ts — Resolve template links from compliance rules.\n *\n * Some compliance rules have a `linked_template_id` that points to a document template\n * (e.g., annual stockholder meeting → shareholders-meeting-minutes). This module\n * resolves those links for surfacing template suggestions alongside deadlines.\n */\n\nimport { loadRuleIndex, loadFullRule } from './rule-loader.js';\nimport { loadTemplateIndex } from '../templates/template-loader.js';\n\nexport interface TemplateLink {\n ruleId: string;\n filingName: string;\n category: string;\n templateId: string;\n templateName: string;\n}\n\n/**\n * Resolve the template link for a single rule.\n * Returns null if the rule has no linked_template_id or the template is not found.\n */\nexport async function resolveTemplateLink(\n ruleId: string,\n): Promise<{ templateId: string; templateName: string } | null> {\n let fullRule: Record<string, unknown>;\n try {\n fullRule = await loadFullRule(ruleId);\n } catch {\n return null;\n }\n\n const linkedTemplateId = fullRule.linked_template_id as string | null | undefined;\n if (!linkedTemplateId) return null;\n\n const templates = await loadTemplateIndex();\n const entry = templates.find((t) => t.template_id === linkedTemplateId);\n if (!entry) return null;\n\n return { templateId: entry.template_id, templateName: entry.name };\n}\n\n/**\n * Resolve all template links across all compliance rules.\n * Returns an array of TemplateLink objects for rules with non-null linked_template_id.\n */\nexport async function resolveAllTemplateLinks(): Promise<TemplateLink[]> {\n const ruleIndex = await loadRuleIndex();\n const templates = await loadTemplateIndex();\n const templateMap = new Map(templates.map((t) => [t.template_id, t.name]));\n\n const links: TemplateLink[] = [];\n\n const loadPromises = ruleIndex.map(async (rule) => {\n try {\n const fullRule = await loadFullRule(rule.rule_id);\n const linkedTemplateId = fullRule.linked_template_id as string | null | undefined;\n if (!linkedTemplateId) return;\n\n const templateName = templateMap.get(linkedTemplateId);\n if (!templateName) return;\n\n links.push({\n ruleId: rule.rule_id,\n filingName: rule.filing_name,\n category: rule.category,\n templateId: linkedTemplateId,\n templateName,\n });\n } catch {\n // Skip rules that fail to load\n }\n });\n\n await Promise.all(loadPromises);\n\n return links;\n}\n","/**\n * tools/ff-compliance.ts — ff_compliance tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n * Phase 1 actions: generate_deadlines, score, guide.\n * Phase 2 actions: list_deadlines, complete, history.\n * Phase 2 stubs: (none remaining).\n */\n\nimport type { SessionContext } from '../auth/session.js';\nimport { FfComplianceSchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { validateCompanyOwnership } from '../auth/ownership.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport { loadStaticFile } from '../utils/static-data.js';\nimport { getCompanyById } from '../db/companies.js';\nimport { loadRuleIndex, loadFullRule } from '../domain/compliance/rule-loader.js';\nimport { evaluateRules, summarizeEvaluation } from '../domain/compliance/rule-evaluator.js';\nimport {\n generateDeadlines,\n type RuleDeadline,\n type CompanyContext,\n} from '../domain/compliance/deadline-generator.js';\nimport { computeComplianceScore } from '../domain/compliance/compliance-scorer.js';\nimport {\n upsertDeadlines,\n getDeadlineById,\n listDeadlines as dbListDeadlines,\n completeDeadline as dbCompleteDeadline,\n deleteDeadlinesByCompany,\n markOverdueDeadlines,\n createFilingHistory,\n listFilingHistory as dbListFilingHistory,\n suppressDeadlinesByRule,\n unsuppressDeadlinesByRule,\n suppressDeadline as dbSuppressDeadline,\n reopenDeadline as dbReopenDeadline,\n voidFilingHistory,\n findNextOccurrenceDeadline,\n type DeadlineRecord,\n} from '../db/deadlines.js';\nimport { computeNextOccurrence } from '../domain/compliance/next-occurrence.js';\nimport {\n isOverdue,\n computeSeverity,\n computeOverdueInfo,\n severityRank,\n type OverdueInfo,\n} from '../domain/compliance/severity.js';\nimport {\n loadCalculator,\n calculateTax,\n type TaxCalculationResult,\n} from '../domain/compliance/tax-calculator.js';\nimport { buildComplianceMeta } from '../domain/compliance/disclaimers.js';\nimport { resolveAllTemplateLinks } from '../domain/compliance/template-linker.js';\nimport { loadTemplateIndex } from '../domain/templates/template-loader.js';\n\nconst TOOL_NAME = 'ff_compliance';\n\nexport async function handleFfCompliance(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n _extra?: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfComplianceSchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Steps 3-5 vary by action\n switch (input.action) {\n case 'generate_deadlines': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const result = await executeGenerateDeadlines(ctx, input.companyId, input.forceRegenerate);\n\n // Step 5: Respond\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'generate_deadlines',\n companyId: input.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: result,\n message: buildDeadlineMessage(result.summary),\n nextAction: {\n tool: 'ff_compliance',\n action: 'score',\n params: { companyId: input.companyId },\n },\n compliance: buildComplianceMeta(null, null),\n });\n }\n\n case 'score': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const result = await executeScore(ctx, input.companyId);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'score',\n companyId: input.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: result,\n message: buildScoreMessage(result),\n compliance: buildComplianceMeta(null, null),\n });\n }\n\n case 'guide': {\n // No company ownership check — guide is informational\n const result = await executeGuide(input.ruleId, input.guideType);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'guide',\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: result,\n message: `${input.guideType === 'filing' ? 'Filing' : 'Remediation'} guide for ${input.ruleId}.`,\n compliance: buildComplianceMeta(result.jurisdiction, result.category),\n });\n }\n\n case 'list_deadlines': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const result = await dbListDeadlines(\n ctx.supabase,\n input.companyId,\n { status: input.status, category: input.category, severity: input.severity },\n input.limit,\n input.offset,\n );\n\n // Overdue detection pass: catch deadlines that became overdue since last generation\n const overdueUpdates: Array<{ id: string; severity: string }> = [];\n for (const d of result.items) {\n if (d.status === 'pending' && isOverdue(d.dueDate)) {\n const sev = computeSeverity(d.dueDate);\n if (sev) {\n overdueUpdates.push({ id: d.id, severity: sev });\n d.status = 'overdue';\n d.severity = sev;\n }\n }\n }\n if (overdueUpdates.length > 0) {\n await markOverdueDeadlines(ctx.supabase, input.companyId, overdueUpdates);\n }\n\n // Enrich deadlines with overdue info and template suggestions\n const enrichedItems = await enrichDeadlines(result.items, ctx.tier);\n\n // Sort overdue items by severity (critical first)\n enrichedItems.sort((a, b) => {\n if (a.overdueInfo && b.overdueInfo) {\n return severityRank(b.overdueInfo.severity) - severityRank(a.overdueInfo.severity);\n }\n if (a.overdueInfo) return -1;\n if (b.overdueInfo) return 1;\n return 0;\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'list_deadlines',\n companyId: input.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: { ...result, items: enrichedItems },\n message: `Found ${result.total} deadline${result.total !== 1 ? 's' : ''}.`,\n nextAction: result.total > 0\n ? { tool: 'ff_compliance', action: 'complete', params: { deadlineId: result.items[0]!.id } }\n : undefined,\n compliance: buildComplianceMeta(null, input.category ?? null),\n });\n }\n\n case 'complete': {\n const result = await executeComplete(ctx, input);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'complete',\n companyId: result.deadline.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: result,\n message: buildCompleteMessage(result),\n nextAction: {\n tool: 'ff_compliance',\n action: 'list_deadlines',\n params: { companyId: result.deadline.companyId, status: 'pending' },\n },\n compliance: buildComplianceMeta(result.deadline.jurisdiction, result.deadline.category),\n });\n }\n\n case 'history': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const result = await dbListFilingHistory(\n ctx.supabase,\n input.companyId,\n input.limit,\n input.offset,\n );\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'history',\n companyId: input.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: result,\n message: `${result.total} filing${result.total !== 1 ? 's' : ''} in history.`,\n compliance: buildComplianceMeta(null, null),\n });\n }\n\n case 'override': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const overrideResult = await executeOverride(ctx, input);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'override',\n companyId: input.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: overrideResult,\n message: input.applicable\n ? `Restored rule \"${input.ruleId}\" — ${overrideResult.count} deadline${overrideResult.count !== 1 ? 's' : ''} reactivated.`\n : `Suppressed rule \"${input.ruleId}\" — ${overrideResult.count} deadline${overrideResult.count !== 1 ? 's' : ''} marked not applicable.`,\n compliance: buildComplianceMeta(null, null),\n });\n }\n\n case 'reopen': {\n const reopenResult = await executeReopen(ctx, input);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'reopen',\n companyId: reopenResult.deadline.companyId,\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: reopenResult,\n message: buildReopenMessage(reopenResult),\n nextAction: {\n tool: 'ff_compliance',\n action: 'list_deadlines',\n params: { companyId: reopenResult.deadline.companyId, status: 'pending' },\n },\n compliance: buildComplianceMeta(reopenResult.deadline.jurisdiction, reopenResult.deadline.category),\n });\n }\n\n case 'calculate_tax': {\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n const taxResult = await executeCalculateTax(input.companyId, input.ruleId, input.inputs ?? {});\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'calculate_tax',\n companyId: input.companyId,\n input: { ruleId: input.ruleId },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: taxResult,\n message: buildTaxMessage(taxResult),\n compliance: buildComplianceMeta(taxResult.jurisdiction, null),\n });\n }\n\n case 'template_links': {\n // No company ownership check — informational action\n let links = await resolveAllTemplateLinks();\n\n if (input.category) {\n links = links.filter((l) => l.category === input.category);\n }\n if (input.ruleId) {\n links = links.filter((l) => l.ruleId === input.ruleId);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'template_links',\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: { links, total: links.length },\n message: `${links.length} compliance rule${links.length !== 1 ? 's' : ''} linked to document templates.`,\n compliance: buildComplianceMeta(null, null),\n });\n }\n }\n } catch (error) {\n done(true);\n\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: 'TIER_RESTRICTED',\n });\n return formatErrorResponse(ffErr);\n }\n\n logger.error('ff_compliance unexpected error', {\n action: (rawInput.action as string) ?? 'unknown',\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: error instanceof Error ? error.constructor.name : 'unknown',\n });\n\n return formatErrorResponse(error);\n }\n}\n\n// === generate_deadlines ===\n\ninterface DeadlineResult {\n deadlines: Array<{\n ruleId: string;\n filingName: string;\n jurisdiction: string;\n category: string;\n dueDate: string;\n frequency: string;\n status: string;\n linkedTemplate: { templateId: string; templateName: string } | null;\n }>;\n skipped: Array<{ ruleId: string; reason: string }>;\n persisted: number;\n overdueMarked: number;\n summary: {\n totalGenerated: number;\n historicalCount: number;\n upcomingCount: number;\n skippedCount: number;\n applicableRules: number;\n pendingRules: number;\n suppressedRules: number;\n };\n}\n\nasync function executeGenerateDeadlines(\n ctx: SessionContext,\n companyId: string,\n forceRegenerate = false,\n): Promise<DeadlineResult> {\n // Load company\n const company = await getCompanyById(ctx.supabase, companyId);\n if (!company) throw ffError.notFound('Company', companyId);\n\n // Load rule index and evaluate applicability\n const ruleIndex = await loadRuleIndex();\n const evaluated = evaluateRules(ruleIndex, company);\n const evalSummary = summarizeEvaluation(evaluated);\n\n // Load full rule details for applicable rules\n const applicableRules = evaluated.filter((r) => r.status === 'applicable');\n const ruleDetails = new Map<string, { deadline: RuleDeadline }>();\n const ruleLinkedTemplates = new Map<string, string>();\n\n // Pre-load template index for name resolution\n const templateIndex = await loadTemplateIndex();\n const templateNameMap = new Map(templateIndex.map((t) => [t.template_id, t.name]));\n\n // Batch-load rule details\n const loadPromises = applicableRules.map(async (r) => {\n try {\n const fullRule = await loadFullRule(r.ruleId);\n if (fullRule.deadline) {\n ruleDetails.set(r.ruleId, { deadline: fullRule.deadline as RuleDeadline });\n }\n const linkedId = fullRule.linked_template_id as string | null | undefined;\n if (linkedId) {\n ruleLinkedTemplates.set(r.ruleId, linkedId);\n }\n } catch {\n // Rule file missing — will be caught as \"skipped\" by generator\n }\n });\n await Promise.all(loadPromises);\n\n // Generate deadlines\n const companyContext: CompanyContext = {\n formationDate: company.formationDate,\n fiscalYearEnd: company.fiscalYearEnd,\n };\n const result = generateDeadlines(evaluated, ruleDetails, companyContext);\n\n // Persist to DB\n if (forceRegenerate) {\n await deleteDeadlinesByCompany(ctx.supabase, companyId);\n }\n\n const upsertInput = result.deadlines.map((d) => ({\n userId: ctx.userId,\n companyId,\n ruleId: d.ruleId,\n filingName: d.filingName,\n jurisdiction: d.jurisdiction,\n category: d.category,\n dueDate: d.dueDate,\n frequency: d.frequency,\n coverageTier: d.coverageTier,\n // DB accepts: pending|completed|overdue|suppressed.\n // Generated statuses (\"upcoming\"/\"unknown\") are view-layer semantics and\n // must both persist as pending.\n status: 'pending' as const,\n }));\n\n const persisted = await upsertDeadlines(ctx.supabase, upsertInput);\n\n // Overdue detection pass: mark any pending deadlines that are past due\n const overdueUpdates: Array<{ id: string; severity: string }> = [];\n for (const d of persisted) {\n if (d.status === 'pending' && isOverdue(d.dueDate)) {\n const severity = computeSeverity(d.dueDate);\n if (severity) {\n overdueUpdates.push({ id: d.id, severity });\n }\n }\n }\n const overdueCount = await markOverdueDeadlines(ctx.supabase, companyId, overdueUpdates);\n\n return {\n deadlines: result.deadlines.map((d) => {\n const linkedId = ruleLinkedTemplates.get(d.ruleId);\n const linkedName = linkedId ? templateNameMap.get(linkedId) : undefined;\n return {\n ruleId: d.ruleId,\n filingName: d.filingName,\n jurisdiction: d.jurisdiction,\n category: d.category,\n dueDate: d.dueDate,\n frequency: d.frequency,\n status: d.status,\n linkedTemplate: linkedId && linkedName\n ? { templateId: linkedId, templateName: linkedName }\n : null,\n };\n }),\n skipped: result.skipped,\n persisted: persisted.length,\n overdueMarked: overdueCount,\n summary: {\n ...result.summary,\n applicableRules: evalSummary.applicable,\n pendingRules: evalSummary.pending,\n suppressedRules: evalSummary.suppressed,\n },\n };\n}\n\nfunction buildDeadlineMessage(summary: DeadlineResult['summary']): string {\n const parts: string[] = [];\n parts.push(`Generated ${summary.totalGenerated} deadline${summary.totalGenerated !== 1 ? 's' : ''}`);\n parts.push(`${summary.upcomingCount} upcoming, ${summary.historicalCount} historical`);\n parts.push(`from ${summary.applicableRules} applicable rule${summary.applicableRules !== 1 ? 's' : ''}`);\n\n if (summary.pendingRules > 0) {\n parts.push(\n `(${summary.pendingRules} rule${summary.pendingRules !== 1 ? 's' : ''} pending profile completion)`,\n );\n }\n\n return parts.join(' — ') + '.';\n}\n\n// === score ===\n\ninterface ScoreResult {\n score: number;\n categoryScores: Array<{\n category: string;\n displayName: string;\n score: number;\n deductionCount: number;\n totalDeadlines: number;\n }>;\n deductions: Array<{\n ruleId: string;\n filingName: string;\n dueDate: string;\n points: number;\n reason: string;\n }>;\n pendingRuleCount: number;\n pendingMessage: string | null;\n summary: {\n totalDeadlines: number;\n upcomingCount: number;\n unknownCount: number;\n overdueCount: number;\n };\n}\n\nasync function executeScore(\n ctx: SessionContext,\n companyId: string,\n): Promise<ScoreResult> {\n // First generate deadlines (same pipeline as generate_deadlines action)\n const company = await getCompanyById(ctx.supabase, companyId);\n if (!company) throw ffError.notFound('Company', companyId);\n\n const ruleIndex = await loadRuleIndex();\n const evaluated = evaluateRules(ruleIndex, company);\n const evalSummary = summarizeEvaluation(evaluated);\n\n const applicableRules = evaluated.filter((r) => r.status === 'applicable');\n const ruleDetails = new Map<string, { deadline: RuleDeadline }>();\n\n const loadPromises = applicableRules.map(async (r) => {\n try {\n const fullRule = await loadFullRule(r.ruleId);\n if (fullRule.deadline) {\n ruleDetails.set(r.ruleId, { deadline: fullRule.deadline as RuleDeadline });\n }\n } catch {\n // Skip missing rules\n }\n });\n await Promise.all(loadPromises);\n\n const companyContext: CompanyContext = {\n formationDate: company.formationDate,\n fiscalYearEnd: company.fiscalYearEnd,\n };\n const deadlineResult = generateDeadlines(evaluated, ruleDetails, companyContext);\n\n // Compute score\n return computeComplianceScore(deadlineResult.deadlines, evalSummary.pending);\n}\n\nfunction buildScoreMessage(result: ScoreResult): string {\n const parts: string[] = [];\n parts.push(`Compliance score: ${result.score}/100`);\n\n if (result.deductions.length > 0) {\n parts.push(`${result.deductions.length} deduction${result.deductions.length !== 1 ? 's' : ''} found`);\n }\n\n if (result.pendingMessage) {\n parts.push(result.pendingMessage);\n }\n\n return parts.join('. ') + '.';\n}\n\n// === complete ===\n\ninterface CompleteInput {\n action: 'complete';\n deadlineId: string;\n filingDate: string;\n confirmationNumber?: string;\n amountPaid?: number;\n documentId?: string;\n notes?: string;\n}\n\ninterface CompleteResult {\n deadline: DeadlineRecord;\n filingHistory: {\n id: string;\n filingDate: string;\n confirmationNumber: string | null;\n amountPaid: number | null;\n };\n nextOccurrence: { dueDate: string } | null;\n}\n\nasync function executeComplete(\n ctx: SessionContext,\n input: CompleteInput,\n): Promise<CompleteResult> {\n // Get the deadline\n const deadline = await getDeadlineById(ctx.supabase, input.deadlineId);\n if (!deadline) throw ffError.notFound('Deadline', input.deadlineId);\n\n // Authorize via company ownership\n await validateCompanyOwnership(ctx.supabase, deadline.companyId, ctx.userId);\n\n // Verify status allows completion (pending or overdue)\n if (deadline.status !== 'pending' && deadline.status !== 'overdue') {\n throw ffError.conflict('Deadline', `Cannot complete a deadline with status \"${deadline.status}\".`);\n }\n\n // Mark as completed\n const completedAt = new Date().toISOString();\n const updated = await dbCompleteDeadline(ctx.supabase, input.deadlineId, completedAt);\n\n // Create filing history record\n const history = await createFilingHistory(ctx.supabase, {\n userId: ctx.userId,\n companyId: deadline.companyId,\n deadlineId: input.deadlineId,\n filingDate: input.filingDate,\n confirmationNumber: input.confirmationNumber,\n amountPaid: input.amountPaid,\n documentId: input.documentId,\n notes: input.notes,\n });\n\n // Compute next occurrence for recurring deadlines\n let nextOccurrence: { dueDate: string } | null = null;\n try {\n const fullRule = await loadFullRule(deadline.ruleId);\n if (fullRule.deadline) {\n nextOccurrence = computeNextOccurrence(deadline, fullRule.deadline as RuleDeadline);\n\n // Persist the next occurrence if computed\n if (nextOccurrence) {\n await upsertDeadlines(ctx.supabase, [{\n userId: ctx.userId,\n companyId: deadline.companyId,\n ruleId: deadline.ruleId,\n filingName: deadline.filingName,\n jurisdiction: deadline.jurisdiction,\n category: deadline.category,\n dueDate: nextOccurrence.dueDate,\n frequency: deadline.frequency,\n coverageTier: deadline.coverageTier,\n }]);\n }\n }\n } catch {\n // Rule loading failed — skip next occurrence (non-fatal)\n }\n\n return {\n deadline: updated,\n filingHistory: {\n id: history.id,\n filingDate: history.filingDate,\n confirmationNumber: history.confirmationNumber,\n amountPaid: history.amountPaid,\n },\n nextOccurrence,\n };\n}\n\nfunction buildCompleteMessage(result: CompleteResult): string {\n const parts: string[] = [];\n parts.push(`Marked \"${result.deadline.filingName}\" as completed.`);\n if (result.nextOccurrence) {\n parts.push(`Next occurrence: ${result.nextOccurrence.dueDate}.`);\n }\n return parts.join(' ');\n}\n\n// === guide ===\n\ninterface GuideResult {\n ruleId: string;\n guideType: string;\n content: string;\n linkedRuleId: string;\n jurisdiction: string;\n category: string;\n}\n\nasync function executeGuide(\n ruleId: string,\n guideType: string,\n): Promise<GuideResult> {\n const fullRule = await loadFullRule(ruleId);\n\n const guideField = guideType === 'filing'\n ? (fullRule.linked_filing_guide as string | null)\n : (fullRule.linked_remediation_guide as string | null);\n\n if (!guideField) {\n throw ffError.notFound(\n `${guideType} guide`,\n ruleId,\n );\n }\n\n // Load the guide markdown file from static content\n let content: string;\n try {\n content = await loadStaticFile(guideField);\n } catch {\n throw ffError.notFound(`Guide file`, guideField);\n }\n\n return {\n ruleId,\n guideType,\n content,\n linkedRuleId: ruleId,\n jurisdiction: (fullRule.jurisdiction as string) ?? 'unknown',\n category: (fullRule.category as string) ?? 'unknown',\n };\n}\n\n// === override ===\n\ninterface OverrideInput {\n action: 'override';\n companyId: string;\n ruleId: string;\n applicable: boolean;\n reason: string;\n}\n\nasync function executeOverride(\n ctx: SessionContext,\n input: OverrideInput,\n): Promise<{ applicable: boolean; count: number }> {\n if (!input.applicable) {\n // Suppress: mark rule as not applicable\n const { count } = await suppressDeadlinesByRule(\n ctx.supabase,\n input.companyId,\n input.ruleId,\n input.reason,\n );\n return { applicable: false, count };\n } else {\n // Restore: mark rule as applicable again\n const { count } = await unsuppressDeadlinesByRule(\n ctx.supabase,\n input.companyId,\n input.ruleId,\n );\n return { applicable: true, count };\n }\n}\n\n// === reopen ===\n\ninterface ReopenInput {\n action: 'reopen';\n deadlineId: string;\n reason: string;\n}\n\ninterface ReopenResult {\n deadline: DeadlineRecord;\n filingHistoryVoided: number;\n nextOccurrenceSuppressed: boolean;\n}\n\nasync function executeReopen(\n ctx: SessionContext,\n input: ReopenInput,\n): Promise<ReopenResult> {\n // Get the deadline\n const deadline = await getDeadlineById(ctx.supabase, input.deadlineId);\n if (!deadline) throw ffError.notFound('Deadline', input.deadlineId);\n\n // Authorize via company ownership\n await validateCompanyOwnership(ctx.supabase, deadline.companyId, ctx.userId);\n\n // Only completed deadlines can be reopened\n if (deadline.status !== 'completed') {\n throw ffError.conflict(\n 'Deadline',\n `Cannot reopen a deadline with status \"${deadline.status}\". Only completed deadlines can be reopened.`,\n );\n }\n\n // 1. Reopen the deadline\n const reopened = await dbReopenDeadline(ctx.supabase, input.deadlineId);\n\n // 2. Void filing history\n const voided = await voidFilingHistory(ctx.supabase, input.deadlineId);\n\n // 3. Suppress auto-generated next occurrence (if any)\n let nextOccurrenceSuppressed = false;\n const nextDeadline = await findNextOccurrenceDeadline(\n ctx.supabase,\n deadline.companyId,\n deadline.ruleId,\n deadline.dueDate,\n );\n if (nextDeadline) {\n await dbSuppressDeadline(\n ctx.supabase,\n nextDeadline.id,\n `Suppressed: predecessor ${input.deadlineId} was reopened`,\n );\n nextOccurrenceSuppressed = true;\n }\n\n return {\n deadline: reopened,\n filingHistoryVoided: voided,\n nextOccurrenceSuppressed,\n };\n}\n\nfunction buildReopenMessage(result: ReopenResult): string {\n const parts: string[] = [];\n parts.push(`Reopened \"${result.deadline.filingName}\" — status is now pending.`);\n if (result.filingHistoryVoided > 0) {\n parts.push(`${result.filingHistoryVoided} filing record${result.filingHistoryVoided !== 1 ? 's' : ''} voided.`);\n }\n if (result.nextOccurrenceSuppressed) {\n parts.push('Next auto-generated occurrence suppressed.');\n }\n return parts.join(' ');\n}\n\n// === calculate_tax ===\n\nasync function executeCalculateTax(\n _companyId: string,\n ruleId: string,\n userInputs: Record<string, unknown>,\n): Promise<TaxCalculationResult> {\n // Load the rule to find its linked calculator\n const fullRule = await loadFullRule(ruleId);\n const calcPath = fullRule.linked_tax_calculator as string | undefined;\n\n if (!calcPath) {\n // No dedicated calculator — try to extract static fee from rule\n const fees = fullRule.fees as Record<string, unknown> | undefined;\n if (fees) {\n return {\n calculatorId: ruleId,\n calculatorName: `Static fee for ${(fullRule.filing_name as string) ?? ruleId}`,\n jurisdiction: (fullRule.jurisdiction as string) ?? 'unknown',\n methods: [{\n methodId: 'static_fee',\n methodName: 'Filing Fee',\n amount: typeof fees.base_fee === 'number' ? fees.base_fee : null,\n formula: `Base fee from compliance rule.`,\n inputsUsed: {},\n }],\n recommended: typeof fees.base_fee === 'number'\n ? { methodId: 'static_fee', methodName: 'Filing Fee', amount: fees.base_fee }\n : null,\n selectionRule: null,\n notes: [],\n };\n }\n\n throw ffError.notFound('Tax calculator', ruleId);\n }\n\n // Extract calculator ID from path (e.g., \"tax-calculators/de-franchise-tax.json\" → \"de-franchise-tax\")\n const calcId = calcPath.replace(/^tax-calculators\\//, '').replace(/\\.json$/, '');\n const calculator = await loadCalculator(calcId);\n\n // Run calculation with user-provided inputs\n return calculateTax(calculator, userInputs);\n}\n\nfunction buildTaxMessage(result: TaxCalculationResult): string {\n if (result.recommended) {\n return `${result.calculatorName}: Recommended method \"${result.recommended.methodName}\" — $${result.recommended.amount.toLocaleString()}.`;\n }\n if (result.methods.length === 0) {\n return `${result.calculatorName}: Could not calculate. ${result.notes[0] ?? ''}`;\n }\n return `${result.calculatorName}: ${result.methods.length} method${result.methods.length !== 1 ? 's' : ''} calculated.`;\n}\n\n// === Deadline enrichment ===\n\nexport interface TemplateSuggestion {\n templateId: string;\n templateName: string;\n suggestion: string;\n}\n\ninterface EnrichedDeadline extends DeadlineRecord {\n overdueInfo: OverdueInfo | null;\n templateSuggestion: TemplateSuggestion | null;\n}\n\n/**\n * Enrich deadlines with overdue info and template suggestions.\n * Overdue items get penalty/portal/remediation info.\n * Items with a linked template get a templateSuggestion.\n */\nasync function enrichDeadlines(\n items: DeadlineRecord[],\n tier: 'free' | 'paid',\n): Promise<EnrichedDeadline[]> {\n // Pre-load template index for name resolution\n const templates = await loadTemplateIndex();\n const templateMap = new Map(templates.map((t) => [t.template_id, t.name]));\n\n const enriched: EnrichedDeadline[] = [];\n\n for (const d of items) {\n let overdueInfo: OverdueInfo | null = null;\n let templateSuggestion: TemplateSuggestion | null = null;\n\n try {\n const rule = await loadFullRule(d.ruleId);\n\n // Overdue enrichment\n if (d.status === 'overdue') {\n overdueInfo = computeOverdueInfo(d, rule, tier);\n }\n\n // Template suggestion\n const linkedTemplateId = rule.linked_template_id as string | null | undefined;\n if (linkedTemplateId) {\n const templateName = templateMap.get(linkedTemplateId);\n if (templateName) {\n templateSuggestion = {\n templateId: linkedTemplateId,\n templateName,\n suggestion: `Generate \"${templateName}\" for this deadline?`,\n };\n }\n }\n } catch {\n // Rule loading failed — still include the deadline without enrichment\n }\n\n enriched.push({ ...d, overdueInfo, templateSuggestion });\n }\n\n return enriched;\n}\n","/**\n * tools/ff-system.ts — ff_system tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n * System diagnostics, health checks, and audit log access.\n */\n\nimport type { SessionContext } from '../auth/session.js';\nimport { FfSystemSchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport { getTemplateWarnings } from '../domain/templates/template-loader.js';\nimport { getMetrics, getHealthStatus } from '../utils/metrics.js';\n\nconst TOOL_NAME = 'ff_system';\n\n/** MCP server version — read from package at build time. */\nconst SERVER_VERSION = '0.1.0';\n\n/** Minimum schema version this server requires. */\nconst MIN_SCHEMA_VERSION = 1;\n\nfunction isLocalDevMode(): boolean {\n const value = process.env.FORCEFIELD_LOCAL_DEV;\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';\n}\n\nexport async function handleFfSystem(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n _extra?: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfSystemSchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Step 3: Authorize — no company ownership check for system actions\n\n // Steps 4-5: Execute → Respond (per action)\n switch (input.action) {\n case 'health': {\n const health = await checkHealth(ctx);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'health',\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: health,\n message: buildHealthMessage(health),\n });\n }\n\n case 'audit_log': {\n const result = await queryAuditLog(ctx, {\n toolName: input.toolName,\n companyId: input.companyId,\n startDate: input.startDate,\n endDate: input.endDate,\n limit: input.limit,\n offset: input.offset,\n });\n\n done();\n return formatToolResponse({\n data: result,\n message: `Showing ${result.items.length} of ${result.total} audit entries.`,\n });\n }\n\n case 'export_all': {\n if (isLocalDevMode()) {\n done();\n return formatToolResponse({\n data: {\n mode: 'local-dev',\n status: 'stubbed',\n format: input.format,\n note: 'export_all is stubbed in local dev mode.',\n },\n message: `Local dev mode: simulated export_all (${input.format}).`,\n });\n }\n done();\n throw ffError.internal('export_all is not available yet. Coming in a future release.');\n }\n\n case 'get_metrics': {\n const metricsData = getMetrics();\n const healthStatus = getHealthStatus();\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'get_metrics',\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: { ...healthStatus, tools: metricsData },\n message: buildMetricsMessage(healthStatus, metricsData.length),\n });\n }\n\n case 'delete_account': {\n if (isLocalDevMode()) {\n done();\n return formatToolResponse({\n data: {\n mode: 'local-dev',\n status: 'stubbed',\n confirmPhraseMatched: input.confirmPhrase === 'DELETE MY ACCOUNT',\n note: 'delete_account is stubbed in local dev mode.',\n },\n message: 'Local dev mode: simulated delete_account request.',\n });\n }\n done();\n throw ffError.internal('delete_account is not available yet. Coming in a future release.');\n }\n }\n } catch (error) {\n done(true);\n\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: 'TIER_RESTRICTED',\n });\n return formatErrorResponse(ffErr);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: error instanceof Error ? error.constructor.name : 'unknown',\n });\n\n return formatErrorResponse(error);\n }\n}\n\n// === Health check ===\n\ninterface HealthResult {\n serverVersion: string;\n tier: string;\n companyCount: number;\n supabaseConnected: boolean;\n schemaCompatible: boolean;\n schemaVersion: number | null;\n templateWarnings: string[];\n uptimeMs: number;\n totalCalls: number;\n totalErrors: number;\n}\n\nasync function checkHealth(ctx: SessionContext): Promise<HealthResult> {\n // Check Supabase connectivity + company count in parallel\n const [supabaseCheck, companyCount] = await Promise.all([\n checkSupabase(ctx),\n countCompanies(ctx),\n ]);\n\n const health = getHealthStatus();\n\n return {\n serverVersion: SERVER_VERSION,\n tier: ctx.tier,\n companyCount,\n supabaseConnected: supabaseCheck.connected,\n schemaCompatible: supabaseCheck.compatible,\n schemaVersion: supabaseCheck.schemaVersion,\n templateWarnings: getTemplateWarnings(),\n uptimeMs: health.uptimeMs,\n totalCalls: health.totalCalls,\n totalErrors: health.totalErrors,\n };\n}\n\nasync function checkSupabase(ctx: SessionContext): Promise<{\n connected: boolean;\n compatible: boolean;\n schemaVersion: number | null;\n}> {\n try {\n // Read schema_version from a config table — for now, check connectivity via a simple query\n const { error } = await ctx.supabase\n .from('companies')\n .select('id', { count: 'exact', head: true });\n\n if (error) {\n return { connected: false, compatible: false, schemaVersion: null };\n }\n\n // Schema version check — hardcoded until we add a config table\n const schemaVersion = MIN_SCHEMA_VERSION;\n return {\n connected: true,\n compatible: schemaVersion >= MIN_SCHEMA_VERSION,\n schemaVersion,\n };\n } catch {\n return { connected: false, compatible: false, schemaVersion: null };\n }\n}\n\nasync function countCompanies(ctx: SessionContext): Promise<number> {\n try {\n const { count, error } = await ctx.supabase\n .from('companies')\n .select('id', { count: 'exact', head: true });\n\n if (error || count === null) return 0;\n return count;\n } catch {\n return 0;\n }\n}\n\nfunction buildHealthMessage(health: HealthResult): string {\n const parts: string[] = [];\n\n if (health.supabaseConnected) {\n parts.push(`Forcefield v${health.serverVersion} — connected`);\n } else {\n parts.push(`Forcefield v${health.serverVersion} — backend unavailable (local scanning still works)`);\n }\n\n parts.push(`Tier: ${health.tier}`);\n parts.push(`Companies: ${health.companyCount}`);\n parts.push(`Uptime: ${formatUptime(health.uptimeMs)}`);\n\n if (health.totalCalls > 0) {\n parts.push(`Calls: ${health.totalCalls} (${health.totalErrors} errors)`);\n }\n\n if (!health.schemaCompatible && health.supabaseConnected) {\n parts.push('Schema outdated — run `npm update -g @forcefield/mcp-server` to update');\n }\n\n if (health.templateWarnings.length > 0) {\n parts.push(`${health.templateWarnings.length} template warning(s)`);\n }\n\n return parts.join('. ') + '.';\n}\n\nfunction buildMetricsMessage(\n health: { status: string; uptimeMs: number; totalCalls: number; totalErrors: number },\n toolCount: number,\n): string {\n const errorRate = health.totalCalls > 0\n ? ((health.totalErrors / health.totalCalls) * 100).toFixed(1)\n : '0.0';\n return (\n `Status: ${health.status}. ` +\n `Uptime: ${formatUptime(health.uptimeMs)}. ` +\n `${health.totalCalls} calls across ${toolCount} tool actions (${errorRate}% error rate).`\n );\n}\n\nfunction formatUptime(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ${seconds % 60}s`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m`;\n}\n\n// === Audit log ===\n\ninterface AuditLogQuery {\n toolName?: string;\n companyId?: string;\n startDate?: string;\n endDate?: string;\n limit: number;\n offset: number;\n}\n\ninterface AuditLogResult {\n items: Array<{\n id: string;\n toolName: string;\n action: string;\n companyId: string | null;\n result: string;\n createdAt: string;\n }>;\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\nasync function queryAuditLog(ctx: SessionContext, query: AuditLogQuery): Promise<AuditLogResult> {\n let builder = ctx.supabase\n .from('audit_log')\n .select('id, tool_name, action, company_id, result_summary, created_at', { count: 'exact' })\n .eq('user_id', ctx.userId)\n .order('created_at', { ascending: false });\n\n if (query.toolName) {\n builder = builder.eq('tool_name', query.toolName);\n }\n if (query.companyId) {\n builder = builder.eq('company_id', query.companyId);\n }\n if (query.startDate) {\n builder = builder.gte('created_at', query.startDate);\n }\n if (query.endDate) {\n builder = builder.lte('created_at', `${query.endDate}T23:59:59.999Z`);\n }\n\n builder = builder.range(query.offset, query.offset + query.limit - 1);\n\n const { data, error, count } = await builder;\n\n if (error) {\n throw ffError.internal(`Failed to query audit log: ${error.message}`);\n }\n\n const total = count ?? 0;\n const items = (data ?? []).map((row) => ({\n id: row.id,\n toolName: row.tool_name,\n action: row.action,\n companyId: row.company_id,\n result: (row.result_summary as { status?: string } | null)?.status ?? 'unknown',\n createdAt: row.created_at,\n }));\n\n return {\n items,\n total,\n limit: query.limit,\n offset: query.offset,\n hasMore: query.offset + items.length < total,\n };\n}\n","/**\n * tools/ff-vault.ts — ff_vault tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n * Implements: upload, get, metadata, search, list, delete, store_sensitive, get_sensitive.\n * Stubs: save_generated (Phase 4).\n */\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport type { SessionContext } from '../auth/session.js';\nimport { FfVaultSchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { validateCompanyOwnership } from '../auth/ownership.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport { validateScanPath } from '../domain/scanning/path-validator.js';\nimport { extractText, isSupportedExtension } from '../domain/scanning/extract-text.js';\nimport { classifyDocument } from '../domain/classification/classifier.js';\nimport { createDocument, getDocumentById, getDocumentMetadata, buildStoragePath, searchDocuments, listDocuments, deleteDocument } from '../db/documents.js';\nimport { storeSensitive, listSensitive } from '../db/sensitive.js';\n\nconst TOOL_NAME = 'ff_vault';\nconst MAX_VAULT_FILE_SIZE = 25 * 1024 * 1024; // 25MB\n\n/** MIME types by extension (common corporate document formats). */\nconst MIME_MAP: Record<string, string> = {\n '.pdf': 'application/pdf',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.doc': 'application/msword',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.xls': 'application/vnd.ms-excel',\n '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n '.rtf': 'application/rtf',\n '.html': 'text/html',\n '.htm': 'text/html',\n '.csv': 'text/csv',\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n};\n\nfunction lookupMime(filePath: string): string {\n return MIME_MAP[extname(filePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\nexport async function handleFfVault(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n _extra?: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfVaultSchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Steps 3-5: Authorize → Execute → Respond (per action)\n switch (input.action) {\n case 'upload': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const resolvedPath = await validateScanPath(input.filePath);\n\n // Size check (vault limit: 25MB)\n const stats = await stat(resolvedPath);\n if (stats.size > MAX_VAULT_FILE_SIZE) {\n throw ffError.fileTooLarge(stats.size);\n }\n\n const mimeType = lookupMime(resolvedPath);\n const fileName = basename(resolvedPath);\n const title = input.title ?? fileName;\n\n // Extract text (graceful degradation: corrupted files stored without text)\n let extractedText: string | null = null;\n let extractionStatus: 'ok' | 'text_unavailable' = 'ok';\n\n if (isSupportedExtension(resolvedPath)) {\n try {\n const result = await extractText(resolvedPath);\n extractedText = result.text;\n } catch (err) {\n logger.warn('Text extraction failed, storing without text', {\n filePath: fileName,\n error: err instanceof Error ? err.message : 'Unknown',\n });\n extractionStatus = 'text_unavailable';\n }\n } else {\n // Unsupported format for text extraction — still allow upload\n extractionStatus = 'text_unavailable';\n }\n\n // Auto-classify\n const documentClass = extractedText\n ? classifyDocument(extractedText, title)\n : 'unclassified';\n\n // Pre-generate UUID for storage path\n const documentId = randomUUID();\n const storagePath = buildStoragePath(ctx.userId, input.companyId, documentId, fileName);\n\n // Upload to Supabase Storage\n const fileBuffer = await readFile(resolvedPath);\n const { error: uploadError } = await ctx.supabase.storage\n .from('documents')\n .upload(storagePath, fileBuffer, { contentType: mimeType });\n\n if (uploadError) {\n throw ffError.uploadFailed(fileName, uploadError.message);\n }\n\n // Insert DB row\n const doc = await createDocument(ctx.supabase, {\n id: documentId,\n companyId: input.companyId,\n userId: ctx.userId,\n title,\n documentType: input.documentType,\n documentClass,\n description: input.description,\n fileName,\n fileSize: stats.size,\n mimeType,\n storagePath,\n extractedText: extractedText ?? undefined,\n extractionStatus,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'upload',\n companyId: input.companyId,\n input: { fileName, documentClass, fileSize: stats.size },\n result: 'success',\n });\n\n // Text excerpt (first 500 chars)\n const textExcerpt = extractedText\n ? extractedText.slice(0, 500) + (extractedText.length > 500 ? '...' : '')\n : null;\n\n done();\n return formatToolResponse({\n data: {\n documentId: doc.id,\n title: doc.title,\n fileName: doc.fileName,\n fileSize: doc.fileSize,\n mimeType: doc.mimeType,\n documentClass: doc.documentClass,\n extractionStatus: doc.extractionStatus,\n textExcerpt,\n },\n message: `Uploaded ${fileName} → classified as \"${documentClass}\". ${extractionStatus === 'ok' ? 'Text extracted.' : 'Stored without text extraction.'}`,\n nextAction: { tool: 'ff_vault', action: 'search', params: { companyId: input.companyId } },\n prompt: 'Document saved to vault. Would you like to search your documents or upload another?',\n });\n }\n\n case 'get': {\n // Step 3: Authorize (RLS handles it — user can only see their own docs)\n const doc = await getDocumentById(ctx.supabase, input.documentId);\n if (!doc) {\n throw ffError.notFound('document', input.documentId);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'get',\n input: { documentId: input.documentId },\n result: 'success',\n });\n\n const getMessage = doc.extractionStatus === 'text_unavailable'\n ? `Retrieved \"${doc.title}\" (${doc.documentClass}). Text extraction failed. The original file is available for download.`\n : `Retrieved \"${doc.title}\" (${doc.documentClass}).`;\n\n done();\n return formatToolResponse({\n data: doc,\n message: getMessage,\n });\n }\n\n case 'metadata': {\n // Step 3: Authorize (RLS handles it)\n const meta = await getDocumentMetadata(ctx.supabase, input.documentId);\n if (!meta) {\n throw ffError.notFound('document', input.documentId);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'metadata',\n input: { documentId: input.documentId },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: meta,\n message: `Metadata for \"${meta.title}\" (${meta.documentClass}, ${formatSize(meta.fileSize)}).`,\n });\n }\n\n case 'search': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const results = await searchDocuments(\n ctx.supabase,\n input.companyId,\n input.query,\n input.limit,\n input.offset,\n );\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'search',\n companyId: input.companyId,\n input: { query: input.query, limit: input.limit, offset: input.offset },\n result: 'success',\n });\n\n // Step 5: Respond\n if (results.total === 0) {\n done();\n return formatToolResponse({\n data: results,\n message: `No documents match '${input.query}'. Try different keywords or upload more documents.`,\n nextAction: { tool: 'ff_vault', action: 'upload', params: { companyId: input.companyId } },\n prompt: 'No results found. Would you like to upload a document or try a different search?',\n });\n }\n\n done();\n return formatToolResponse({\n data: results,\n message: `Found ${results.total} document${results.total === 1 ? '' : 's'} matching '${input.query}'.`,\n prompt: results.hasMore\n ? 'More results available. Would you like to see the next page or refine your search?'\n : undefined,\n });\n }\n\n case 'list': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const listResults = await listDocuments(\n ctx.supabase,\n input.companyId,\n { documentClass: input.documentClass },\n input.limit,\n input.offset,\n );\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'list',\n companyId: input.companyId,\n input: { documentClass: input.documentClass, limit: input.limit, offset: input.offset },\n result: 'success',\n });\n\n // Step 5: Respond\n if (listResults.total === 0) {\n done();\n return formatToolResponse({\n data: listResults,\n message: 'No documents in vault. Upload documents with ff_vault(action: upload).',\n nextAction: { tool: 'ff_vault', action: 'upload', params: { companyId: input.companyId } },\n prompt: 'Your vault is empty. Would you like to upload a document?',\n });\n }\n\n done();\n return formatToolResponse({\n data: listResults,\n message: `${listResults.total} document${listResults.total === 1 ? '' : 's'} in vault.`,\n prompt: listResults.hasMore\n ? 'More documents available. Would you like to see the next page?'\n : undefined,\n });\n }\n\n case 'delete': {\n // Step 3: Authorize (RLS handles it — same as get)\n // Step 4: Execute\n const deleteResult = await deleteDocument(ctx.supabase, input.documentId);\n if (!deleteResult) {\n throw ffError.notFound('document', input.documentId);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'delete',\n input: { documentId: input.documentId },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: { documentId: input.documentId, deleted: true },\n message: `Permanently removed \"${deleteResult.fileName}\". Linked data has been preserved.`,\n nextAction: { tool: 'ff_vault', action: 'list' },\n });\n }\n\n case 'store_sensitive': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute — encryption happens in PostgreSQL\n const sensitiveId = await storeSensitive(ctx.supabase, {\n companyId: input.companyId,\n userId: ctx.userId,\n dataType: input.dataType,\n label: input.label,\n value: input.value,\n });\n\n // Audit log: NEVER log plaintext value\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'store_sensitive',\n companyId: input.companyId,\n input: { dataType: input.dataType, label: input.label },\n result: 'success',\n });\n\n // Step 5: Respond — never echo back the value\n done();\n return formatToolResponse({\n data: { id: sensitiveId, stored: true, dataType: input.dataType, label: input.label },\n message: `Stored ${input.dataType} \"${input.label}\" securely. Value encrypted at rest.`,\n nextAction: { tool: TOOL_NAME, action: 'get_sensitive', params: { companyId: input.companyId } },\n prompt: 'Would you like to view stored sensitive data or store another value?',\n });\n }\n\n case 'get_sensitive': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute — decryption happens in PostgreSQL\n const items = await listSensitive(ctx.supabase, input.companyId, input.dataType);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'get_sensitive',\n companyId: input.companyId,\n input: { dataType: input.dataType ?? null },\n result: 'success',\n });\n\n // Step 5: Respond — PII masker handles masking unless showSensitive\n if (items.length === 0) {\n done();\n return formatToolResponse({\n data: { items: [], total: 0 },\n message: input.dataType\n ? `No ${input.dataType} values stored for this company.`\n : 'No sensitive data stored for this company.',\n nextAction: { tool: TOOL_NAME, action: 'store_sensitive', params: { companyId: input.companyId } },\n prompt: 'Would you like to store a sensitive value?',\n });\n }\n\n done();\n return formatToolResponse(\n {\n data: { items, total: items.length },\n message: `${items.length} sensitive ${items.length === 1 ? 'value' : 'values'} retrieved.`,\n },\n { showSensitive: input.showSensitive },\n );\n }\n\n case 'save_generated': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute — retrieve generated document info + fresh signed URL\n const generatedDoc = await getDocumentById(ctx.supabase, input.documentId);\n if (!generatedDoc) {\n throw ffError.notFound('document', input.documentId);\n }\n\n if (generatedDoc.source !== 'generated') {\n throw ffError.validationFailed([{\n path: 'documentId',\n message: 'This document was not created by the generate action. Use ff_vault get instead.',\n }]);\n }\n\n // Generate fresh signed URL\n const { data: signedData, error: signedError } = await ctx.supabase.storage\n .from('documents')\n .createSignedUrl(generatedDoc.storagePath, 3600);\n\n const downloadUrl = signedError ? null : signedData?.signedUrl ?? null;\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'save_generated',\n companyId: input.companyId,\n input: { documentId: input.documentId },\n result: 'success',\n });\n\n done();\n return formatToolResponse({\n data: {\n documentId: generatedDoc.id,\n title: generatedDoc.title,\n fileName: generatedDoc.fileName,\n fileSize: generatedDoc.fileSize,\n mimeType: generatedDoc.mimeType,\n documentClass: generatedDoc.documentClass,\n source: generatedDoc.source,\n sourceMetadata: generatedDoc.sourceMetadata,\n downloadUrl,\n createdAt: generatedDoc.createdAt,\n },\n message: `Retrieved generated document \"${generatedDoc.title}\".${downloadUrl ? ' Download URL valid for 1 hour.' : ''}`,\n });\n }\n }\n } catch (error) {\n done(true);\n\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: 'TIER_RESTRICTED',\n });\n return formatErrorResponse(ffErr);\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n result: 'error',\n errorCode: error instanceof Error ? error.constructor.name : 'unknown',\n });\n\n return formatErrorResponse(error);\n }\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${bytes}B`;\n}\n","/**\n * domain/classification/classifier.ts — Score-based document auto-classification.\n *\n * Pure function, no I/O. Counts keyword hits per class, highest wins.\n * Title hint provides secondary signal (filename often contains type info).\n */\n\nimport type { DocumentClass } from './types.js';\n\ninterface ClassRule {\n keywords: string[];\n titleKeywords?: string[];\n}\n\nconst CLASS_RULES: Record<Exclude<DocumentClass, 'unclassified'>, ClassRule> = {\n certificate_of_incorporation: {\n keywords: [\n 'certificate of incorporation',\n 'articles of incorporation',\n 'incorporator',\n 'authorized shares',\n 'par value',\n 'registered office',\n 'certificate of formation',\n ],\n titleKeywords: ['incorporation', 'certificate', 'articles', 'formation'],\n },\n operating_agreement: {\n keywords: [\n 'operating agreement',\n 'limited liability company',\n 'member interest',\n 'membership interest',\n 'capital contribution',\n 'managing member',\n 'distributions',\n 'llc agreement',\n ],\n titleKeywords: ['operating agreement', 'llc agreement'],\n },\n safe: {\n keywords: [\n 'simple agreement for future equity',\n 'safe',\n 'valuation cap',\n 'discount rate',\n 'equity financing',\n 'liquidity event',\n 'dissolution event',\n 'post-money valuation cap',\n 'pre-money valuation cap',\n ],\n titleKeywords: ['safe', 'simple agreement'],\n },\n convertible_note: {\n keywords: [\n 'convertible note',\n 'convertible promissory note',\n 'principal amount',\n 'maturity date',\n 'conversion price',\n 'interest rate',\n 'qualified financing',\n ],\n titleKeywords: ['convertible note', 'promissory note'],\n },\n stock_purchase_agreement: {\n keywords: [\n 'stock purchase agreement',\n 'share purchase',\n 'restricted stock',\n 'purchase price per share',\n 'repurchase option',\n 'vesting schedule',\n 'series a',\n 'series b',\n 'preferred stock',\n ],\n titleKeywords: ['stock purchase', 'spa', 'share purchase', 'series'],\n },\n option_grant: {\n keywords: [\n 'stock option',\n 'option grant',\n 'exercise price',\n 'strike price',\n 'vesting',\n 'incentive stock option',\n 'non-qualified stock option',\n 'iso',\n 'nso',\n 'option plan',\n ],\n titleKeywords: ['option grant', 'stock option', 'iso', 'nso'],\n },\n board_consent: {\n keywords: [\n 'board of directors',\n 'unanimous written consent',\n 'board consent',\n 'resolved',\n 'whereas',\n 'board resolution',\n 'directors consent',\n ],\n titleKeywords: ['board consent', 'board resolution', 'director consent'],\n },\n shareholder_consent: {\n keywords: [\n 'stockholder consent',\n 'shareholder consent',\n 'stockholder approval',\n 'shareholder resolution',\n 'written consent of stockholders',\n 'majority stockholders',\n ],\n titleKeywords: ['stockholder', 'shareholder consent'],\n },\n meeting_minutes: {\n keywords: [\n 'meeting minutes',\n 'minutes of meeting',\n 'annual meeting',\n 'called to order',\n 'quorum',\n 'adjourned',\n 'meeting of the board',\n ],\n titleKeywords: ['minutes', 'meeting minutes'],\n },\n tax_filing: {\n keywords: [\n 'form 1120',\n 'form 1065',\n 'form 1120s',\n 'form 941',\n 'form 940',\n 'form w-2',\n 'form 1099',\n 'tax return',\n 'internal revenue service',\n 'taxable income',\n 'estimated tax',\n ],\n titleKeywords: ['1120', '1065', '941', '940', 'w-2', '1099', 'tax return', 'tax filing'],\n },\n state_filing: {\n keywords: [\n 'annual report',\n 'biennial report',\n 'statement of information',\n 'franchise tax report',\n 'public information report',\n 'secretary of state',\n 'business entity report',\n 'periodic report',\n ],\n titleKeywords: ['annual report', 'biennial', 'statement of information', 'franchise tax'],\n },\n irs_correspondence: {\n keywords: [\n 'department of the treasury',\n 'internal revenue service',\n 'ein confirmation',\n 'employer identification number',\n 'cp notice',\n 'irs notice',\n 'determination letter',\n 'letter 147c',\n ],\n titleKeywords: ['irs', 'ein', 'cp notice', '147c', 'determination letter'],\n },\n employment_agreement: {\n keywords: [\n 'employment agreement',\n 'offer letter',\n 'at-will employment',\n 'base salary',\n 'compensation',\n 'termination',\n 'severance',\n 'non-compete',\n 'confidential information',\n 'invention assignment',\n ],\n titleKeywords: ['employment', 'offer letter', 'employment agreement'],\n },\n nda: {\n keywords: [\n 'non-disclosure agreement',\n 'nondisclosure',\n 'confidentiality agreement',\n 'confidential information',\n 'disclosing party',\n 'receiving party',\n 'mutual nda',\n ],\n titleKeywords: ['nda', 'non-disclosure', 'confidentiality agreement'],\n },\n bylaws: {\n keywords: [\n 'bylaws',\n 'by-laws',\n 'corporate bylaws',\n 'stockholders meeting',\n 'board of directors',\n 'officers',\n 'indemnification',\n 'fiscal year',\n 'corporate seal',\n 'amendment of bylaws',\n ],\n titleKeywords: ['bylaws', 'by-laws'],\n },\n};\n\n/** Weight: text body hit = 1, title hint hit = 2 */\nconst TITLE_WEIGHT = 2;\n\n/** Minimum score to classify (avoids false positives on sparse text). */\nconst MIN_SCORE = 1;\n\n/**\n * Classify a document by its extracted text and optional title hint.\n * Returns the best-matching DocumentClass, or 'unclassified' if no strong match.\n */\nexport function classifyDocument(text: string, titleHint?: string): DocumentClass {\n const lowerText = text.toLowerCase();\n const lowerTitle = titleHint?.toLowerCase() ?? '';\n\n let bestClass: DocumentClass = 'unclassified';\n let bestScore = 0;\n\n for (const [docClass, rule] of Object.entries(CLASS_RULES) as Array<\n [Exclude<DocumentClass, 'unclassified'>, ClassRule]\n >) {\n let score = 0;\n\n for (const kw of rule.keywords) {\n if (lowerText.includes(kw)) score += 1;\n }\n\n if (lowerTitle && rule.titleKeywords) {\n for (const kw of rule.titleKeywords) {\n if (lowerTitle.includes(kw)) score += TITLE_WEIGHT;\n }\n }\n\n if (score > bestScore) {\n bestScore = score;\n bestClass = docClass;\n }\n }\n\n return bestScore >= MIN_SCORE ? bestClass : 'unclassified';\n}\n","/**\n * db/sensitive.ts — Encrypted sensitive data operations via Supabase RPC.\n *\n * All encryption/decryption happens in PostgreSQL (SECURITY DEFINER functions).\n * The JS client never sees the encryption key.\n *\n * Note: RPC functions are cast via `unknown` because the generated Supabase types\n * don't include custom RPCs until `supabase gen types` is re-run after migration.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface SensitiveDataRecord {\n id: string;\n companyId: string;\n userId: string;\n dataType: string;\n label: string;\n decryptedValue: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SensitiveDataCreateInput {\n companyId: string;\n userId: string;\n dataType: string;\n label: string;\n value: string;\n}\n\n// --- Untyped RPC helper ---\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype UntypedRpc = (fn: string, params: Record<string, unknown>) => PromiseLike<{ data: any; error: any }>;\n\nfunction rpc(supabase: TypedSupabaseClient): UntypedRpc {\n return (supabase as unknown as { rpc: UntypedRpc }).rpc.bind(supabase);\n}\n\n// --- Operations ---\n\n/**\n * Store an encrypted sensitive data value via RPC.\n * Returns the new record's UUID.\n */\nexport async function storeSensitive(\n supabase: TypedSupabaseClient,\n input: SensitiveDataCreateInput,\n): Promise<string> {\n const { data, error } = await rpc(supabase)('store_sensitive_data', {\n p_company_id: input.companyId,\n p_user_id: input.userId,\n p_data_type: input.dataType,\n p_label: input.label,\n p_value: input.value,\n });\n\n if (error) throw error;\n\n return data as string;\n}\n\n/**\n * List sensitive data for a company, optionally filtered by dataType.\n * Returns decrypted values (decryption happens in PostgreSQL).\n */\nexport async function listSensitive(\n supabase: TypedSupabaseClient,\n companyId: string,\n dataType?: string,\n): Promise<SensitiveDataRecord[]> {\n const { data, error } = await rpc(supabase)('list_sensitive_data', {\n p_company_id: companyId,\n p_data_type: dataType ?? null,\n });\n\n if (error) throw error;\n\n const rows = (data ?? []) as Array<Record<string, unknown>>;\n return rows.map(mapRow);\n}\n\n/**\n * Delete a sensitive data record by ID.\n * Returns true if deleted, false if not found.\n */\nexport async function deleteSensitive(\n supabase: TypedSupabaseClient,\n id: string,\n): Promise<boolean> {\n const { data, error } = await rpc(supabase)('delete_sensitive_data', {\n p_id: id,\n });\n\n if (error) throw error;\n\n return data as boolean;\n}\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): SensitiveDataRecord {\n return {\n id: row.id as string,\n companyId: row.company_id as string,\n userId: row.user_id as string,\n dataType: row.data_type as string,\n label: row.label as string,\n decryptedValue: row.decrypted_value as string,\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n","/**\n * tools/ff-generate.ts — ff_generate tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n * Implements: list_templates, preview, start_interview, answer_interview, generate.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { SessionContext } from '../auth/session.js';\nimport { FfGenerateSchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { validateCompanyOwnership } from '../auth/ownership.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport {\n loadTemplateIndex,\n loadFullTemplate,\n loadTemplatePreview,\n} from '../domain/templates/template-loader.js';\nimport { prefillVariables } from '../domain/templates/pre-filler.js';\nimport { normalizeQuestionFlow, computeNextStep } from '../domain/templates/interview-engine.js';\nimport { renderMarkdown, extractPostGenInstructions } from '../domain/templates/document-renderer.js';\nimport { markdownToDocx } from '../domain/templates/docx-generator.js';\nimport { convertDocxToPdf } from '../domain/templates/pdf-converter.js';\nimport { categoryToDocumentClass } from '../domain/templates/category-mapping.js';\nimport { getCompanyById } from '../db/companies.js';\nimport { getDeadlineById } from '../db/deadlines.js';\nimport { createDocument, buildStoragePath } from '../db/documents.js';\nimport {\n findActiveSession,\n createSession,\n getSessionById,\n updateSessionAnswers,\n completeSession,\n} from '../db/interview-sessions.js';\nimport { getConfig } from '../config.js';\n\nconst TOOL_NAME = 'ff_generate';\n\n/**\n * Build template variable pre-fill context from a deadline.\n * Maps deadline fields to common template variable names.\n */\nasync function buildDeadlineContext(\n ctx: SessionContext,\n deadlineId: string,\n companyId: string,\n): Promise<Record<string, unknown>> {\n try {\n const deadline = await getDeadlineById(ctx.supabase, deadlineId);\n if (!deadline) return {};\n\n // Security: verify deadline belongs to the same company\n if (deadline.companyId !== companyId) return {};\n\n const context: Record<string, unknown> = {};\n\n if (deadline.dueDate) {\n context.meeting_date = deadline.dueDate;\n context.deadline_date = deadline.dueDate;\n context.filing_due_date = deadline.dueDate;\n }\n if (deadline.category) {\n context.filing_type = deadline.category;\n context.deadline_category = deadline.category;\n }\n if (deadline.jurisdiction) {\n context.governing_law_state = deadline.jurisdiction;\n }\n if (deadline.filingName) {\n context.related_filing = deadline.filingName;\n }\n\n return context;\n } catch {\n return {};\n }\n}\n\nexport async function handleFfGenerate(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n _extra?: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfGenerateSchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Step 3: Authorize — per action below\n\n // Steps 4-5: Execute → Respond (per action)\n switch (input.action) {\n case 'list_templates': {\n const allTemplates = await loadTemplateIndex();\n\n const filtered = input.category\n ? allTemplates.filter((t) => t.category === input.category)\n : allTemplates;\n\n // Collect unique categories\n const categories = [...new Set(allTemplates.map((t) => t.category))].sort();\n\n done();\n return formatToolResponse({\n data: {\n templates: filtered.map((t) => ({\n templateId: t.template_id,\n name: t.name,\n category: t.category,\n })),\n totalCount: filtered.length,\n categories,\n },\n message: input.category\n ? `Found ${filtered.length} template(s) in category \"${input.category}\".`\n : `${filtered.length} templates available across ${categories.length} categories.`,\n });\n }\n\n case 'preview': {\n let template: Record<string, unknown>;\n try {\n template = await loadFullTemplate(input.templateId);\n } catch {\n throw ffError.notFound('template', input.templateId);\n }\n\n let previewContent: string;\n try {\n previewContent = await loadTemplatePreview(input.templateId);\n } catch {\n throw ffError.notFound('template preview', input.templateId);\n }\n\n const requiredVars = template.required_variables as Array<{\n name: string;\n type: string;\n required: boolean;\n title?: string;\n }> | undefined;\n\n done();\n return formatToolResponse({\n data: {\n templateId: input.templateId,\n name: template.name as string,\n category: template.category as string,\n entityTypes: template.entity_types as string[],\n requiredVariables: (requiredVars ?? []).map((v) => ({\n name: v.name,\n type: v.type,\n required: v.required,\n title: v.title,\n })),\n preview: previewContent,\n },\n message: `Preview for \"${template.name as string}\". Use ff_generate with action \"start_interview\" to begin filling out this document.`,\n nextAction: {\n tool: 'ff_generate',\n action: 'start_interview',\n params: { templateId: input.templateId },\n },\n });\n }\n\n case 'start_interview': {\n // Step 3: Authorize — company ownership\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Load template\n let template: Record<string, unknown>;\n try {\n template = await loadFullTemplate(input.templateId);\n } catch {\n throw ffError.notFound('template', input.templateId);\n }\n\n // Load company for pre-fill\n const company = await getCompanyById(ctx.supabase, input.companyId);\n if (!company) {\n throw ffError.notFound('company', input.companyId);\n }\n\n // Check for existing active session (resume flow)\n const existing = await findActiveSession(ctx.supabase, ctx.userId, input.companyId, input.templateId);\n\n const steps = normalizeQuestionFlow(template);\n const templateName = template.name as string;\n\n let sessionId: string;\n let preFilled: Record<string, unknown>;\n let answers: Record<string, unknown>;\n let resumed: boolean;\n\n if (existing) {\n // Resume existing session\n sessionId = existing.id;\n preFilled = existing.preFilled;\n answers = existing.answers;\n resumed = true;\n } else {\n // Create new session with pre-fill\n const requiredVars = (template.required_variables ?? []) as Array<{\n name: string;\n company_field_path: string | null;\n }>;\n\n // Build deadline context if deadlineId provided\n const deadlineContext = input.deadlineId\n ? await buildDeadlineContext(ctx, input.deadlineId, input.companyId)\n : {};\n\n const companyPreFill = prefillVariables(company, requiredVars);\n\n // Merge: deadline context first, company values override\n preFilled = { ...deadlineContext, ...companyPreFill };\n answers = {};\n resumed = false;\n\n const session = await createSession(ctx.supabase, {\n userId: ctx.userId,\n companyId: input.companyId,\n templateId: input.templateId,\n preFilled,\n });\n sessionId = session.id;\n }\n\n // Compute next step\n const next = computeNextStep(steps, answers, preFilled);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'start_interview',\n companyId: input.companyId,\n result: 'success',\n input: { templateId: input.templateId, resumed },\n });\n\n done();\n\n if (!next) {\n // All variables already known — skip straight to completion\n return formatToolResponse({\n data: {\n sessionId,\n templateName,\n status: 'complete',\n totalSteps: steps.length,\n preFilled,\n answers,\n allVariables: { ...preFilled, ...answers },\n },\n message: `All variables for \"${templateName}\" are already filled from your company profile. Ready to generate.`,\n nextAction: {\n tool: 'ff_generate',\n action: 'generate',\n params: { templateId: input.templateId, sessionId },\n },\n });\n }\n\n return formatToolResponse({\n data: {\n sessionId,\n templateName,\n status: resumed ? 'resumed' : 'started',\n totalSteps: steps.length,\n currentStepIndex: next.stepIndex,\n preFilled,\n nextStep: {\n stepNumber: next.step.stepNumber,\n label: next.step.label,\n description: next.step.description,\n variables: next.step.variables.map((v) => ({\n name: v.name,\n type: v.type,\n required: v.required,\n title: v.title,\n question: v.question,\n ...(v.format && { format: v.format }),\n ...(v.default !== undefined && { default: v.default }),\n ...(v.enum && { enum: v.enum }),\n })),\n },\n },\n message: resumed\n ? `Resuming interview for \"${templateName}\". ${Object.keys(preFilled).length} field(s) pre-filled, ${Object.keys(answers).length} already answered.`\n : `Starting interview for \"${templateName}\". ${Object.keys(preFilled).length} field(s) pre-filled from your company profile.`,\n prompt: next.step.variables.map((v) => v.question).join('\\n'),\n nextAction: {\n tool: 'ff_generate',\n action: 'answer_interview',\n params: { sessionId, companyId: input.companyId },\n },\n });\n }\n\n case 'answer_interview': {\n // Step 3: Authorize — company ownership\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Load session\n const session = await getSessionById(ctx.supabase, input.sessionId);\n if (!session) {\n throw ffError.notFound('interview session', input.sessionId);\n }\n if (session.status !== 'in_progress') {\n throw ffError.conflict('interview session', `Session is already ${session.status}.`);\n }\n\n // Load template for step computation\n let template: Record<string, unknown>;\n try {\n template = await loadFullTemplate(session.templateId);\n } catch {\n throw ffError.notFound('template', session.templateId);\n }\n\n const steps = normalizeQuestionFlow(template);\n const templateName = template.name as string;\n\n // Merge new answers into existing\n const mergedAnswers = { ...session.answers, ...input.answers };\n\n // Compute next step with merged state\n const next = computeNextStep(steps, mergedAnswers, session.preFilled);\n\n if (!next) {\n // Interview complete\n await updateSessionAnswers(ctx.supabase, session.id, mergedAnswers, steps.length);\n await completeSession(ctx.supabase, session.id);\n\n const allVariables = { ...session.preFilled, ...mergedAnswers };\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'answer_interview',\n companyId: input.companyId,\n result: 'success',\n input: { sessionId: input.sessionId, completed: true },\n });\n\n done();\n return formatToolResponse({\n data: {\n sessionId: session.id,\n templateName,\n status: 'complete',\n totalSteps: steps.length,\n allVariables,\n },\n message: `Interview for \"${templateName}\" is complete. All ${Object.keys(allVariables).length} variables collected. Ready to generate.`,\n nextAction: {\n tool: 'ff_generate',\n action: 'generate',\n params: { templateId: session.templateId, sessionId: session.id },\n },\n });\n }\n\n // Update session with merged answers\n await updateSessionAnswers(ctx.supabase, session.id, mergedAnswers, next.stepIndex);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'answer_interview',\n companyId: input.companyId,\n result: 'success',\n input: { sessionId: input.sessionId, answeredCount: Object.keys(input.answers).length },\n });\n\n done();\n return formatToolResponse({\n data: {\n sessionId: session.id,\n templateName,\n status: 'in_progress',\n totalSteps: steps.length,\n currentStepIndex: next.stepIndex,\n answeredSoFar: Object.keys(mergedAnswers).length,\n nextStep: {\n stepNumber: next.step.stepNumber,\n label: next.step.label,\n description: next.step.description,\n variables: next.step.variables.map((v) => ({\n name: v.name,\n type: v.type,\n required: v.required,\n title: v.title,\n question: v.question,\n ...(v.format && { format: v.format }),\n ...(v.default !== undefined && { default: v.default }),\n ...(v.enum && { enum: v.enum }),\n })),\n },\n },\n message: `Step ${next.step.stepNumber}: ${next.step.label ?? next.step.variables.map((v) => v.title).join(', ')}`,\n prompt: next.step.variables.map((v) => v.question).join('\\n'),\n nextAction: {\n tool: 'ff_generate',\n action: 'answer_interview',\n params: { sessionId: session.id, companyId: input.companyId },\n },\n });\n }\n\n case 'generate': {\n // Validate: at least one of sessionId or variables required\n if (!input.sessionId && !input.variables) {\n throw ffError.validationFailed([{\n path: 'sessionId|variables',\n message: 'At least one of sessionId or variables must be provided.',\n }]);\n }\n\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n // a. Load template\n let template: Record<string, unknown>;\n try {\n template = await loadFullTemplate(input.templateId);\n } catch {\n throw ffError.notFound('template', input.templateId);\n }\n\n const templateName = template.name as string;\n const templateCategory = template.category as string;\n const requiredVars = (template.required_variables ?? []) as Array<{\n name: string;\n type: string;\n required: boolean;\n title?: string;\n }>;\n\n // b. Load variables: session and/or direct\n let variables: Record<string, unknown> = {};\n\n if (input.sessionId) {\n const session = await getSessionById(ctx.supabase, input.sessionId);\n if (!session) {\n throw ffError.notFound('interview session', input.sessionId);\n }\n variables = { ...session.preFilled, ...session.answers };\n }\n\n if (input.variables) {\n // Direct variables override session values\n variables = { ...variables, ...input.variables };\n }\n\n // c. Load preview markdown\n let previewMarkdown: string;\n try {\n previewMarkdown = await loadTemplatePreview(input.templateId);\n } catch {\n throw ffError.notFound('template preview', input.templateId);\n }\n\n // d. Render markdown\n const renderedMarkdown = renderMarkdown(previewMarkdown, variables, requiredVars);\n\n // e. Extract post-gen instructions (from preview markdown before stripping)\n const postGenFromPreview = extractPostGenInstructions(previewMarkdown);\n const postGenFromTemplate = template.post_generation_instructions as string[] | undefined;\n const postGenerationInstructions = postGenFromTemplate?.length\n ? postGenFromTemplate\n : postGenFromPreview\n ? [postGenFromPreview]\n : [];\n\n // f. Generate DOCX\n const docxBuffer = await markdownToDocx(renderedMarkdown, templateName);\n\n // g. Try PDF (graceful degradation)\n let pdfBuffer: Buffer | null = null;\n let pdfSkipReason: string | null = null;\n try {\n const config = getConfig();\n if (config.CLOUDCONVERT_API_KEY) {\n pdfBuffer = await convertDocxToPdf(docxBuffer, config.CLOUDCONVERT_API_KEY);\n if (!pdfBuffer) pdfSkipReason = 'CloudConvert conversion returned no result.';\n } else {\n pdfSkipReason = 'No CloudConvert API key configured.';\n }\n } catch {\n pdfSkipReason = 'PDF conversion unavailable.';\n }\n\n // h. Pre-generate document IDs\n const docxDocId = randomUUID();\n const pdfDocId = pdfBuffer ? randomUUID() : null;\n\n // Sanitize template name for filename\n const safeTemplateName = templateName.replace(/[^a-zA-Z0-9-_ ]/g, '').replace(/\\s+/g, '-').toLowerCase();\n\n // i. Upload DOCX to Supabase Storage\n const docxFileName = `${safeTemplateName}.docx`;\n const docxStoragePath = buildStoragePath(ctx.userId, input.companyId, docxDocId, docxFileName);\n\n const { error: docxUploadError } = await ctx.supabase.storage\n .from('documents')\n .upload(docxStoragePath, docxBuffer, {\n contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n });\n\n if (docxUploadError) {\n throw ffError.uploadFailed(docxFileName, docxUploadError.message);\n }\n\n // j. Create DOCX document record\n const docxDocClass = categoryToDocumentClass(templateCategory);\n const sourceMetadata = {\n templateId: input.templateId,\n ...(input.sessionId && { sessionId: input.sessionId }),\n };\n\n await createDocument(ctx.supabase, {\n id: docxDocId,\n companyId: input.companyId,\n userId: ctx.userId,\n title: `${templateName} (DOCX)`,\n documentClass: docxDocClass,\n fileName: docxFileName,\n fileSize: docxBuffer.length,\n mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n storagePath: docxStoragePath,\n extractionStatus: 'text_unavailable',\n source: 'generated',\n sourceMetadata,\n });\n\n // Generate signed URL for DOCX\n const { data: docxSignedData, error: docxSignedError } = await ctx.supabase.storage\n .from('documents')\n .createSignedUrl(docxStoragePath, 3600);\n\n const docxDownloadUrl = docxSignedError ? null : docxSignedData?.signedUrl ?? null;\n\n // k. PDF: upload + create record (if available)\n let pdfDoc: { documentId: string; fileName: string; downloadUrl: string | null } | null = null;\n\n if (pdfBuffer && pdfDocId) {\n const pdfFileName = `${safeTemplateName}.pdf`;\n const pdfStoragePath = buildStoragePath(ctx.userId, input.companyId, pdfDocId, pdfFileName);\n\n const { error: pdfUploadError } = await ctx.supabase.storage\n .from('documents')\n .upload(pdfStoragePath, pdfBuffer, { contentType: 'application/pdf' });\n\n if (!pdfUploadError) {\n await createDocument(ctx.supabase, {\n id: pdfDocId,\n companyId: input.companyId,\n userId: ctx.userId,\n title: `${templateName} (PDF)`,\n documentClass: docxDocClass,\n fileName: pdfFileName,\n fileSize: pdfBuffer.length,\n mimeType: 'application/pdf',\n storagePath: pdfStoragePath,\n extractionStatus: 'text_unavailable',\n source: 'generated',\n sourceMetadata,\n });\n\n const { data: pdfSignedData, error: pdfSignedError } = await ctx.supabase.storage\n .from('documents')\n .createSignedUrl(pdfStoragePath, 3600);\n\n pdfDoc = {\n documentId: pdfDocId,\n fileName: pdfFileName,\n downloadUrl: pdfSignedError ? null : pdfSignedData?.signedUrl ?? null,\n };\n } else {\n logger.warn('PDF upload failed, continuing without PDF', {\n error: pdfUploadError.message,\n });\n }\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'generate',\n companyId: input.companyId,\n result: 'success',\n input: {\n templateId: input.templateId,\n sessionId: input.sessionId ?? null,\n hasPdf: !!pdfDoc,\n },\n });\n\n // Step 5: Respond\n const formatNotes: string[] = [];\n if (pdfSkipReason) {\n formatNotes.push(`PDF not generated: ${pdfSkipReason} DOCX is available for download.`);\n }\n\n done();\n return formatToolResponse({\n data: {\n templateName,\n templateId: input.templateId,\n renderedMarkdown,\n documents: {\n docx: {\n documentId: docxDocId,\n fileName: docxFileName,\n downloadUrl: docxDownloadUrl,\n },\n pdf: pdfDoc,\n },\n postGenerationInstructions,\n ...(formatNotes.length > 0 && { notes: formatNotes }),\n },\n message: pdfDoc\n ? `Generated \"${templateName}\". DOCX and PDF saved to vault.`\n : `Generated \"${templateName}\". DOCX saved to vault.`,\n });\n }\n }\n } catch (error) {\n done(true);\n\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n return formatErrorResponse(ffErr);\n }\n\n // Audit log for errors on company-scoped actions\n if (rawInput.companyId) {\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n companyId: rawInput.companyId as string,\n result: 'error',\n errorCode: error instanceof Error && 'code' in error ? (error as { code: string }).code : 'UNKNOWN',\n });\n }\n\n return formatErrorResponse(error);\n }\n}\n","/**\n * domain/templates/pre-filler.ts — Resolve company_field_path values.\n *\n * Maps template required_variables[].company_field_path to actual company data.\n * Supports: simple paths (\"name\"), dot notation (\"primary_contact.name\"),\n * and array index (\"officers[0].name\").\n *\n * Unknown paths return undefined — the user is prompted instead.\n */\n\nimport type { CompanyRecord } from '../../db/companies.js';\n\n/**\n * Resolve a single company_field_path against a CompanyRecord.\n * Returns undefined if path is null, empty, or resolves to nothing.\n */\nexport function resolveCompanyFieldPath(\n company: CompanyRecord,\n fieldPath: string | null,\n): unknown | undefined {\n if (!fieldPath) return undefined;\n\n const record = company as unknown as Record<string, unknown>;\n\n // Tokenize: split on dots and array brackets\n // \"officers[0].name\" → [\"officers\", \"0\", \"name\"]\n const segments = fieldPath\n .replace(/\\[(\\d+)\\]/g, '.$1')\n .split('.')\n .filter(Boolean);\n\n let current: unknown = record;\n\n for (const segment of segments) {\n if (current === null || current === undefined) return undefined;\n\n if (typeof current === 'object') {\n current = (current as Record<string, unknown>)[segment];\n } else {\n return undefined;\n }\n }\n\n // Don't return null or empty string as pre-filled — treat as missing\n if (current === null || current === '') return undefined;\n\n return current;\n}\n\ninterface VariableWithPath {\n name: string;\n company_field_path: string | null;\n}\n\n/**\n * Pre-fill variables from a company profile.\n * Returns a map of variable_name → resolved value for all non-empty matches.\n */\nexport function prefillVariables(\n company: CompanyRecord,\n requiredVariables: VariableWithPath[],\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const v of requiredVariables) {\n const resolved = resolveCompanyFieldPath(company, v.company_field_path);\n if (resolved !== undefined) {\n result[v.name] = resolved;\n }\n }\n\n return result;\n}\n","/**\n * domain/templates/condition-evaluator.ts — Simple condition string evaluator.\n *\n * Evaluates conditions from template required_variables[].condition strings.\n * Supports: == / ===, !=, includes. No eval() — parsed safely.\n *\n * Examples from data:\n * \"nda_term_type == 'fixed'\"\n * \"include_non_compete == true\"\n * \"work_arrangement != 'remote'\"\n * \"safe_type includes 'valuation_cap'\"\n */\n\ntype Operator = '==' | '!=' | 'includes';\n\ninterface ParsedCondition {\n variable: string;\n operator: Operator;\n value: string | boolean;\n}\n\n/**\n * Parse a condition string into structured parts.\n * Returns null if the condition can't be parsed.\n */\nfunction parseCondition(condition: string): ParsedCondition | null {\n const trimmed = condition.trim();\n\n // Try each operator (check multi-char operators first)\n for (const op of ['===', '==', '!=', 'includes'] as const) {\n const parts = trimmed.split(new RegExp(`\\\\s+${op === 'includes' ? op : op.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\s+`));\n if (parts.length === 2) {\n const variable = parts[0]!.trim();\n const rawValue = parts[1]!.trim();\n const operator: Operator = op === '===' ? '==' : op;\n\n // Parse the right-hand side\n const value = parseValue(rawValue);\n\n return { variable, operator, value };\n }\n }\n\n return null;\n}\n\n/**\n * Parse a value literal — supports quoted strings and bare booleans.\n */\nfunction parseValue(raw: string): string | boolean {\n // Quoted string: 'fixed' or \"fixed\"\n if ((raw.startsWith(\"'\") && raw.endsWith(\"'\")) || (raw.startsWith('\"') && raw.endsWith('\"'))) {\n return raw.slice(1, -1);\n }\n // Boolean\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n // Bare string (unquoted)\n return raw;\n}\n\n/**\n * Evaluate a condition string against a variables map.\n *\n * Returns false for:\n * - Unparseable conditions\n * - Missing variables in the map\n */\nexport function evaluateCondition(\n condition: string,\n variables: Record<string, unknown>,\n): boolean {\n const parsed = parseCondition(condition);\n if (!parsed) return false;\n\n const actual = variables[parsed.variable];\n\n // Missing variable → condition not met\n if (actual === undefined) return false;\n\n switch (parsed.operator) {\n case '==':\n return actual == parsed.value;\n\n case '!=':\n return actual != parsed.value;\n\n case 'includes': {\n // String contains check\n const str = String(actual);\n return str.includes(String(parsed.value));\n }\n }\n}\n","/**\n * domain/templates/interview-engine.ts — Normalize question flows and compute next step.\n *\n * Bridges two question_flow formats into a single internal representation:\n * - Format 1 (22 templates): { order, variable_name, question_text } — one var per step\n * - Format 2 (10 templates): { step, label, description, variables[] } — grouped vars\n *\n * Conditions live on required_variables[].condition (both formats).\n */\n\nimport { evaluateCondition } from './condition-evaluator.js';\n\n// --- Public types ---\n\nexport interface VariableInfo {\n name: string;\n type: string;\n required: boolean;\n title: string;\n question: string;\n format?: string;\n default?: unknown;\n enum?: string[];\n condition?: string;\n companyFieldPath?: string | null;\n}\n\nexport interface NormalizedStep {\n stepNumber: number;\n label: string | null;\n description: string | null;\n variables: VariableInfo[];\n}\n\n// --- Raw template types (from JSON) ---\n\ninterface RawRequiredVariable {\n name: string;\n type: string;\n required: boolean;\n title?: string;\n description?: string;\n question?: string;\n format?: string;\n default?: unknown;\n enum?: string[];\n condition?: string;\n company_field_path?: string | null;\n}\n\ninterface RawFlowEntryFormat1 {\n order: number;\n variable_name: string;\n question_text: string;\n}\n\ninterface RawFlowEntryFormat2 {\n step: number;\n label: string;\n description: string;\n variables?: string[];\n}\n\n// --- Normalization ---\n\n/**\n * Normalize a template's question_flow into a uniform step list.\n * Requires both `required_variables` and `question_flow` from the template JSON.\n */\nexport function normalizeQuestionFlow(template: Record<string, unknown>): NormalizedStep[] {\n const requiredVars = (template.required_variables ?? []) as RawRequiredVariable[];\n const questionFlow = (template.question_flow ?? []) as Array<Record<string, unknown>>;\n\n // Build lookup: variable name → full variable info\n const varMap = new Map<string, RawRequiredVariable>();\n for (const v of requiredVars) {\n varMap.set(v.name, v);\n }\n\n // Detect format: Format 1 has `variable_name`, Format 2 has `step`\n const isFormat1 = questionFlow.length > 0 && 'variable_name' in questionFlow[0]!;\n\n if (isFormat1) {\n return normalizeFormat1(questionFlow as unknown as RawFlowEntryFormat1[], varMap);\n }\n return normalizeFormat2(questionFlow as unknown as RawFlowEntryFormat2[], varMap);\n}\n\nfunction normalizeFormat1(\n flow: RawFlowEntryFormat1[],\n varMap: Map<string, RawRequiredVariable>,\n): NormalizedStep[] {\n return flow.map((entry) => {\n const varInfo = varMap.get(entry.variable_name);\n return {\n stepNumber: entry.order,\n label: null,\n description: null,\n variables: [buildVariableInfo(entry.variable_name, varInfo, entry.question_text)],\n };\n });\n}\n\nfunction normalizeFormat2(\n flow: RawFlowEntryFormat2[],\n varMap: Map<string, RawRequiredVariable>,\n): NormalizedStep[] {\n const steps: NormalizedStep[] = [];\n\n for (const entry of flow) {\n // Skip steps with no variables (e.g., \"Generate\" steps)\n if (!entry.variables || entry.variables.length === 0) continue;\n\n const variables: VariableInfo[] = entry.variables.map((varName) => {\n const varInfo = varMap.get(varName);\n return buildVariableInfo(varName, varInfo, varInfo?.question ?? null);\n });\n\n steps.push({\n stepNumber: entry.step,\n label: entry.label,\n description: entry.description,\n variables,\n });\n }\n\n return steps;\n}\n\nfunction buildVariableInfo(\n name: string,\n raw: RawRequiredVariable | undefined,\n questionOverride: string | null,\n): VariableInfo {\n return {\n name,\n type: raw?.type ?? 'string',\n required: raw?.required ?? true,\n title: raw?.title ?? name,\n question: questionOverride ?? raw?.question ?? `Please provide ${name}`,\n ...(raw?.format && { format: raw.format }),\n ...(raw?.default !== undefined && { default: raw.default }),\n ...(raw?.enum && { enum: raw.enum }),\n ...(raw?.condition && { condition: raw.condition }),\n ...(raw?.company_field_path !== undefined && { companyFieldPath: raw.company_field_path }),\n };\n}\n\n// --- Step computation ---\n\n/**\n * Compute the next unanswered step, skipping steps/variables that are:\n * - Already answered (in `answers`)\n * - Already pre-filled (in `preFilled`)\n * - Conditional and condition is false\n *\n * Returns null when the interview is complete.\n */\nexport function computeNextStep(\n steps: NormalizedStep[],\n answers: Record<string, unknown>,\n preFilled: Record<string, unknown>,\n): { step: NormalizedStep; stepIndex: number } | null {\n const allKnown = { ...preFilled, ...answers };\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i]!;\n\n // Filter to variables that still need answering\n const remaining = step.variables.filter((v) => {\n // Already have a value?\n if (v.name in answers || v.name in preFilled) return false;\n\n // Conditional variable whose condition is not met?\n if (v.condition && !evaluateCondition(v.condition, allKnown)) return false;\n\n return true;\n });\n\n if (remaining.length > 0) {\n return {\n step: { ...step, variables: remaining },\n stepIndex: i,\n };\n }\n }\n\n return null; // All done\n}\n\n/**\n * Check if the interview is complete (no remaining unanswered steps).\n */\nexport function isInterviewComplete(\n steps: NormalizedStep[],\n answers: Record<string, unknown>,\n preFilled: Record<string, unknown>,\n): boolean {\n return computeNextStep(steps, answers, preFilled) === null;\n}\n","/**\n * domain/templates/document-renderer.ts — Render markdown templates with variable substitution.\n *\n * Preview markdown placeholders are `[Human Readable Text]`.\n * Variable names are snake_case. Matching is done via normalization.\n */\n\nexport interface RequiredVariable {\n name: string;\n type: string;\n required: boolean;\n title?: string;\n}\n\n/**\n * Convert a snake_case variable name to Title Case with digit spacing.\n * Example: `party1_company_name` → `Party 1 Company Name`\n */\nexport function toTitleCase(snakeName: string): string {\n return snakeName\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ')\n // Insert space before digit runs: \"Party1\" → \"Party 1\"\n .replace(/([a-zA-Z])(\\d)/g, '$1 $2');\n}\n\n/**\n * Normalize a string for fuzzy matching: lowercase, strip non-alphanumeric.\n */\nfunction normalize(text: string): string {\n return text.toLowerCase().replace(/[^a-z0-9]/g, '');\n}\n\ninterface SubstitutionEntry {\n variableName: string;\n value: unknown;\n /** Normalized candidate forms for matching. */\n candidates: string[];\n}\n\n/**\n * Build a substitution map from variables and their metadata.\n * Each variable produces candidate normalized strings for placeholder matching.\n */\nexport function buildSubstitutionMap(\n variables: Record<string, unknown>,\n requiredVars: RequiredVariable[],\n): SubstitutionEntry[] {\n const varMeta = new Map(requiredVars.map((v) => [v.name, v]));\n const entries: SubstitutionEntry[] = [];\n\n for (const [name, value] of Object.entries(variables)) {\n if (value === undefined || value === null || value === '') continue;\n\n const candidates: string[] = [];\n\n // Primary: from snake_case name → Title Case → normalized\n candidates.push(normalize(toTitleCase(name)));\n\n // Secondary: from the variable's title field\n const meta = varMeta.get(name);\n if (meta?.title) {\n candidates.push(normalize(meta.title));\n }\n\n entries.push({ variableName: name, value, candidates });\n }\n\n return entries;\n}\n\n/**\n * Find the best matching variable for a placeholder text.\n * Returns the SubstitutionEntry or null if no match.\n */\nfunction findBestMatch(\n placeholderText: string,\n entries: SubstitutionEntry[],\n): SubstitutionEntry | null {\n // Split on ':' and take first part (strip hint text)\n const beforeColon = placeholderText.split(':')[0]!;\n const normalizedPlaceholder = normalize(beforeColon);\n\n if (!normalizedPlaceholder) return null;\n\n // Pass 1: exact match\n for (const entry of entries) {\n for (const candidate of entry.candidates) {\n if (candidate === normalizedPlaceholder) return entry;\n }\n }\n\n // Pass 2: candidate is a prefix of placeholder (longest wins)\n let bestMatch: SubstitutionEntry | null = null;\n let bestLen = 0;\n\n for (const entry of entries) {\n for (const candidate of entry.candidates) {\n if (\n candidate.length > bestLen &&\n normalizedPlaceholder.startsWith(candidate)\n ) {\n bestMatch = entry;\n bestLen = candidate.length;\n }\n }\n }\n\n return bestMatch;\n}\n\n/**\n * Render a preview markdown template by substituting `[...]` placeholders\n * with variable values. Strips the Generation Notes section and header metadata.\n */\nexport function renderMarkdown(\n previewMarkdown: string,\n variables: Record<string, unknown>,\n requiredVars: RequiredVariable[],\n): string {\n const entries = buildSubstitutionMap(variables, requiredVars);\n\n // Track which variables have been used (prevent double-use)\n const used = new Set<string>();\n\n // Replace [placeholder] patterns\n let rendered = previewMarkdown.replace(\n /\\[([^\\]]+)\\]/g,\n (fullMatch, placeholderText: string) => {\n // Skip URL-like content (markdown links)\n if (fullMatch.startsWith('[') && previewMarkdown.includes(`](`) &&\n previewMarkdown.indexOf(`](`, previewMarkdown.indexOf(fullMatch)) -\n previewMarkdown.indexOf(fullMatch) < fullMatch.length + 5) {\n // More precise check: see if this bracket is immediately followed by (\n const idx = previewMarkdown.indexOf(fullMatch);\n const afterBracket = previewMarkdown.charAt(idx + fullMatch.length);\n if (afterBracket === '(') return fullMatch;\n }\n\n const match = findBestMatch(placeholderText, entries.filter((e) => !used.has(e.variableName)));\n if (match) {\n used.add(match.variableName);\n return String(match.value);\n }\n\n // Try again without the used filter (allow multi-use for same variable appearing multiple times)\n const matchWithReuse = findBestMatch(placeholderText, entries);\n if (matchWithReuse) {\n return String(matchWithReuse.value);\n }\n\n // No match — leave placeholder as-is\n return fullMatch;\n },\n );\n\n // Strip Generation Notes section (everything after `## Generation Notes`)\n const genNotesIdx = rendered.indexOf('## Generation Notes');\n if (genNotesIdx !== -1) {\n // Keep content before the heading, trim trailing whitespace/separators\n rendered = rendered.slice(0, genNotesIdx).replace(/\\n---\\s*$/, '').trimEnd();\n }\n\n // Strip header metadata block (lines before first `---`)\n const firstSeparator = rendered.indexOf('\\n---\\n');\n if (firstSeparator !== -1) {\n // Check if the first few lines look like metadata (Template ID, Category, etc.)\n const headerCandidate = rendered.slice(0, firstSeparator);\n if (headerCandidate.includes('**Template ID**') || headerCandidate.includes('**Category**')) {\n rendered = rendered.slice(firstSeparator + 5).trimStart(); // Skip past `\\n---\\n`\n }\n }\n\n return rendered;\n}\n\n/**\n * Extract post-generation instructions from the Generation Notes section.\n * Returns the text after \"**Post-generation**:\" or the template's post_generation_instructions.\n */\nexport function extractPostGenInstructions(previewMarkdown: string): string | null {\n const genNotesIdx = previewMarkdown.indexOf('## Generation Notes');\n if (genNotesIdx === -1) return null;\n\n const genNotes = previewMarkdown.slice(genNotesIdx);\n\n // Look for **Post-generation**: line\n const postGenMatch = genNotes.match(/\\*\\*Post-generation\\*\\*:\\s*(.+)/);\n if (postGenMatch) {\n return postGenMatch[1]!.trim();\n }\n\n return null;\n}\n","/**\n * domain/templates/docx-generator.ts — Markdown → DOCX conversion.\n *\n * Pipeline: markdown-it (md→HTML) → html-to-docx (HTML→DOCX Buffer).\n */\n\nimport MarkdownIt from 'markdown-it';\n// @ts-expect-error — html-to-docx has no type declarations\nimport HTMLtoDOCX from 'html-to-docx';\n\nconst md = new MarkdownIt({ html: true, breaks: true });\n\n/**\n * Convert rendered markdown to a DOCX buffer.\n * Returns a Buffer containing the DOCX file (zip archive).\n */\nexport async function markdownToDocx(\n markdown: string,\n title?: string,\n): Promise<Buffer> {\n const html = md.render(markdown);\n\n // Wrap in minimal HTML document structure\n const fullHtml = `<!DOCTYPE html>\n<html>\n<head><meta charset=\"UTF-8\"><title>${escapeHtml(title ?? 'Document')}</title></head>\n<body>${html}</body>\n</html>`;\n\n const buffer = await HTMLtoDOCX(fullHtml, null, {\n title: title ?? 'Document',\n table: { row: { cantSplit: true } },\n });\n\n return Buffer.from(buffer);\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n","/**\n * domain/templates/pdf-converter.ts — DOCX → PDF via CloudConvert REST API.\n *\n * Graceful degradation: returns null on any error (timeout, network, missing key).\n * Caller falls back to DOCX-only output.\n */\n\nimport { logger } from '../../utils/logger.js';\n\nconst CLOUDCONVERT_API_URL = 'https://api.cloudconvert.com/v2';\nconst TIMEOUT_MS = 30_000;\nconst POLL_INTERVAL_MS = 1_000;\n\ninterface CloudConvertJob {\n data: {\n id: string;\n status: string;\n tasks: Array<{\n name: string;\n operation: string;\n status: string;\n result?: {\n files?: Array<{ url: string }>;\n form?: { url: string; parameters: Record<string, string> };\n };\n }>;\n };\n}\n\n/**\n * Convert a DOCX buffer to PDF via CloudConvert.\n * Returns the PDF buffer, or null on any error.\n */\nexport async function convertDocxToPdf(\n docxBuffer: Buffer,\n apiKey: string,\n): Promise<Buffer | null> {\n if (!apiKey) return null;\n\n try {\n // 1. Create job: import/upload → convert (docx→pdf) → export/url\n const jobRes = await fetchWithTimeout(`${CLOUDCONVERT_API_URL}/jobs`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tasks: {\n 'import-file': { operation: 'import/upload' },\n 'convert-to-pdf': {\n operation: 'convert',\n input: ['import-file'],\n output_format: 'pdf',\n input_format: 'docx',\n },\n 'export-result': {\n operation: 'export/url',\n input: ['convert-to-pdf'],\n },\n },\n }),\n });\n\n if (!jobRes.ok) {\n logger.warn('CloudConvert job creation failed', { status: jobRes.status });\n return null;\n }\n\n const job = (await jobRes.json()) as CloudConvertJob;\n const jobId = job.data.id;\n\n // 2. Upload DOCX to the upload URL\n const uploadTask = job.data.tasks.find((t) => t.name === 'import-file');\n const uploadUrl = uploadTask?.result?.form?.url;\n const uploadParams = uploadTask?.result?.form?.parameters;\n\n if (!uploadUrl || !uploadParams) {\n logger.warn('CloudConvert: no upload URL in job response');\n return null;\n }\n\n const formData = new FormData();\n for (const [key, value] of Object.entries(uploadParams)) {\n formData.append(key, value);\n }\n formData.append('file', new Blob([new Uint8Array(docxBuffer)]), 'document.docx');\n\n const uploadRes = await fetchWithTimeout(uploadUrl, {\n method: 'POST',\n body: formData,\n });\n\n if (!uploadRes.ok) {\n logger.warn('CloudConvert upload failed', { status: uploadRes.status });\n return null;\n }\n\n // 3. Poll for completion\n const startTime = Date.now();\n while (Date.now() - startTime < TIMEOUT_MS) {\n await sleep(POLL_INTERVAL_MS);\n\n const statusRes = await fetchWithTimeout(\n `${CLOUDCONVERT_API_URL}/jobs/${jobId}`,\n {\n headers: { Authorization: `Bearer ${apiKey}` },\n },\n );\n\n if (!statusRes.ok) {\n logger.warn('CloudConvert status poll failed', { status: statusRes.status });\n return null;\n }\n\n const statusJob = (await statusRes.json()) as CloudConvertJob;\n\n if (statusJob.data.status === 'finished') {\n // 4. Download PDF from export URL\n const exportTask = statusJob.data.tasks.find(\n (t) => t.name === 'export-result',\n );\n const pdfUrl = exportTask?.result?.files?.[0]?.url;\n\n if (!pdfUrl) {\n logger.warn('CloudConvert: no PDF URL in completed job');\n return null;\n }\n\n const pdfRes = await fetchWithTimeout(pdfUrl);\n if (!pdfRes.ok) {\n logger.warn('CloudConvert PDF download failed', { status: pdfRes.status });\n return null;\n }\n\n return Buffer.from(await pdfRes.arrayBuffer());\n }\n\n if (statusJob.data.status === 'error') {\n logger.warn('CloudConvert job failed');\n return null;\n }\n }\n\n // Timeout\n logger.warn('CloudConvert conversion timed out', { jobId });\n return null;\n } catch (error) {\n logger.warn('CloudConvert error', {\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function fetchWithTimeout(\n url: string,\n init?: RequestInit,\n): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);\n\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timeoutId);\n }\n}\n","/**\n * domain/templates/category-mapping.ts — Map template categories to DocumentClass.\n *\n * Used during document generation to auto-classify saved documents.\n */\n\nimport type { DocumentClass } from '../classification/types.js';\n\nconst CATEGORY_TO_CLASS: Record<string, DocumentClass> = {\n governance: 'board_consent',\n meeting_minutes: 'meeting_minutes',\n equity: 'stock_purchase_agreement',\n nda: 'nda',\n contract: 'unclassified',\n employment: 'employment_agreement',\n privacy: 'unclassified',\n tax: 'tax_filing',\n compliance: 'state_filing',\n};\n\n/**\n * Map a template category to a DocumentClass for vault auto-classification.\n * Returns 'unclassified' for unknown categories.\n */\nexport function categoryToDocumentClass(category: string): DocumentClass {\n return CATEGORY_TO_CLASS[category] ?? 'unclassified';\n}\n","/**\n * db/interview-sessions.ts — Interview session CRUD operations.\n *\n * Persists guided interview state so abandoned interviews can be resumed.\n * Sessions are scoped to (user_id, company_id, template_id).\n */\n\nimport type { TypedSupabaseClient } from './client.js';\nimport type { Json } from '../../../../supabase/types/database.js';\n\n// --- Types ---\n\nexport interface InterviewSessionRecord {\n id: string;\n userId: string;\n companyId: string;\n templateId: string;\n answers: Record<string, unknown>;\n preFilled: Record<string, unknown>;\n currentStep: number;\n status: 'in_progress' | 'completed' | 'abandoned';\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface InterviewSessionCreateInput {\n userId: string;\n companyId: string;\n templateId: string;\n preFilled: Record<string, unknown>;\n}\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): InterviewSessionRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n companyId: row.company_id as string,\n templateId: row.template_id as string,\n answers: (row.answers as Record<string, unknown>) ?? {},\n preFilled: (row.pre_filled as Record<string, unknown>) ?? {},\n currentStep: row.current_step as number,\n status: row.status as InterviewSessionRecord['status'],\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\n/**\n * Find an active (in_progress) session for the same user/company/template.\n * Returns null if none exists — caller creates a new one.\n */\nexport async function findActiveSession(\n supabase: TypedSupabaseClient,\n userId: string,\n companyId: string,\n templateId: string,\n): Promise<InterviewSessionRecord | null> {\n const { data, error } = await supabase\n .from('interview_sessions')\n .select('*')\n .eq('user_id', userId)\n .eq('company_id', companyId)\n .eq('template_id', templateId)\n .eq('status', 'in_progress')\n .order('created_at', { ascending: false })\n .limit(1)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Create a new interview session.\n */\nexport async function createSession(\n supabase: TypedSupabaseClient,\n input: InterviewSessionCreateInput,\n): Promise<InterviewSessionRecord> {\n const { data, error } = await supabase\n .from('interview_sessions')\n .insert({\n user_id: input.userId,\n company_id: input.companyId,\n template_id: input.templateId,\n pre_filled: input.preFilled as Json,\n })\n .select()\n .single();\n\n if (error) throw error;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Get a session by ID.\n */\nexport async function getSessionById(\n supabase: TypedSupabaseClient,\n sessionId: string,\n): Promise<InterviewSessionRecord | null> {\n const { data, error } = await supabase\n .from('interview_sessions')\n .select('*')\n .eq('id', sessionId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Update session answers and current step.\n */\nexport async function updateSessionAnswers(\n supabase: TypedSupabaseClient,\n sessionId: string,\n answers: Record<string, unknown>,\n currentStep: number,\n): Promise<void> {\n const { error } = await supabase\n .from('interview_sessions')\n .update({\n answers: answers as Json,\n current_step: currentStep,\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId);\n\n if (error) throw error;\n}\n\n/**\n * Mark a session as completed.\n */\nexport async function completeSession(\n supabase: TypedSupabaseClient,\n sessionId: string,\n): Promise<void> {\n const { error } = await supabase\n .from('interview_sessions')\n .update({\n status: 'completed',\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId);\n\n if (error) throw error;\n}\n","/**\n * db/share-classes.ts — Share class CRUD operations + camelCase mapper.\n *\n * Every function receives supabase client as parameter (from SessionContext).\n * Returns null for single-row lookups when not found (uses .maybeSingle()).\n * Only throws for unexpected Supabase errors.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface ShareClassRecord {\n id: string;\n userId: string;\n companyId: string;\n name: string;\n classType: string;\n seniority: number;\n authorizedShares: number;\n parValue: number | null;\n pricePerShare: number | null;\n votesPerShare: number;\n series: string | null;\n liquidationPreference: number | null;\n participationCap: number | null;\n conversionRights: string | null;\n antiDilutionType: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ShareClassCreateInput {\n userId: string;\n companyId: string;\n name: string;\n classType: string;\n authorizedShares: number;\n seniority?: number;\n parValue?: number | null;\n pricePerShare?: number;\n votesPerShare?: number;\n series?: string;\n liquidationPreference?: number | null;\n participationCap?: number | null;\n conversionRights?: string | null;\n antiDilutionType?: string;\n}\n\nexport interface ShareClassListResult {\n items: ShareClassRecord[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): ShareClassRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n companyId: row.company_id as string,\n name: row.name as string,\n classType: row.class_type as string,\n seniority: row.seniority as number,\n authorizedShares: Number(row.authorized_shares),\n parValue: row.par_value != null ? Number(row.par_value) : null,\n pricePerShare: row.price_per_share != null ? Number(row.price_per_share) : null,\n votesPerShare: row.votes_per_share as number,\n series: (row.series as string) ?? null,\n liquidationPreference: row.liquidation_preference != null ? Number(row.liquidation_preference) : null,\n participationCap: row.participation_cap != null ? Number(row.participation_cap) : null,\n conversionRights: (row.conversion_rights as string) ?? null,\n antiDilutionType: (row.anti_dilution_type as string) ?? 'none',\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\nexport async function createShareClass(\n supabase: TypedSupabaseClient,\n input: ShareClassCreateInput,\n): Promise<ShareClassRecord> {\n const { data, error } = await supabase\n .from('share_classes')\n .insert({\n user_id: input.userId,\n company_id: input.companyId,\n name: input.name,\n class_type: input.classType,\n authorized_shares: input.authorizedShares,\n seniority: input.seniority ?? 0,\n par_value: input.parValue ?? null,\n price_per_share: input.pricePerShare ?? null,\n votes_per_share: input.votesPerShare ?? 1,\n series: input.series ?? null,\n liquidation_preference: input.liquidationPreference ?? null,\n participation_cap: input.participationCap ?? null,\n conversion_rights: input.conversionRights ?? null,\n anti_dilution_type: input.antiDilutionType ?? 'none',\n })\n .select()\n .single();\n\n if (error) {\n if (error.code === '23505') {\n throw Object.assign(\n new Error(`A share class named \"${input.name}\" already exists for this company.`),\n { code: 'CONFLICT' },\n );\n }\n throw error;\n }\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function getShareClassById(\n supabase: TypedSupabaseClient,\n shareClassId: string,\n): Promise<ShareClassRecord | null> {\n const { data, error } = await supabase\n .from('share_classes')\n .select('*')\n .eq('id', shareClassId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function listAllShareClasses(\n supabase: TypedSupabaseClient,\n companyId: string,\n): Promise<ShareClassRecord[]> {\n const { data, error } = await supabase\n .from('share_classes')\n .select('*')\n .eq('company_id', companyId)\n .order('seniority', { ascending: true });\n\n if (error) throw error;\n\n return (data ?? []).map((row) => mapRow(row as unknown as Record<string, unknown>));\n}\n\nexport async function listShareClasses(\n supabase: TypedSupabaseClient,\n companyId: string,\n limit = 25,\n offset = 0,\n): Promise<ShareClassListResult> {\n const { data, error, count } = await supabase\n .from('share_classes')\n .select('*', { count: 'exact' })\n .eq('company_id', companyId)\n .order('seniority', { ascending: true })\n .range(offset, offset + limit - 1);\n\n if (error) throw error;\n\n const items = (data ?? []).map((row) => mapRow(row as unknown as Record<string, unknown>));\n const total = count ?? items.length;\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n}\n","/**\n * db/equity-plans.ts — Equity plan CRUD operations + camelCase mapper.\n *\n * Every function receives supabase client as parameter (from SessionContext).\n * Returns null for single-row lookups when not found (uses .maybeSingle()).\n * Only throws for unexpected Supabase errors.\n *\n * Pool utilization (granted/available/exercised/cancelled) is computed at\n * query time from equity_grants — not stored on the plan row itself.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface EquityPlanRecord {\n id: string;\n userId: string;\n companyId: string;\n shareClassId: string;\n name: string;\n initialSharesReserved: number;\n boardApprovalDate: string | null;\n cancellationBehavior: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface EquityPlanCreateInput {\n userId: string;\n companyId: string;\n shareClassId: string;\n name: string;\n initialSharesReserved: number;\n boardApprovalDate?: string;\n cancellationBehavior?: string;\n}\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): EquityPlanRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n companyId: row.company_id as string,\n shareClassId: row.share_class_id as string,\n name: row.name as string,\n initialSharesReserved: Number(row.initial_shares_reserved),\n boardApprovalDate: (row.board_approval_date as string) ?? null,\n cancellationBehavior: (row.cancellation_behavior as string) ?? 'return_to_pool',\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\nexport async function createEquityPlan(\n supabase: TypedSupabaseClient,\n input: EquityPlanCreateInput,\n): Promise<EquityPlanRecord> {\n const { data, error } = await supabase\n .from('equity_plans')\n .insert({\n user_id: input.userId,\n company_id: input.companyId,\n share_class_id: input.shareClassId,\n name: input.name,\n initial_shares_reserved: input.initialSharesReserved,\n board_approval_date: input.boardApprovalDate ?? null,\n cancellation_behavior: input.cancellationBehavior ?? 'return_to_pool',\n })\n .select()\n .single();\n\n if (error) {\n if (error.code === '23505') {\n throw Object.assign(\n new Error(`An equity plan named \"${input.name}\" already exists for this company.`),\n { code: 'CONFLICT' },\n );\n }\n throw error;\n }\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function getEquityPlanById(\n supabase: TypedSupabaseClient,\n planId: string,\n): Promise<EquityPlanRecord | null> {\n const { data, error } = await supabase\n .from('equity_plans')\n .select('*')\n .eq('id', planId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n","/**\n * db/stakeholders.ts — Stakeholder CRUD operations + camelCase mapper.\n *\n * Every function receives supabase client as parameter (from SessionContext).\n * Returns null for single-row lookups when not found (uses .maybeSingle()).\n * Only throws for unexpected Supabase errors.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface StakeholderRecord {\n id: string;\n userId: string;\n companyId: string;\n name: string;\n email: string | null;\n type: string;\n stakeholderType: string;\n institutionName: string | null;\n relationshipStartDate: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface StakeholderCreateInput {\n userId: string;\n companyId: string;\n name: string;\n email?: string;\n type: string;\n stakeholderType?: string;\n institutionName?: string;\n relationshipStartDate?: string;\n}\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): StakeholderRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n companyId: row.company_id as string,\n name: row.name as string,\n email: (row.email as string) ?? null,\n type: row.type as string,\n stakeholderType: (row.stakeholder_type as string) ?? 'individual',\n institutionName: (row.institution_name as string) ?? null,\n relationshipStartDate: (row.relationship_start_date as string) ?? null,\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\nexport async function createStakeholder(\n supabase: TypedSupabaseClient,\n input: StakeholderCreateInput,\n): Promise<StakeholderRecord> {\n const { data, error } = await supabase\n .from('stakeholders')\n .insert({\n user_id: input.userId,\n company_id: input.companyId,\n name: input.name,\n email: input.email ?? null,\n type: input.type,\n stakeholder_type: input.stakeholderType ?? 'individual',\n institution_name: input.institutionName ?? null,\n relationship_start_date: input.relationshipStartDate ?? null,\n })\n .select()\n .single();\n\n if (error) {\n if (error.code === '23505') {\n throw Object.assign(\n new Error(`A stakeholder named \"${input.name}\" (${input.type}) already exists for this company.`),\n { code: 'CONFLICT' },\n );\n }\n throw error;\n }\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function listStakeholdersByCompany(\n supabase: TypedSupabaseClient,\n companyId: string,\n): Promise<StakeholderRecord[]> {\n const { data, error } = await supabase\n .from('stakeholders')\n .select('*')\n .eq('company_id', companyId);\n\n if (error) throw error;\n\n return (data ?? []).map((row) => mapRow(row as unknown as Record<string, unknown>));\n}\n\nexport async function getStakeholderById(\n supabase: TypedSupabaseClient,\n stakeholderId: string,\n): Promise<StakeholderRecord | null> {\n const { data, error } = await supabase\n .from('stakeholders')\n .select('*')\n .eq('id', stakeholderId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n","/**\n * db/equity-grants.ts — Equity grant CRUD + lifecycle operations.\n *\n * Every function receives supabase client as parameter (from SessionContext).\n * Returns null for single-row lookups when not found (uses .maybeSingle()).\n * Only throws for unexpected Supabase errors.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface EquityGrantRecord {\n id: string;\n userId: string;\n companyId: string;\n stakeholderId: string;\n instrumentType: string;\n shareClassId: string | null;\n equityPlanId: string | null;\n shares: number | null;\n amount: number | null;\n pricePerShare: number | null;\n valuationCap: number | null;\n discountRate: number | null;\n interestRate: number | null;\n maturityDate: string | null;\n grantDate: string;\n boardApprovalDate: string | null;\n election83bFiled: boolean;\n election83bDate: string | null;\n status: string;\n notes: string | null;\n convertedFromId: string | null;\n cancelReason: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface VestingScheduleRecord {\n id: string;\n userId: string;\n grantId: string;\n cliffDate: string | null;\n vestingStart: string;\n periodMonths: number;\n totalShares: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface EquityGrantCreateInput {\n userId: string;\n companyId: string;\n stakeholderId: string;\n instrumentType: string;\n shareClassId?: string;\n equityPlanId?: string;\n shares?: number;\n amount?: number;\n pricePerShare?: number;\n valuationCap?: number;\n discountRate?: number;\n interestRate?: number;\n grantDate: string;\n boardApprovalDate?: string;\n status?: string;\n convertedFromId?: string;\n vestingMonths?: number;\n cliffMonths?: number;\n vestingStartDate?: string;\n}\n\nexport interface EquityGrantWithVesting {\n grant: EquityGrantRecord;\n vesting: VestingScheduleRecord | null;\n}\n\n// --- Mappers ---\n\nfunction mapGrantRow(row: Record<string, unknown>): EquityGrantRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n companyId: row.company_id as string,\n stakeholderId: row.stakeholder_id as string,\n instrumentType: row.instrument_type as string,\n shareClassId: (row.share_class_id as string) ?? null,\n equityPlanId: (row.equity_plan_id as string) ?? null,\n shares: row.shares != null ? Number(row.shares) : null,\n amount: row.amount != null ? Number(row.amount) : null,\n pricePerShare: row.price_per_share != null ? Number(row.price_per_share) : null,\n valuationCap: row.valuation_cap != null ? Number(row.valuation_cap) : null,\n discountRate: row.discount_rate != null ? Number(row.discount_rate) : null,\n interestRate: row.interest_rate != null ? Number(row.interest_rate) : null,\n maturityDate: (row.maturity_date as string) ?? null,\n grantDate: row.grant_date as string,\n boardApprovalDate: (row.board_approval_date as string) ?? null,\n election83bFiled: row.election_83b_filed as boolean,\n election83bDate: (row.election_83b_date as string) ?? null,\n status: row.status as string,\n notes: (row.notes as string) ?? null,\n convertedFromId: (row.converted_from_id as string) ?? null,\n cancelReason: (row.cancel_reason as string) ?? null,\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\nfunction mapVestingRow(row: Record<string, unknown>): VestingScheduleRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n grantId: row.grant_id as string,\n cliffDate: (row.cliff_date as string) ?? null,\n vestingStart: row.vesting_start as string,\n periodMonths: row.period_months as number,\n totalShares: Number(row.total_shares),\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\nexport async function createEquityGrant(\n supabase: TypedSupabaseClient,\n input: EquityGrantCreateInput,\n): Promise<EquityGrantWithVesting> {\n // Insert the grant\n const { data: grantData, error: grantError } = await supabase\n .from('equity_grants')\n .insert({\n user_id: input.userId,\n company_id: input.companyId,\n stakeholder_id: input.stakeholderId,\n instrument_type: input.instrumentType,\n share_class_id: input.shareClassId ?? null,\n equity_plan_id: input.equityPlanId ?? null,\n shares: input.shares ?? null,\n amount: input.amount ?? null,\n price_per_share: input.pricePerShare ?? null,\n valuation_cap: input.valuationCap ?? null,\n discount_rate: input.discountRate ?? null,\n interest_rate: input.interestRate ?? null,\n grant_date: input.grantDate,\n board_approval_date: input.boardApprovalDate ?? null,\n status: input.status ?? 'active',\n converted_from_id: input.convertedFromId ?? null,\n })\n .select()\n .single();\n\n if (grantError) throw grantError;\n\n const grant = mapGrantRow(grantData as unknown as Record<string, unknown>);\n\n // Insert vesting schedule if provided\n let vesting: VestingScheduleRecord | null = null;\n if (input.vestingMonths && input.shares) {\n const vestingStart = input.vestingStartDate ?? input.grantDate;\n const cliffDate = input.cliffMonths\n ? addMonths(vestingStart, input.cliffMonths)\n : null;\n\n const { data: vestingData, error: vestingError } = await supabase\n .from('vesting_schedules')\n .insert({\n user_id: input.userId,\n grant_id: grant.id,\n cliff_date: cliffDate,\n vesting_start: vestingStart,\n period_months: input.vestingMonths,\n total_shares: input.shares,\n })\n .select()\n .single();\n\n if (vestingError) throw vestingError;\n vesting = mapVestingRow(vestingData as unknown as Record<string, unknown>);\n }\n\n return { grant, vesting };\n}\n\nexport async function getEquityGrantById(\n supabase: TypedSupabaseClient,\n grantId: string,\n): Promise<EquityGrantRecord | null> {\n const { data, error } = await supabase\n .from('equity_grants')\n .select('*')\n .eq('id', grantId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapGrantRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function updateGrantStatus(\n supabase: TypedSupabaseClient,\n grantId: string,\n status: string,\n cancelReason?: string,\n): Promise<EquityGrantRecord> {\n const update: Record<string, unknown> = { status };\n if (cancelReason !== undefined) {\n update.cancel_reason = cancelReason;\n }\n\n const { data, error } = await supabase\n .from('equity_grants')\n .update(update)\n .eq('id', grantId)\n .select()\n .single();\n\n if (error) throw error;\n return mapGrantRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function updateGrantMetadata(\n supabase: TypedSupabaseClient,\n grantId: string,\n fields: {\n election83bFiled?: boolean;\n election83bDate?: string;\n notes?: string;\n boardApprovalDate?: string;\n },\n): Promise<EquityGrantRecord> {\n const update: Record<string, unknown> = {};\n if (fields.election83bFiled !== undefined) update.election_83b_filed = fields.election83bFiled;\n if (fields.election83bDate !== undefined) update.election_83b_date = fields.election83bDate;\n if (fields.notes !== undefined) update.notes = fields.notes;\n if (fields.boardApprovalDate !== undefined) update.board_approval_date = fields.boardApprovalDate;\n\n const { data, error } = await supabase\n .from('equity_grants')\n .update(update)\n .eq('id', grantId)\n .select()\n .single();\n\n if (error) throw error;\n return mapGrantRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function reduceGrantShares(\n supabase: TypedSupabaseClient,\n grantId: string,\n sharesToReduce: number,\n): Promise<EquityGrantRecord> {\n // First get current shares to compute new value\n const current = await getEquityGrantById(supabase, grantId);\n if (!current || current.shares == null) {\n throw new Error(`Grant ${grantId} not found or has no shares.`);\n }\n\n const newShares = current.shares - sharesToReduce;\n const { data, error } = await supabase\n .from('equity_grants')\n .update({ shares: newShares })\n .eq('id', grantId)\n .select()\n .single();\n\n if (error) throw error;\n return mapGrantRow(data as unknown as Record<string, unknown>);\n}\n\nexport async function getActiveShareCount(\n supabase: TypedSupabaseClient,\n companyId: string,\n): Promise<number> {\n const { data, error } = await supabase\n .from('equity_grants')\n .select('shares')\n .eq('company_id', companyId)\n .eq('status', 'active')\n .not('shares', 'is', null);\n\n if (error) throw error;\n\n return (data ?? []).reduce((sum, row) => sum + Number(row.shares ?? 0), 0);\n}\n\nexport async function listGrantsByCompany(\n supabase: TypedSupabaseClient,\n companyId: string,\n filters?: { status?: string; instrumentTypes?: string[] },\n): Promise<EquityGrantRecord[]> {\n let query = supabase\n .from('equity_grants')\n .select('*')\n .eq('company_id', companyId);\n\n if (filters?.status) {\n query = query.eq('status', filters.status);\n }\n if (filters?.instrumentTypes && filters.instrumentTypes.length > 0) {\n query = query.in('instrument_type', filters.instrumentTypes);\n }\n\n const { data, error } = await query;\n if (error) throw error;\n\n return (data ?? []).map((row) => mapGrantRow(row as unknown as Record<string, unknown>));\n}\n\nexport async function getVestingByGrantId(\n supabase: TypedSupabaseClient,\n grantId: string,\n): Promise<VestingScheduleRecord | null> {\n const { data, error } = await supabase\n .from('vesting_schedules')\n .select('*')\n .eq('grant_id', grantId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapVestingRow(data as unknown as Record<string, unknown>);\n}\n\n// --- Helpers ---\n\nfunction addMonths(dateStr: string, months: number): string {\n const d = new Date(dateStr);\n d.setMonth(d.getMonth() + months);\n return d.toISOString().split('T')[0]!;\n}\n","/**\n * domain/conversion.ts — SAFE/note conversion math and 83(b) deadline.\n *\n * Self-contained conversion logic per data/cap-table/conversion-formulas.json.\n * Used by convert_grant action in ff-equity tool handler.\n * Story 4-5 (round modeling) reuses this at scale.\n */\n\nexport interface ConversionInput {\n instrumentType: 'safe' | 'convertible_note';\n amount: number;\n valuationCap?: number;\n discountRate?: number;\n interestRate?: number;\n grantDate: string;\n pricePerShare: number;\n preMoneyShares: number;\n}\n\nexport interface ConversionResult {\n conversionPrice: number;\n shares: number;\n formula: string;\n breakdown: {\n capPrice?: number;\n discountPrice?: number;\n accruedInterest?: number;\n totalConversionAmount: number;\n };\n}\n\n/**\n * Compute SAFE or convertible note conversion to equity shares.\n *\n * Logic:\n * 1. Cap price (if valuationCap): valuationCap / preMoneyShares\n * 2. Discount price (if discountRate): pricePerShare * (1 - discountRate)\n * 3. Conversion price: min of available prices (cap, discount, round price)\n * 4. For convertible notes: accrue simple interest, add to principal\n * 5. Shares: totalConversionAmount / conversionPrice\n */\nexport function computeConversion(input: ConversionInput): ConversionResult {\n const {\n instrumentType,\n amount,\n valuationCap,\n discountRate,\n interestRate,\n grantDate,\n pricePerShare,\n preMoneyShares,\n } = input;\n\n // Step 1: Cap price\n const capPrice = valuationCap && preMoneyShares > 0\n ? valuationCap / preMoneyShares\n : undefined;\n\n // Step 2: Discount price\n const discountPrice = discountRate != null && discountRate > 0\n ? pricePerShare * (1 - discountRate)\n : undefined;\n\n // Step 3: Determine conversion price (lowest available)\n const candidates: number[] = [pricePerShare];\n if (capPrice != null) candidates.push(capPrice);\n if (discountPrice != null) candidates.push(discountPrice);\n const conversionPrice = Math.min(...candidates);\n\n // Determine which formula was used\n let formula: string;\n if (capPrice != null && discountPrice != null) {\n formula = 'cap_and_discount';\n } else if (capPrice != null) {\n formula = 'cap';\n } else if (discountPrice != null) {\n formula = 'discount';\n } else {\n formula = 'round_price';\n }\n\n // Step 4: For convertible notes, accrue simple interest\n let accruedInterest: number | undefined;\n let totalConversionAmount = amount;\n\n if (instrumentType === 'convertible_note' && interestRate && interestRate > 0) {\n const grantMs = new Date(grantDate).getTime();\n const nowMs = Date.now();\n const days = Math.max(0, (nowMs - grantMs) / (1000 * 60 * 60 * 24));\n accruedInterest = amount * interestRate * (days / 365);\n totalConversionAmount = amount + accruedInterest;\n }\n\n // Step 5: Shares\n const shares = totalConversionAmount / conversionPrice;\n\n return {\n conversionPrice,\n shares: Math.floor(shares),\n formula,\n breakdown: {\n ...(capPrice != null && { capPrice }),\n ...(discountPrice != null && { discountPrice }),\n ...(accruedInterest != null && { accruedInterest }),\n totalConversionAmount,\n },\n };\n}\n\n/**\n * Compute 83(b) election deadline: grant_date + 30 days.\n * Returns ISO date string (YYYY-MM-DD).\n */\nexport function compute83bDeadline(grantDate: string): string {\n const d = new Date(grantDate);\n d.setDate(d.getDate() + 30);\n return d.toISOString().split('T')[0]!;\n}\n","/**\n * domain/ownership.ts — Fully diluted ownership computation.\n *\n * Pure computation, no Supabase deps — independently testable.\n * Used by the `ownership` action in ff-equity tool handler.\n *\n * Sums to exactly 10000 basis points (100%) via largest remainder method (NFR48).\n */\n\nimport type { EquityGrantRecord } from '../db/equity-grants.js';\n\nexport interface OwnershipInput {\n grants: EquityGrantRecord[];\n includeOptions: boolean;\n includeSafes: boolean;\n includeWarrants: boolean;\n}\n\nexport interface StakeholderOwnership {\n stakeholderId: string;\n shares: number;\n basisPoints: number;\n percentage: string;\n description: string;\n grants: Array<{\n grantId: string;\n instrumentType: string;\n shares: number;\n basisPoints: number;\n }>;\n}\n\nexport interface OwnershipResult {\n stakeholders: StakeholderOwnership[];\n totalShares: number;\n totalBasisPoints: 10000;\n summary: string;\n}\n\n/** Instrument types always included in ownership. */\nconst ALWAYS_INCLUDE = new Set([\n 'common_stock',\n 'preferred_stock',\n 'membership_interest',\n 'profits_interest',\n 'rsu',\n]);\n\n/** Instrument types included when includeOptions is true. */\nconst OPTION_TYPES = new Set(['option_iso', 'option_nso']);\n\n/** Instrument types included when includeSafes is true. */\nconst SAFE_TYPES = new Set(['safe', 'convertible_note']);\n\n/** Instrument types included when includeWarrants is true. */\nconst WARRANT_TYPES = new Set(['warrant']);\n\n/**\n * Distribute remaining basis points using the largest remainder method.\n * Ensures sum equals exactly `target`.\n */\nfunction distributeByLargestRemainder(\n rawValues: number[],\n target: number,\n): number[] {\n const floors = rawValues.map(Math.floor);\n const remainders = rawValues.map((v, i) => v - floors[i]!);\n let remaining = target - floors.reduce((a, b) => a + b, 0);\n\n // Build index array sorted by remainder descending\n const indices = remainders\n .map((_, i) => i)\n .sort((a, b) => remainders[b]! - remainders[a]!);\n\n for (const idx of indices) {\n if (remaining <= 0) break;\n floors[idx]!++;\n remaining--;\n }\n\n return floors;\n}\n\nexport function computeOwnership(input: OwnershipInput): OwnershipResult {\n const { grants, includeOptions, includeSafes, includeWarrants } = input;\n\n // Step 1: Filter grants by inclusion flags\n const included = grants.filter((g) => {\n if (ALWAYS_INCLUDE.has(g.instrumentType)) return true;\n if (includeOptions && OPTION_TYPES.has(g.instrumentType)) return true;\n if (includeSafes && SAFE_TYPES.has(g.instrumentType)) return true;\n if (includeWarrants && WARRANT_TYPES.has(g.instrumentType)) return true;\n return false;\n });\n\n // Step 2: Only count grants with non-null shares\n const withShares = included.filter((g) => g.shares != null && g.shares > 0);\n\n if (withShares.length === 0) {\n return {\n stakeholders: [],\n totalShares: 0,\n totalBasisPoints: 10000,\n summary: 'No share-based grants found for ownership calculation.',\n };\n }\n\n // Step 3: Sum total shares\n const totalShares = withShares.reduce((sum, g) => sum + g.shares!, 0);\n\n // Step 4: Group by stakeholder\n const byStakeholder = new Map<string, EquityGrantRecord[]>();\n for (const g of withShares) {\n const existing = byStakeholder.get(g.stakeholderId);\n if (existing) {\n existing.push(g);\n } else {\n byStakeholder.set(g.stakeholderId, [g]);\n }\n }\n\n // Step 5: Compute shares per stakeholder\n const stakeholderEntries: Array<{ id: string; shares: number; grants: EquityGrantRecord[] }> = [];\n for (const [id, sGrants] of byStakeholder) {\n const shares = sGrants.reduce((sum, g) => sum + g.shares!, 0);\n stakeholderEntries.push({ id, shares, grants: sGrants });\n }\n\n // Sort by shares descending for consistent output\n stakeholderEntries.sort((a, b) => b.shares - a.shares);\n\n // Step 6: Compute basis points with largest remainder method\n const rawBasisPoints = stakeholderEntries.map(\n (e) => (e.shares / totalShares) * 10000,\n );\n const distributedBP = distributeByLargestRemainder(rawBasisPoints, 10000);\n\n // Step 7: Build result\n const stakeholders: StakeholderOwnership[] = stakeholderEntries.map((entry, i) => {\n const bp = distributedBP[i]!;\n const pct = (bp / 100).toFixed(2) + '%';\n\n // Per-grant breakdown\n const grantDetails = entry.grants.map((g) => ({\n grantId: g.id,\n instrumentType: g.instrumentType,\n shares: g.shares!,\n basisPoints: Math.round((g.shares! / totalShares) * 10000),\n }));\n\n return {\n stakeholderId: entry.id,\n shares: entry.shares,\n basisPoints: bp,\n percentage: pct,\n description: `Owns ${entry.shares.toLocaleString()} shares (${pct}) across ${entry.grants.length} grant(s).`,\n grants: grantDetails,\n };\n });\n\n const summary = `Fully diluted cap table: ${stakeholders.length} stakeholder(s), ${totalShares.toLocaleString()} total shares.`;\n\n return {\n stakeholders,\n totalShares,\n totalBasisPoints: 10000,\n summary,\n };\n}\n","/**\n * domain/vesting.ts — Vesting schedule snapshot computation.\n *\n * Pure computation, no Supabase deps — independently testable.\n * Used by the `vesting` action in ff-equity tool handler.\n */\n\nimport type { VestingScheduleRecord } from '../db/equity-grants.js';\n\nexport interface VestingInput {\n vestingSchedule: VestingScheduleRecord;\n grantShares: number;\n asOfDate: string;\n}\n\nexport interface VestingResult {\n totalShares: number;\n vestedShares: number;\n unvestedShares: number;\n vestedPercentage: string;\n vestedBasisPoints: number;\n cliffDate: string | null;\n cliffReached: boolean;\n vestingStartDate: string;\n vestingEndDate: string;\n nextVestingDate: string | null;\n monthsElapsed: number;\n monthsTotal: number;\n description: string;\n}\n\n/**\n * Count full months between two ISO date strings.\n * If end day < start day, subtract 1 from the month count.\n */\nfunction monthsBetween(startStr: string, endStr: string): number {\n const [sy, sm, sd] = startStr.split('-').map(Number) as [number, number, number];\n const [ey, em, ed] = endStr.split('-').map(Number) as [number, number, number];\n let months = (ey - sy) * 12 + (em - sm);\n if (ed < sd) months--;\n return Math.max(0, months);\n}\n\n/** Add months to an ISO date string, returns ISO date. */\nfunction addMonths(dateStr: string, months: number): string {\n const [y, m, d] = dateStr.split('-').map(Number) as [number, number, number];\n const date = new Date(y, m - 1 + months, d);\n const yyyy = date.getFullYear();\n const mm = String(date.getMonth() + 1).padStart(2, '0');\n const dd = String(date.getDate()).padStart(2, '0');\n return `${yyyy}-${mm}-${dd}`;\n}\n\nexport function computeVesting(input: VestingInput): VestingResult {\n const { vestingSchedule, grantShares, asOfDate } = input;\n const { cliffDate, vestingStart, periodMonths } = vestingSchedule;\n const totalShares = grantShares;\n const vestingEndDate = addMonths(vestingStart, periodMonths);\n\n // Before cliff?\n const cliffReached = cliffDate == null || asOfDate >= cliffDate;\n\n let vestedShares: number;\n let monthsElapsed: number;\n\n if (!cliffReached) {\n // Before cliff: nothing vested\n vestedShares = 0;\n monthsElapsed = monthsBetween(vestingStart, asOfDate);\n } else if (asOfDate >= vestingEndDate) {\n // Past end: fully vested\n vestedShares = totalShares;\n monthsElapsed = periodMonths;\n } else {\n // Between cliff and end: linear vesting\n monthsElapsed = monthsBetween(vestingStart, asOfDate);\n vestedShares = Math.min(\n totalShares,\n Math.floor((totalShares * monthsElapsed) / periodMonths),\n );\n }\n\n const unvestedShares = totalShares - vestedShares;\n const vestedBasisPoints = totalShares > 0\n ? Math.round((vestedShares / totalShares) * 10000)\n : 0;\n const vestedPercentage = (vestedBasisPoints / 100).toFixed(2) + '%';\n\n // Next vesting date\n let nextVestingDate: string | null = null;\n if (vestedShares < totalShares) {\n if (!cliffReached && cliffDate) {\n nextVestingDate = cliffDate;\n } else {\n const candidate = addMonths(vestingStart, monthsElapsed + 1);\n nextVestingDate = candidate <= vestingEndDate ? candidate : null;\n }\n }\n\n // Description\n let description: string;\n if (vestedShares === 0 && !cliffReached) {\n description = `No shares vested yet. Cliff date is ${cliffDate}. ${totalShares.toLocaleString()} shares will begin vesting after the cliff.`;\n } else if (vestedShares === totalShares) {\n description = `Fully vested: all ${totalShares.toLocaleString()} shares are vested as of ${asOfDate}.`;\n } else {\n description = `${vestedShares.toLocaleString()} of ${totalShares.toLocaleString()} shares vested (${vestedPercentage}). ${unvestedShares.toLocaleString()} shares remaining.${nextVestingDate ? ` Next vesting date: ${nextVestingDate}.` : ''}`;\n }\n\n return {\n totalShares,\n vestedShares,\n unvestedShares,\n vestedPercentage,\n vestedBasisPoints,\n cliffDate: cliffDate ?? null,\n cliffReached,\n vestingStartDate: vestingStart,\n vestingEndDate,\n nextVestingDate,\n monthsElapsed,\n monthsTotal: periodMonths,\n description,\n };\n}\n","/**\n * domain/waterfall.ts — Liquidation waterfall computation.\n *\n * Pure computation, no Supabase deps — independently testable.\n * Used by the `waterfall` action in ff-equity tool handler.\n *\n * Algorithm:\n * 1. Sort classes by seniority descending (highest priority first)\n * 2. Pay liquidation preferences in priority order\n * 3. Non-participating preferred conversion check (compare pref vs as-converted)\n * 4. Distribute remainder pro-rata to common + participating preferred\n * 5. Apply participation caps with clawback to common\n * 6. Build per-stakeholder payouts with return multiples\n *\n * Money values accurate to cents (NFR48).\n */\n\nexport interface WaterfallStakeholderInput {\n stakeholderId: string;\n shares: number;\n investment: number;\n}\n\nexport interface WaterfallClassInput {\n classId: string;\n className: string;\n classType: 'common' | 'preferred';\n seniority: number;\n liquidationPreference: number;\n participationCap: number | null;\n totalShares: number;\n totalInvestment: number;\n stakeholders: WaterfallStakeholderInput[];\n}\n\nexport interface WaterfallInput {\n exitAmount: number;\n classes: WaterfallClassInput[];\n}\n\nexport interface WaterfallClassResult {\n classId: string;\n className: string;\n classType: string;\n preferenceAmount: number;\n preferencePaid: number;\n participationPaid: number;\n totalPaid: number;\n status: 'fully_paid' | 'partially_paid' | 'unpaid' | 'converted';\n converted: boolean;\n}\n\nexport interface WaterfallStakeholderResult {\n stakeholderId: string;\n payout: number;\n returnMultiple: string;\n breakdown: Array<{\n classId: string;\n className: string;\n payout: number;\n }>;\n}\n\nexport interface WaterfallResult {\n exitAmount: number;\n classes: WaterfallClassResult[];\n stakeholders: WaterfallStakeholderResult[];\n remainingProceeds: number;\n auditTrail: string[];\n disclaimer: string;\n summary: string;\n}\n\nconst DISCLAIMER = 'These calculations are estimates. Verify independently with your attorney before making decisions based on these numbers.';\n\n/** Round to 2 decimal places (cents). */\nfunction toCents(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\nexport function computeWaterfall(input: WaterfallInput): WaterfallResult {\n const { exitAmount, classes } = input;\n const auditTrail: string[] = [];\n\n if (classes.length === 0) {\n return {\n exitAmount,\n classes: [],\n stakeholders: [],\n remainingProceeds: exitAmount,\n auditTrail: ['No share classes provided.'],\n disclaimer: DISCLAIMER,\n summary: 'No share classes to analyze.',\n };\n }\n\n const totalSharesAll = classes.reduce((s, c) => s + c.totalShares, 0);\n\n auditTrail.push(`Exit amount: $${exitAmount.toLocaleString()}`);\n auditTrail.push(`Share classes: ${classes.length}`);\n auditTrail.push(`Total shares: ${totalSharesAll.toLocaleString()}`);\n\n // --- Step 1: Sort preferred classes by seniority descending (highest first) ---\n const preferred = classes\n .filter((c) => c.classType === 'preferred')\n .sort((a, b) => b.seniority - a.seniority);\n\n const common = classes.filter((c) => c.classType === 'common');\n\n // Track per-class results\n const classResults = new Map<string, {\n preferenceAmount: number;\n preferencePaid: number;\n participationPaid: number;\n converted: boolean;\n status: 'fully_paid' | 'partially_paid' | 'unpaid' | 'converted';\n }>();\n\n // Init all classes\n for (const c of classes) {\n classResults.set(c.classId, {\n preferenceAmount: c.classType === 'preferred' ? toCents(c.totalInvestment * c.liquidationPreference) : 0,\n preferencePaid: 0,\n participationPaid: 0,\n converted: false,\n status: 'unpaid',\n });\n }\n\n let remaining = exitAmount;\n\n // --- Step 2: Pay liquidation preferences in seniority order ---\n auditTrail.push('--- Liquidation Preference Payments ---');\n\n for (const pClass of preferred) {\n const cr = classResults.get(pClass.classId)!;\n const prefAmount = cr.preferenceAmount;\n\n if (remaining <= 0) {\n auditTrail.push(`${pClass.className}: $0 (no remaining proceeds). Status: unpaid.`);\n continue;\n }\n\n const paid = toCents(Math.min(remaining, prefAmount));\n cr.preferencePaid = paid;\n remaining = toCents(remaining - paid);\n\n if (paid >= prefAmount) {\n cr.status = 'fully_paid';\n auditTrail.push(`${pClass.className}: $${paid.toLocaleString()} preference paid in full.`);\n } else {\n cr.status = 'partially_paid';\n auditTrail.push(`${pClass.className}: $${paid.toLocaleString()} of $${prefAmount.toLocaleString()} preference (partial).`);\n }\n }\n\n // --- Step 3: Non-participating preferred conversion check ---\n auditTrail.push('--- Conversion Check ---');\n\n for (const pClass of preferred) {\n const cr = classResults.get(pClass.classId)!;\n\n // Non-participating: participationCap === null\n if (pClass.participationCap !== null) continue;\n if (cr.status === 'unpaid') continue;\n\n // Compute as-converted payout\n const asConverted = totalSharesAll > 0\n ? toCents((pClass.totalShares / totalSharesAll) * exitAmount)\n : 0;\n\n if (asConverted > cr.preferencePaid) {\n // Return preference to pool and mark as converting\n remaining = toCents(remaining + cr.preferencePaid);\n auditTrail.push(`${pClass.className}: converting (as-converted $${asConverted.toLocaleString()} > preference $${cr.preferencePaid.toLocaleString()}). Preference returned to pool.`);\n cr.preferencePaid = 0;\n cr.converted = true;\n cr.status = 'converted';\n } else {\n auditTrail.push(`${pClass.className}: keeping preference ($${cr.preferencePaid.toLocaleString()} >= as-converted $${asConverted.toLocaleString()}).`);\n }\n }\n\n // --- Step 4: Distribute remainder pro-rata ---\n auditTrail.push('--- Pro-Rata Distribution ---');\n\n if (remaining > 0) {\n // Pool includes: common shares + converting preferred + participating preferred (cap > 0 or cap === 0)\n let poolShares = 0;\n const participatingClasses: WaterfallClassInput[] = [];\n\n for (const c of common) {\n poolShares += c.totalShares;\n participatingClasses.push(c);\n }\n\n for (const pClass of preferred) {\n const cr = classResults.get(pClass.classId)!;\n // Converting non-participating preferred participates in pro-rata\n if (cr.converted) {\n poolShares += pClass.totalShares;\n participatingClasses.push(pClass);\n }\n // Participating preferred (cap === 0 or cap > 0) gets pro-rata\n else if (pClass.participationCap !== null && cr.status !== 'unpaid') {\n poolShares += pClass.totalShares;\n participatingClasses.push(pClass);\n }\n }\n\n if (poolShares > 0) {\n const perShare = remaining / poolShares;\n\n for (const c of participatingClasses) {\n const cr = classResults.get(c.classId)!;\n const participation = toCents(perShare * c.totalShares);\n cr.participationPaid = participation;\n }\n\n auditTrail.push(`$${remaining.toLocaleString()} distributed pro-rata across ${poolShares.toLocaleString()} shares ($${toCents(perShare).toFixed(4)}/share).`);\n\n const totalDistributed = participatingClasses.reduce(\n (s, c) => s + classResults.get(c.classId)!.participationPaid,\n 0,\n );\n remaining = toCents(remaining - totalDistributed);\n }\n }\n\n // --- Step 5: Apply participation caps ---\n auditTrail.push('--- Participation Cap Check ---');\n\n let clawback = 0;\n for (const pClass of preferred) {\n const cr = classResults.get(pClass.classId)!;\n // Only applies to capped participating preferred (cap > 0)\n if (pClass.participationCap === null || pClass.participationCap === 0) continue;\n if (cr.converted || cr.status === 'unpaid') continue;\n\n const totalReturn = cr.preferencePaid + cr.participationPaid;\n const maxReturn = toCents(pClass.totalInvestment * pClass.participationCap);\n\n if (totalReturn > maxReturn) {\n const excess = toCents(totalReturn - maxReturn);\n cr.participationPaid = toCents(cr.participationPaid - excess);\n clawback += excess;\n auditTrail.push(`${pClass.className}: capped at ${pClass.participationCap}x ($${maxReturn.toLocaleString()}). Clawed back $${excess.toLocaleString()}.`);\n }\n }\n\n // Redistribute clawback to common\n if (clawback > 0) {\n const commonShares = common.reduce((s, c) => s + c.totalShares, 0);\n if (commonShares > 0) {\n const perShare = clawback / commonShares;\n for (const c of common) {\n const cr = classResults.get(c.classId)!;\n cr.participationPaid = toCents(cr.participationPaid + perShare * c.totalShares);\n }\n auditTrail.push(`$${clawback.toLocaleString()} clawback redistributed to common shareholders.`);\n }\n remaining = toCents(remaining); // no change to remaining, just redistributed\n }\n\n // Update common class status\n for (const c of common) {\n const cr = classResults.get(c.classId)!;\n if (cr.participationPaid > 0) {\n cr.status = 'fully_paid';\n }\n }\n\n // --- Step 6: Build per-stakeholder payouts ---\n const stakeholderPayouts = new Map<string, {\n payout: number;\n investment: number;\n breakdown: Array<{ classId: string; className: string; payout: number }>;\n }>();\n\n for (const c of classes) {\n const cr = classResults.get(c.classId)!;\n const classTotalPaid = toCents(cr.preferencePaid + cr.participationPaid);\n\n if (classTotalPaid === 0 && c.stakeholders.length === 0) continue;\n\n for (const s of c.stakeholders) {\n if (s.shares === 0) continue;\n\n // Stakeholder's share of this class's payout\n const fraction = c.totalShares > 0 ? s.shares / c.totalShares : 0;\n const payout = toCents(classTotalPaid * fraction);\n\n const existing = stakeholderPayouts.get(s.stakeholderId);\n if (existing) {\n existing.payout = toCents(existing.payout + payout);\n existing.investment = toCents(existing.investment + s.investment);\n existing.breakdown.push({\n classId: c.classId,\n className: c.className,\n payout,\n });\n } else {\n stakeholderPayouts.set(s.stakeholderId, {\n payout,\n investment: s.investment,\n breakdown: [{\n classId: c.classId,\n className: c.className,\n payout,\n }],\n });\n }\n }\n }\n\n // Build final stakeholder results\n const stakeholders: WaterfallStakeholderResult[] = [];\n for (const [stakeholderId, data] of stakeholderPayouts) {\n const returnMultiple = data.investment > 0\n ? `${(data.payout / data.investment).toFixed(2)}x`\n : 'N/A';\n\n stakeholders.push({\n stakeholderId,\n payout: data.payout,\n returnMultiple,\n breakdown: data.breakdown,\n });\n }\n\n // Sort by payout descending\n stakeholders.sort((a, b) => b.payout - a.payout);\n\n // Build class results array\n const classResultsArray: WaterfallClassResult[] = classes.map((c) => {\n const cr = classResults.get(c.classId)!;\n return {\n classId: c.classId,\n className: c.className,\n classType: c.classType,\n preferenceAmount: cr.preferenceAmount,\n preferencePaid: cr.preferencePaid,\n participationPaid: cr.participationPaid,\n totalPaid: toCents(cr.preferencePaid + cr.participationPaid),\n status: cr.status,\n converted: cr.converted,\n };\n });\n\n // Compute actual remaining\n const totalPaidOut = classResultsArray.reduce((s, c) => s + c.totalPaid, 0);\n const finalRemaining = toCents(exitAmount - totalPaidOut);\n\n // Summary\n const totalPreferred = preferred.length;\n const totalCommon = common.length;\n const summary = `Liquidation waterfall for $${exitAmount.toLocaleString()} exit: ${totalPreferred} preferred class(es), ${totalCommon} common class(es), ${stakeholders.length} stakeholder(s). ${finalRemaining > 0.01 ? `$${finalRemaining.toLocaleString()} unallocated.` : 'All proceeds distributed.'}`;\n\n return {\n exitAmount,\n classes: classResultsArray,\n stakeholders,\n remainingProceeds: finalRemaining,\n auditTrail,\n disclaimer: DISCLAIMER,\n summary,\n };\n}\n","/**\n * domain/modeling.ts — Round modeling & SAFE/note conversion engine.\n *\n * Pure computation, no Supabase deps — independently testable.\n * Used by the `model_round` action in ff-equity tool handler.\n *\n * Algorithm:\n * 1. Compute pre-round totals (existing shares + option pool)\n * 2. Option pool shuffle (pre-money, dilutes founders not new investors)\n * 3. Price per share = preMoneyValuation / preMoneyShares\n * 4. Convert SAFEs/notes via computeConversion()\n * 5. New investor shares = investmentAmount / pricePerShare\n * 6. Build before/after ownership comparison\n * 7. Generate audit trail narrative\n *\n * Money values accurate to cents (NFR48).\n */\n\nimport { computeConversion } from './conversion.js';\n\nexport interface RoundModelingInput {\n investmentAmount: number;\n preMoneyValuation: number;\n existingShares: Array<{\n stakeholderId: string;\n shares: number;\n classId: string | null;\n className: string | null;\n }>;\n convertibles: Array<{\n grantId: string;\n stakeholderId: string;\n instrumentType: 'safe' | 'convertible_note';\n amount: number;\n valuationCap?: number;\n discountRate?: number;\n interestRate?: number;\n grantDate: string;\n }>;\n currentOptionPoolShares: number;\n newOptionPoolPercent?: number;\n roundName?: string;\n newShareClassName?: string;\n}\n\nexport interface ConversionEntry {\n grantId: string;\n stakeholderId: string;\n instrumentType: string;\n originalAmount: number;\n conversionPrice: number;\n shares: number;\n formula: string;\n}\n\nexport interface OwnershipEntry {\n stakeholderId: string;\n beforeShares: number;\n beforePercent: string;\n afterShares: number;\n afterPercent: string;\n dilutionPercent: string;\n}\n\nexport interface RoundModelingResult {\n roundName: string;\n preMoneyValuation: number;\n postMoneyValuation: number;\n investmentAmount: number;\n pricePerShare: number;\n newInvestorShares: number;\n\n optionPool: {\n preRoundShares: number;\n postRoundShares: number;\n newSharesCreated: number;\n postRoundPercent: string;\n } | null;\n\n conversions: ConversionEntry[];\n\n ownershipComparison: OwnershipEntry[];\n\n summary: string;\n disclaimer: string;\n auditTrail: string[];\n}\n\nconst DISCLAIMER = 'These calculations are estimates. Verify independently with your attorney before making decisions based on these numbers.';\n\n/** Round to 2 decimal places (cents). */\nfunction toCents(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\n/** Format percentage to 2 decimal places. */\nfunction toPercent(n: number): string {\n return `${(n * 100).toFixed(2)}%`;\n}\n\nexport function computeRoundModeling(input: RoundModelingInput): RoundModelingResult {\n const {\n investmentAmount,\n preMoneyValuation,\n existingShares,\n convertibles,\n currentOptionPoolShares,\n newOptionPoolPercent,\n roundName = 'New Round',\n newShareClassName = 'New Preferred',\n } = input;\n\n const auditTrail: string[] = [];\n\n // --- Step 1: Pre-round totals ---\n const existingShareTotal = existingShares.reduce((s, e) => s + e.shares, 0);\n const preRoundTotal = existingShareTotal + currentOptionPoolShares;\n\n auditTrail.push(`Pre-round: ${existingShareTotal.toLocaleString()} equity shares + ${currentOptionPoolShares.toLocaleString()} option pool = ${preRoundTotal.toLocaleString()} total shares`);\n\n // --- Step 2: Option pool shuffle (pre-money) ---\n let optionPoolResult: RoundModelingResult['optionPool'] = null;\n let poolNewShares = 0;\n\n if (newOptionPoolPercent != null && newOptionPoolPercent > 0) {\n // Pre-money option pool shuffle:\n // Target: pool = newOptionPoolPercent% of post-money shares\n // Post-money shares = pre-money shares + new investor shares\n // new investor shares = investmentAmount / pricePerShare\n // pricePerShare = preMoneyValuation / preMoneyShares\n // preMoneyShares includes the expanded pool\n //\n // Working backwards:\n // postMoneyShares = preMoneyShares + investmentAmount / (preMoneyValuation / preMoneyShares)\n // postMoneyShares = preMoneyShares + preMoneyShares * investmentAmount / preMoneyValuation\n // postMoneyShares = preMoneyShares * (1 + investmentAmount / preMoneyValuation)\n // postMoneyShares = preMoneyShares * postMoneyValuation / preMoneyValuation\n //\n // targetPoolShares = newOptionPoolPercent / 100 * postMoneyShares\n // We need: targetPoolShares = currentPoolShares + poolNewShares\n //\n // But preMoneyShares = preRoundTotal + poolNewShares (pool expands pre-money)\n //\n // targetPoolShares = (newOptionPoolPercent / 100) * preMoneyShares * (postMoneyValuation / preMoneyValuation)\n // (currentPoolShares + poolNewShares) = (newOptionPoolPercent / 100) * (preRoundTotal + poolNewShares) * (postMoney / preMoney)\n //\n // Simplified approach: target pool % of post-money cap table\n // postMoneyValuation = preMoneyValuation + investmentAmount\n // totalPostMoney = preMoneyShares * (postMoneyValuation / preMoneyValuation)\n // targetPool = totalPostMoney * poolPercent\n // preMoneyShares = (preRoundTotal - currentOptionPoolShares) + targetPool + (equity holders shares)\n //\n // Standard VC formula for pre-money pool shuffle:\n // targetPoolShares = poolPercent * postMoneyShares\n // postMoneyShares = preMoneyShares + newInvestorShares\n // newInvestorShares = investmentAmount / (preMoneyValuation / preMoneyShares)\n //\n // Let P = poolPercent, let S = preRoundTotal (before pool expansion)\n // We want pool size = P * (S + poolNewShares + investorShares)\n // pricePerShare = preMoneyValuation / (S + poolNewShares)\n // investorShares = investmentAmount * (S + poolNewShares) / preMoneyValuation\n // postMoneyShares = (S + poolNewShares) + investmentAmount * (S + poolNewShares) / preMoneyValuation\n // = (S + poolNewShares) * (preMoneyValuation + investmentAmount) / preMoneyValuation\n //\n // targetPool = P * (S + poolNewShares) * postMoney / preMoney\n // currentPool + poolNewShares = P * (S + poolNewShares) * postMoney / preMoney\n //\n // Let X = poolNewShares, let T = postMoney / preMoney\n // currentPool + X = P * (S + X) * T\n // currentPool + X = P * T * S + P * T * X\n // currentPool + X - P * T * X = P * T * S\n // X * (1 - P * T) = P * T * S - currentPool\n // X = (P * T * S - currentPool) / (1 - P * T)\n\n const poolPercent = newOptionPoolPercent / 100;\n const postMoneyValuation = preMoneyValuation + investmentAmount;\n const T = postMoneyValuation / preMoneyValuation;\n const denominator = 1 - poolPercent * T;\n\n if (denominator > 0) {\n poolNewShares = Math.max(0, Math.ceil((poolPercent * T * preRoundTotal - currentOptionPoolShares) / denominator));\n }\n\n const totalPoolShares = currentOptionPoolShares + poolNewShares;\n\n auditTrail.push(`Option pool shuffle: target ${newOptionPoolPercent}% of post-money cap table`);\n auditTrail.push(`Option pool: ${currentOptionPoolShares.toLocaleString()} existing + ${poolNewShares.toLocaleString()} new = ${totalPoolShares.toLocaleString()} total pool shares`);\n\n optionPoolResult = {\n preRoundShares: currentOptionPoolShares,\n postRoundShares: totalPoolShares,\n newSharesCreated: poolNewShares,\n postRoundPercent: '', // filled after we know total post-money shares\n };\n }\n\n // --- Step 3: Price per share ---\n const preMoneyShares = preRoundTotal + poolNewShares;\n const pricePerShare = preMoneyShares > 0\n ? preMoneyValuation / preMoneyShares\n : 0;\n\n auditTrail.push(`Pre-money shares (after pool shuffle): ${preMoneyShares.toLocaleString()}`);\n auditTrail.push(`Price per share: $${pricePerShare.toFixed(4)} ($${preMoneyValuation.toLocaleString()} / ${preMoneyShares.toLocaleString()} shares)`);\n\n // --- Step 4: Convert SAFEs/notes ---\n const conversions: ConversionEntry[] = [];\n let totalConversionShares = 0;\n\n for (const conv of convertibles) {\n const result = computeConversion({\n instrumentType: conv.instrumentType,\n amount: conv.amount,\n valuationCap: conv.valuationCap,\n discountRate: conv.discountRate,\n interestRate: conv.interestRate,\n grantDate: conv.grantDate,\n pricePerShare,\n preMoneyShares,\n });\n\n conversions.push({\n grantId: conv.grantId,\n stakeholderId: conv.stakeholderId,\n instrumentType: conv.instrumentType,\n originalAmount: conv.amount,\n conversionPrice: toCents(result.conversionPrice),\n shares: result.shares,\n formula: result.formula,\n });\n\n totalConversionShares += result.shares;\n auditTrail.push(`${conv.instrumentType} ($${conv.amount.toLocaleString()}): converts to ${result.shares.toLocaleString()} shares at $${result.conversionPrice.toFixed(4)}/share (${result.formula})`);\n }\n\n // --- Step 5: New investor shares ---\n const newInvestorShares = pricePerShare > 0\n ? Math.floor(investmentAmount / pricePerShare)\n : 0;\n const postMoneyValuation = preMoneyValuation + investmentAmount;\n\n auditTrail.push(`New investor: ${newInvestorShares.toLocaleString()} ${newShareClassName} shares for $${investmentAmount.toLocaleString()}`);\n auditTrail.push(`Post-money valuation: $${postMoneyValuation.toLocaleString()}`);\n\n // --- Step 6: Build before/after ownership ---\n const totalPostMoney = preMoneyShares + totalConversionShares + newInvestorShares;\n\n // Aggregate shares by stakeholder (before)\n const beforeMap = new Map<string, number>();\n for (const e of existingShares) {\n beforeMap.set(e.stakeholderId, (beforeMap.get(e.stakeholderId) ?? 0) + e.shares);\n }\n\n // Aggregate shares by stakeholder (after = before + converted shares)\n const afterMap = new Map<string, number>();\n for (const [sid, shares] of beforeMap) {\n afterMap.set(sid, shares);\n }\n for (const conv of conversions) {\n afterMap.set(conv.stakeholderId, (afterMap.get(conv.stakeholderId) ?? 0) + conv.shares);\n }\n\n // Collect all stakeholder IDs (including new investor placeholder)\n const allStakeholders = new Set([...beforeMap.keys(), ...afterMap.keys()]);\n\n const ownershipComparison: OwnershipEntry[] = [];\n for (const sid of allStakeholders) {\n const beforeShares = beforeMap.get(sid) ?? 0;\n const afterShares = afterMap.get(sid) ?? 0;\n const beforePct = preRoundTotal > 0 ? beforeShares / preRoundTotal : 0;\n const afterPct = totalPostMoney > 0 ? afterShares / totalPostMoney : 0;\n const dilution = beforePct - afterPct;\n\n ownershipComparison.push({\n stakeholderId: sid,\n beforeShares,\n beforePercent: toPercent(beforePct),\n afterShares,\n afterPercent: toPercent(afterPct),\n dilutionPercent: toPercent(dilution),\n });\n }\n\n // Add new investor row\n ownershipComparison.push({\n stakeholderId: '__new_investor__',\n beforeShares: 0,\n beforePercent: '0.00%',\n afterShares: newInvestorShares,\n afterPercent: toPercent(totalPostMoney > 0 ? newInvestorShares / totalPostMoney : 0),\n dilutionPercent: '0.00%',\n });\n\n // Add option pool row (if pool exists)\n const totalPoolShares = currentOptionPoolShares + poolNewShares;\n if (totalPoolShares > 0) {\n const poolBeforePct = preRoundTotal > 0 ? currentOptionPoolShares / preRoundTotal : 0;\n const poolAfterPct = totalPostMoney > 0 ? totalPoolShares / totalPostMoney : 0;\n ownershipComparison.push({\n stakeholderId: '__option_pool__',\n beforeShares: currentOptionPoolShares,\n beforePercent: toPercent(poolBeforePct),\n afterShares: totalPoolShares,\n afterPercent: toPercent(poolAfterPct),\n dilutionPercent: toPercent(poolBeforePct - poolAfterPct),\n });\n }\n\n // Sort by afterShares descending\n ownershipComparison.sort((a, b) => b.afterShares - a.afterShares);\n\n // Fill in option pool post-round percent\n if (optionPoolResult) {\n optionPoolResult.postRoundPercent = toPercent(totalPostMoney > 0 ? totalPoolShares / totalPostMoney : 0);\n }\n\n // --- Summary ---\n const summary = `${roundName}: $${investmentAmount.toLocaleString()} at $${preMoneyValuation.toLocaleString()} pre-money ($${postMoneyValuation.toLocaleString()} post). ${newInvestorShares.toLocaleString()} new shares at $${pricePerShare.toFixed(4)}/share.${conversions.length > 0 ? ` ${conversions.length} convertible(s) → ${totalConversionShares.toLocaleString()} shares.` : ''}${optionPoolResult ? ` Option pool: ${newOptionPoolPercent}% post-money (${poolNewShares.toLocaleString()} new shares).` : ''}`;\n\n return {\n roundName,\n preMoneyValuation,\n postMoneyValuation,\n investmentAmount,\n pricePerShare: toCents(pricePerShare),\n newInvestorShares,\n optionPool: optionPoolResult,\n conversions,\n ownershipComparison,\n summary,\n disclaimer: DISCLAIMER,\n auditTrail,\n };\n}\n","/**\n * domain/cap-table-import.ts — Equity document regex extraction engine.\n *\n * Extracts structured equity terms from document text using regex patterns.\n * Returns extraction results with a structured prompt for the host AI to refine.\n * Pure functions — no Supabase deps.\n */\n\n// --- Types ---\n\nexport interface ExtractedEquityTerms {\n instrumentType: string | null;\n investmentAmount: number | null;\n shares: number | null;\n pricePerShare: number | null;\n valuationCap: number | null;\n discountRate: number | null;\n interestRate: number | null;\n maturityDate: string | null;\n investorName: string | null;\n shareClassName: string | null;\n vestingMonths: number | null;\n cliffMonths: number | null;\n confidence: 'high' | 'medium' | 'low';\n extractedFields: string[];\n rawExcerpts: Record<string, string>;\n}\n\nexport interface ImportExtractionResult {\n proposedGrant: ExtractedEquityTerms;\n documentExcerpt: string;\n extractionNotes: string[];\n hostAiPrompt: string;\n}\n\n// --- Pattern Definitions ---\n\ninterface PatternDef {\n field: string;\n pattern: RegExp;\n extract: (match: RegExpMatchArray) => unknown;\n}\n\nfunction parseNumber(s: string): number {\n return Number(s.replace(/,/g, ''));\n}\n\nconst SAFE_PATTERNS: PatternDef[] = [\n {\n field: 'valuationCap',\n pattern: /(?:valuation\\s+cap|cap)\\s*(?:of|:|\\s)\\s*\\$?([\\d,]+(?:\\.\\d+)?)/i,\n extract: (m) => parseNumber(m[1]!),\n },\n {\n field: 'discountRate',\n pattern: /(?:discount\\s*(?:rate)?)\\s*(?:of|:|\\s)\\s*(\\d+(?:\\.\\d+)?)\\s*%/i,\n extract: (m) => Number(m[1]!) / 100,\n },\n {\n field: 'investmentAmount',\n pattern: /(?:purchase\\s+amount|investment\\s+amount|aggregate\\s+amount)\\s*(?:of|:|\\s)\\s*\\$?([\\d,]+(?:\\.\\d+)?)/i,\n extract: (m) => parseNumber(m[1]!),\n },\n];\n\nconst NOTE_PATTERNS: PatternDef[] = [\n {\n field: 'investmentAmount',\n pattern: /(?:principal|face)\\s+(?:amount|sum)\\s*(?:of|:|\\s)\\s*\\$?([\\d,]+(?:\\.\\d+)?)/i,\n extract: (m) => parseNumber(m[1]!),\n },\n {\n field: 'interestRate',\n pattern: /(?:interest\\s+rate)\\s*(?:of|:|\\s)\\s*(\\d+(?:\\.\\d+)?)\\s*%\\s*(?:per\\s+annum)?/i,\n extract: (m) => Number(m[1]!) / 100,\n },\n {\n field: 'maturityDate',\n pattern: /(?:maturity\\s+date)\\s*(?:of|:|\\s)\\s*([\\w\\s,]+\\d{4})/i,\n extract: (m) => m[1]!.trim(),\n },\n {\n field: 'valuationCap',\n pattern: /(?:valuation\\s+cap|cap)\\s*(?:of|:|\\s)\\s*\\$?([\\d,]+(?:\\.\\d+)?)/i,\n extract: (m) => parseNumber(m[1]!),\n },\n {\n field: 'discountRate',\n pattern: /(?:discount\\s*(?:rate)?)\\s*(?:of|:|\\s)\\s*(\\d+(?:\\.\\d+)?)\\s*%/i,\n extract: (m) => Number(m[1]!) / 100,\n },\n];\n\nconst STOCK_PATTERNS: PatternDef[] = [\n {\n field: 'shares',\n pattern: /(\\d[\\d,]*)\\s+shares?\\s+of\\s+([\\w\\s]+?)\\s+(?:stock|equity)/i,\n extract: (m) => parseNumber(m[1]!),\n },\n {\n field: 'shareClassName',\n pattern: /(\\d[\\d,]*)\\s+shares?\\s+of\\s+([\\w\\s]+?)\\s+(?:stock|equity)/i,\n extract: (m) => m[2]!.trim(),\n },\n {\n field: 'pricePerShare',\n pattern: /\\$?([\\d.]+)\\s+per\\s+share/i,\n extract: (m) => Number(m[1]!),\n },\n];\n\nconst OPTION_PATTERNS: PatternDef[] = [\n {\n field: 'shares',\n pattern: /option\\s+to\\s+purchase\\s+(\\d[\\d,]*)\\s+shares/i,\n extract: (m) => parseNumber(m[1]!),\n },\n {\n field: 'pricePerShare',\n pattern: /exercise\\s+price\\s*(?:of|:|\\s)\\s*\\$?([\\d.]+)/i,\n extract: (m) => Number(m[1]!),\n },\n];\n\nconst VESTING_PATTERNS: PatternDef[] = [\n {\n field: 'vestingMonths',\n pattern: /vest(?:ing|s)?\\s+over\\s+(\\d+)\\s+years?/i,\n extract: (m) => Number(m[1]!) * 12,\n },\n {\n field: 'vestingMonths',\n pattern: /vest(?:ing|s)?\\s+over\\s+(\\d+)\\s+months?/i,\n extract: (m) => Number(m[1]!),\n },\n {\n field: 'cliffMonths',\n pattern: /(\\d+)[\\s-]month\\s+cliff/i,\n extract: (m) => Number(m[1]!),\n },\n {\n field: 'cliffMonths',\n pattern: /(\\d+)[\\s-]year\\s+cliff/i,\n extract: (m) => Number(m[1]!) * 12,\n },\n];\n\nconst COMMON_PATTERNS: PatternDef[] = [\n {\n field: 'investorName',\n pattern: /(?:investor|purchaser|holder)\\s*(?:name)?(?::|\\.)\\s*(.+)/i,\n extract: (m) => m[1]!.trim(),\n },\n];\n\n// --- Instrument Type Detection ---\n\nfunction detectInstrumentType(text: string): string | null {\n const lower = text.toLowerCase();\n\n if (/post[\\s-]?money\\s+safe/i.test(text) || /pre[\\s-]?money\\s+safe/i.test(text) || /simple\\s+agreement\\s+for\\s+future\\s+equity/i.test(text)) {\n return 'safe';\n }\n if (/convertible\\s+(?:promissory\\s+)?note/i.test(text)) {\n return 'convertible_note';\n }\n if (/stock\\s+option\\s+(?:agreement|grant)/i.test(text) || /option\\s+to\\s+purchase/i.test(text)) {\n return 'option';\n }\n if (/(?:restricted\\s+)?stock\\s+purchase\\s+agreement/i.test(text)) {\n return 'common_stock';\n }\n if (lower.includes('preferred stock')) {\n return 'preferred_stock';\n }\n if (lower.includes('warrant')) {\n return 'warrant';\n }\n\n return null;\n}\n\n// --- Pattern Selection ---\n\nfunction getPatternsForType(instrumentType: string | null): PatternDef[] {\n const patterns: PatternDef[] = [...COMMON_PATTERNS, ...VESTING_PATTERNS];\n\n switch (instrumentType) {\n case 'safe':\n patterns.push(...SAFE_PATTERNS);\n break;\n case 'convertible_note':\n patterns.push(...NOTE_PATTERNS);\n break;\n case 'common_stock':\n case 'preferred_stock':\n patterns.push(...STOCK_PATTERNS);\n break;\n case 'option':\n case 'warrant':\n patterns.push(...OPTION_PATTERNS);\n break;\n default:\n // Unknown type — try all patterns\n patterns.push(...SAFE_PATTERNS, ...NOTE_PATTERNS, ...STOCK_PATTERNS, ...OPTION_PATTERNS);\n break;\n }\n\n return patterns;\n}\n\n// --- Main Extraction ---\n\nexport function extractEquityTerms(text: string, instrumentHint?: string): ImportExtractionResult {\n const detectedType = instrumentHint ?? detectInstrumentType(text);\n const patterns = getPatternsForType(detectedType);\n const extractionNotes: string[] = [];\n\n const extracted: Record<string, unknown> = {};\n const rawExcerpts: Record<string, string> = {};\n const extractedFields: string[] = [];\n\n // Run patterns — first match wins per field\n for (const p of patterns) {\n if (extracted[p.field] != null) continue; // already found\n const match = p.pattern.exec(text);\n if (match) {\n extracted[p.field] = p.extract(match);\n rawExcerpts[p.field] = match[0];\n extractedFields.push(p.field);\n }\n }\n\n // Set instrument type\n if (detectedType) {\n extracted.instrumentType = detectedType;\n extractedFields.push('instrumentType');\n extractionNotes.push(`Detected instrument type: ${detectedType}`);\n } else {\n extractionNotes.push('Could not detect instrument type from document text.');\n }\n\n // Determine confidence\n const fieldCount = extractedFields.length;\n const confidence: 'high' | 'medium' | 'low' =\n fieldCount >= 5 ? 'high'\n : fieldCount >= 3 ? 'medium'\n : 'low';\n\n extractionNotes.push(`Extracted ${fieldCount} field(s) with ${confidence} confidence.`);\n\n if (fieldCount === 0) {\n extractionNotes.push('No equity terms found. The document may not contain standard investment language.');\n }\n\n const proposedGrant: ExtractedEquityTerms = {\n instrumentType: (extracted.instrumentType as string) ?? null,\n investmentAmount: (extracted.investmentAmount as number) ?? null,\n shares: (extracted.shares as number) ?? null,\n pricePerShare: (extracted.pricePerShare as number) ?? null,\n valuationCap: (extracted.valuationCap as number) ?? null,\n discountRate: (extracted.discountRate as number) ?? null,\n interestRate: (extracted.interestRate as number) ?? null,\n maturityDate: (extracted.maturityDate as string) ?? null,\n investorName: (extracted.investorName as string) ?? null,\n shareClassName: (extracted.shareClassName as string) ?? null,\n vestingMonths: (extracted.vestingMonths as number) ?? null,\n cliffMonths: (extracted.cliffMonths as number) ?? null,\n confidence,\n extractedFields,\n rawExcerpts,\n };\n\n // Build document excerpt (first ~2000 chars)\n const documentExcerpt = text.slice(0, 2000);\n\n // Build host AI prompt\n const hostAiPrompt = buildHostAiPrompt(proposedGrant, documentExcerpt);\n\n return {\n proposedGrant,\n documentExcerpt,\n extractionNotes,\n hostAiPrompt,\n };\n}\n\n// --- Host AI Prompt ---\n\nfunction buildHostAiPrompt(terms: ExtractedEquityTerms, excerpt: string): string {\n const lines: string[] = [\n 'I extracted the following equity terms from a document using regex patterns.',\n `Confidence level: ${terms.confidence}`,\n '',\n '**Regex-extracted values:**',\n ];\n\n if (terms.instrumentType) lines.push(`- Instrument type: ${terms.instrumentType}`);\n if (terms.investmentAmount != null) lines.push(`- Investment amount: $${terms.investmentAmount.toLocaleString()}`);\n if (terms.shares != null) lines.push(`- Shares: ${terms.shares.toLocaleString()}`);\n if (terms.pricePerShare != null) lines.push(`- Price per share: $${terms.pricePerShare}`);\n if (terms.valuationCap != null) lines.push(`- Valuation cap: $${terms.valuationCap.toLocaleString()}`);\n if (terms.discountRate != null) lines.push(`- Discount rate: ${(terms.discountRate * 100).toFixed(1)}%`);\n if (terms.interestRate != null) lines.push(`- Interest rate: ${(terms.interestRate * 100).toFixed(1)}%`);\n if (terms.maturityDate) lines.push(`- Maturity date: ${terms.maturityDate}`);\n if (terms.investorName) lines.push(`- Investor: ${terms.investorName}`);\n if (terms.shareClassName) lines.push(`- Share class: ${terms.shareClassName}`);\n if (terms.vestingMonths != null) lines.push(`- Vesting: ${terms.vestingMonths} months`);\n if (terms.cliffMonths != null) lines.push(`- Cliff: ${terms.cliffMonths} months`);\n\n lines.push('');\n lines.push('**Document excerpt:**');\n lines.push('```');\n lines.push(excerpt.slice(0, 1000));\n lines.push('```');\n lines.push('');\n lines.push('Please review the document excerpt, confirm or correct the extracted values,');\n lines.push('and identify any values the regex missed. Then propose an `add_grant` call');\n lines.push('with the confirmed values. Ask the user to confirm before saving.');\n\n return lines.join('\\n');\n}\n","/**\n * domain/cap-table-export.ts — Cap table export formatters.\n *\n * Pure formatting functions — no Supabase deps.\n * Supports CSV, Markdown, JSON, and XLSX (via exceljs).\n */\n\nimport ExcelJS from 'exceljs';\nimport type { ShareClassRecord } from '../db/share-classes.js';\nimport type { StakeholderRecord } from '../db/stakeholders.js';\nimport type { EquityGrantRecord } from '../db/equity-grants.js';\nimport type { VestingResult } from './vesting.js';\n\nexport interface CapTableExportInput {\n companyName: string;\n asOfDate: string;\n shareClasses: ShareClassRecord[];\n stakeholders: StakeholderRecord[];\n grants: EquityGrantRecord[];\n vestingData: Array<{ grantId: string; vesting: VestingResult | null }>;\n}\n\n// --- Helpers ---\n\nfunction stakeholderName(id: string, stakeholders: StakeholderRecord[]): string {\n return stakeholders.find((s) => s.id === id)?.name ?? 'Unknown';\n}\n\nfunction className(id: string | null, classes: ShareClassRecord[]): string {\n if (!id) return 'N/A';\n return classes.find((c) => c.id === id)?.name ?? 'Unknown';\n}\n\nfunction fmtCurrency(n: number | null): string {\n if (n == null) return '';\n return n.toFixed(2);\n}\n\nfunction fmtShares(n: number | null): string {\n if (n == null) return '';\n return n.toLocaleString('en-US');\n}\n\nfunction escapeCsv(val: string): string {\n if (val.includes(',') || val.includes('\"') || val.includes('\\n')) {\n return `\"${val.replace(/\"/g, '\"\"')}\"`;\n }\n return val;\n}\n\n// --- CSV ---\n\nconst CSV_HEADERS = [\n 'Stakeholder',\n 'Type',\n 'Instrument',\n 'Share Class',\n 'Shares',\n 'Amount',\n 'Price/Share',\n 'Status',\n 'Grant Date',\n 'Vesting %',\n];\n\nexport function exportAsCsv(input: CapTableExportInput): string {\n const vestingMap = new Map(input.vestingData.map((v) => [v.grantId, v.vesting]));\n const lines: string[] = [CSV_HEADERS.join(',')];\n\n for (const g of input.grants) {\n const vesting = vestingMap.get(g.id);\n const row = [\n escapeCsv(stakeholderName(g.stakeholderId, input.stakeholders)),\n g.instrumentType,\n g.instrumentType,\n escapeCsv(className(g.shareClassId, input.shareClasses)),\n fmtShares(g.shares),\n fmtCurrency(g.amount),\n fmtCurrency(g.pricePerShare),\n g.status,\n g.grantDate,\n vesting ? vesting.vestedPercentage : 'N/A',\n ];\n lines.push(row.join(','));\n }\n\n return lines.join('\\n');\n}\n\n// --- Markdown ---\n\nexport function exportAsMarkdown(input: CapTableExportInput): string {\n const vestingMap = new Map(input.vestingData.map((v) => [v.grantId, v.vesting]));\n const lines: string[] = [];\n\n // Header\n lines.push(`# Cap Table — ${input.companyName}`);\n lines.push(`**As of:** ${input.asOfDate}`);\n lines.push('');\n\n // Summary\n const totalAuthorized = input.shareClasses.reduce((s, c) => s + c.authorizedShares, 0);\n const totalOutstanding = input.grants\n .filter((g) => g.status === 'active' && g.shares != null)\n .reduce((s, g) => s + g.shares!, 0);\n\n lines.push('## Summary');\n lines.push('');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Share Classes | ${input.shareClasses.length} |`);\n lines.push(`| Stakeholders | ${input.stakeholders.length} |`);\n lines.push(`| Total Authorized | ${totalAuthorized.toLocaleString()} |`);\n lines.push(`| Total Outstanding | ${totalOutstanding.toLocaleString()} |`);\n lines.push(`| Active Grants | ${input.grants.filter((g) => g.status === 'active').length} |`);\n lines.push('');\n\n // Grants table\n lines.push('## Grants');\n lines.push('');\n lines.push('| Stakeholder | Instrument | Share Class | Shares | Amount | Price/Share | Status | Grant Date | Vesting |');\n lines.push('|-------------|------------|-------------|--------|--------|-------------|--------|------------|---------|');\n\n for (const g of input.grants) {\n const vesting = vestingMap.get(g.id);\n const row = [\n stakeholderName(g.stakeholderId, input.stakeholders),\n g.instrumentType,\n className(g.shareClassId, input.shareClasses),\n fmtShares(g.shares) || '—',\n g.amount != null ? `$${fmtCurrency(g.amount)}` : '—',\n g.pricePerShare != null ? `$${fmtCurrency(g.pricePerShare)}` : '—',\n g.status,\n g.grantDate,\n vesting ? vesting.vestedPercentage : 'N/A',\n ];\n lines.push(`| ${row.join(' | ')} |`);\n }\n\n return lines.join('\\n');\n}\n\n// --- JSON ---\n\nexport function exportAsJson(input: CapTableExportInput): object {\n return {\n schemaVersion: '1.0',\n exportedAt: new Date().toISOString(),\n company: input.companyName,\n asOfDate: input.asOfDate,\n shareClasses: input.shareClasses.map((c) => ({\n id: c.id,\n name: c.name,\n classType: c.classType,\n authorizedShares: c.authorizedShares,\n seniority: c.seniority,\n })),\n stakeholders: input.stakeholders.map((s) => ({\n id: s.id,\n name: s.name,\n type: s.type,\n })),\n grants: input.grants.map((g) => ({\n id: g.id,\n stakeholderId: g.stakeholderId,\n stakeholderName: stakeholderName(g.stakeholderId, input.stakeholders),\n instrumentType: g.instrumentType,\n shareClassId: g.shareClassId,\n shareClassName: className(g.shareClassId, input.shareClasses),\n shares: g.shares,\n amount: g.amount,\n pricePerShare: g.pricePerShare,\n status: g.status,\n grantDate: g.grantDate,\n })),\n vestingSchedules: input.vestingData\n .filter((v) => v.vesting != null)\n .map((v) => ({\n grantId: v.grantId,\n ...v.vesting,\n })),\n };\n}\n\n// --- XLSX ---\n\nexport async function exportAsXlsx(input: CapTableExportInput): Promise<Buffer> {\n const workbook = new ExcelJS.Workbook();\n workbook.creator = 'Forcefield';\n workbook.created = new Date();\n\n // --- Sheet 1: Summary ---\n const summary = workbook.addWorksheet('Summary');\n\n const totalAuthorized = input.shareClasses.reduce((s, c) => s + c.authorizedShares, 0);\n const totalOutstanding = input.grants\n .filter((g) => g.status === 'active' && g.shares != null)\n .reduce((s, g) => s + g.shares!, 0);\n\n summary.columns = [\n { header: 'Metric', key: 'metric', width: 25 },\n { header: 'Value', key: 'value', width: 25 },\n ];\n\n const summaryRows = [\n { metric: 'Company', value: input.companyName },\n { metric: 'As of Date', value: input.asOfDate },\n { metric: 'Total Authorized Shares', value: totalAuthorized },\n { metric: 'Total Outstanding Shares', value: totalOutstanding },\n { metric: 'Share Classes', value: input.shareClasses.length },\n { metric: 'Stakeholders', value: input.stakeholders.length },\n { metric: 'Active Grants', value: input.grants.filter((g) => g.status === 'active').length },\n ];\n\n // Add share class breakdown\n for (const sc of input.shareClasses) {\n summaryRows.push({ metric: ` ${sc.name} (authorized)`, value: sc.authorizedShares });\n }\n\n summary.addRows(summaryRows);\n\n // Bold header row\n const summaryHeader = summary.getRow(1);\n summaryHeader.font = { bold: true };\n summaryHeader.eachCell((cell) => { cell.font = { bold: true }; });\n\n // Freeze header\n summary.views = [{ state: 'frozen', ySplit: 1 }];\n\n // --- Sheet 2: Grants Detail ---\n const grants = workbook.addWorksheet('Grants Detail');\n\n grants.columns = [\n { header: 'Stakeholder', key: 'stakeholder', width: 25 },\n { header: 'Instrument', key: 'instrument', width: 18 },\n { header: 'Share Class', key: 'shareClass', width: 20 },\n { header: 'Shares', key: 'shares', width: 15 },\n { header: 'Amount ($)', key: 'amount', width: 15 },\n { header: 'Price/Share ($)', key: 'pps', width: 15 },\n { header: 'Status', key: 'status', width: 12 },\n { header: 'Grant Date', key: 'grantDate', width: 14 },\n ];\n\n for (const g of input.grants) {\n grants.addRow({\n stakeholder: stakeholderName(g.stakeholderId, input.stakeholders),\n instrument: g.instrumentType,\n shareClass: className(g.shareClassId, input.shareClasses),\n shares: g.shares,\n amount: g.amount,\n pps: g.pricePerShare,\n status: g.status,\n grantDate: g.grantDate,\n });\n }\n\n // Bold header + freeze\n const grantsHeader = grants.getRow(1);\n grantsHeader.font = { bold: true };\n grantsHeader.eachCell((cell) => { cell.font = { bold: true }; });\n grants.views = [{ state: 'frozen', ySplit: 1 }];\n\n // Number formatting\n grants.getColumn('shares').numFmt = '#,##0';\n grants.getColumn('amount').numFmt = '#,##0.00';\n grants.getColumn('pps').numFmt = '#,##0.0000';\n\n // --- Sheet 3: Vesting Schedule ---\n const vesting = workbook.addWorksheet('Vesting Schedule');\n\n vesting.columns = [\n { header: 'Stakeholder', key: 'stakeholder', width: 25 },\n { header: 'Grant ID', key: 'grantId', width: 15 },\n { header: 'Total Shares', key: 'total', width: 15 },\n { header: 'Vested', key: 'vested', width: 15 },\n { header: 'Unvested', key: 'unvested', width: 15 },\n { header: 'Vested %', key: 'pct', width: 12 },\n { header: 'Cliff Date', key: 'cliff', width: 14 },\n { header: 'Next Vest Date', key: 'nextVest', width: 14 },\n ];\n\n for (const v of input.vestingData) {\n if (!v.vesting) continue;\n const grant = input.grants.find((g) => g.id === v.grantId);\n vesting.addRow({\n stakeholder: grant ? stakeholderName(grant.stakeholderId, input.stakeholders) : 'Unknown',\n grantId: v.grantId.slice(0, 8),\n total: v.vesting.totalShares,\n vested: v.vesting.vestedShares,\n unvested: v.vesting.unvestedShares,\n pct: v.vesting.vestedPercentage,\n cliff: v.vesting.cliffDate ?? 'N/A',\n nextVest: v.vesting.nextVestingDate ?? 'N/A',\n });\n }\n\n // Bold header + freeze\n const vestingHeader = vesting.getRow(1);\n vestingHeader.font = { bold: true };\n vestingHeader.eachCell((cell) => { cell.font = { bold: true }; });\n vesting.views = [{ state: 'frozen', ySplit: 1 }];\n\n // Number formatting\n vesting.getColumn('total').numFmt = '#,##0';\n vesting.getColumn('vested').numFmt = '#,##0';\n vesting.getColumn('unvested').numFmt = '#,##0';\n\n // Write to buffer\n const buffer = await workbook.xlsx.writeBuffer();\n return Buffer.from(buffer);\n}\n","/**\n * tools/ff-equity.ts — ff_equity tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n * All 15 actions active: create_class, create_plan, list_classes, add_stakeholder,\n * add_grant, update_grant, cancel_grant, exercise_grant, convert_grant,\n * ownership, vesting, waterfall, model_round, import, export.\n */\n\nimport type { SessionContext } from '../auth/session.js';\nimport { FfEquitySchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { validateCompanyOwnership } from '../auth/ownership.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport { createShareClass, listShareClasses, listAllShareClasses, getShareClassById } from '../db/share-classes.js';\nimport { createEquityPlan } from '../db/equity-plans.js';\nimport { createStakeholder, getStakeholderById, listStakeholdersByCompany } from '../db/stakeholders.js';\nimport {\n createEquityGrant,\n getEquityGrantById,\n updateGrantMetadata,\n updateGrantStatus,\n reduceGrantShares,\n getActiveShareCount,\n listGrantsByCompany,\n getVestingByGrantId,\n} from '../db/equity-grants.js';\nimport { computeConversion, compute83bDeadline } from '../domain/conversion.js';\nimport { computeOwnership } from '../domain/ownership.js';\nimport { computeVesting } from '../domain/vesting.js';\nimport { computeWaterfall } from '../domain/waterfall.js';\nimport type { WaterfallClassInput } from '../domain/waterfall.js';\nimport { computeRoundModeling } from '../domain/modeling.js';\nimport { extractEquityTerms } from '../domain/cap-table-import.js';\nimport { exportAsCsv, exportAsMarkdown, exportAsJson, exportAsXlsx } from '../domain/cap-table-export.js';\nimport { getDocumentById } from '../db/documents.js';\n\nconst TOOL_NAME = 'ff_equity';\n\n/** Stock-like instrument types that may qualify for 83(b) election. */\nconst STOCK_TYPES = new Set([\n 'common_stock',\n 'preferred_stock',\n 'membership_interest',\n]);\n\n/** Instrument types that can be exercised. */\nconst EXERCISABLE_TYPES = new Set([\n 'option_iso',\n 'option_nso',\n 'warrant',\n]);\n\n/** Instrument types that can be converted. */\nconst CONVERTIBLE_TYPES = new Set([\n 'safe',\n 'convertible_note',\n]);\n\nexport async function handleFfEquity(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n _extra?: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfEquitySchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Steps 3-5: Authorize → Execute → Respond (per action)\n switch (input.action) {\n case 'create_class': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const shareClass = await createShareClass(ctx.supabase, {\n userId: ctx.userId,\n companyId: input.companyId,\n name: input.name,\n classType: input.classType,\n authorizedShares: input.authorizedShares,\n seniority: input.seniority,\n parValue: input.parValue,\n pricePerShare: input.pricePerShare,\n votesPerShare: input.votesPerShare,\n series: input.series,\n liquidationPreference: input.liquidationPreference,\n participationCap: input.participationCap,\n conversionRights: input.conversionRights,\n antiDilutionType: input.antiDilutionType,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'create_class',\n companyId: input.companyId,\n result: 'success',\n input: { name: input.name, classType: input.classType },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: shareClass,\n message: `Created share class \"${shareClass.name}\" (${shareClass.classType}, ${shareClass.authorizedShares.toLocaleString()} authorized shares).`,\n nextAction: {\n tool: TOOL_NAME,\n action: 'create_plan',\n params: { companyId: input.companyId, shareClassId: shareClass.id },\n },\n });\n }\n\n case 'create_plan': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Security: verify shareClassId belongs to the same company\n const shareClass = await getShareClassById(ctx.supabase, input.shareClassId);\n if (!shareClass) {\n throw ffError.notFound('share class', input.shareClassId);\n }\n if (shareClass.companyId !== input.companyId) {\n throw ffError.notFound('share class', input.shareClassId);\n }\n\n // Step 4: Execute\n const plan = await createEquityPlan(ctx.supabase, {\n userId: ctx.userId,\n companyId: input.companyId,\n shareClassId: input.shareClassId,\n name: input.name,\n initialSharesReserved: input.initialSharesReserved,\n boardApprovalDate: input.boardApprovalDate,\n cancellationBehavior: input.cancellationBehavior,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'create_plan',\n companyId: input.companyId,\n result: 'success',\n input: { name: input.name, shareClassId: input.shareClassId },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: plan,\n message: `Created equity plan \"${plan.name}\" with ${plan.initialSharesReserved.toLocaleString()} shares reserved (linked to \"${shareClass.name}\").`,\n nextAction: {\n tool: TOOL_NAME,\n action: 'add_stakeholder',\n params: { companyId: input.companyId },\n },\n });\n }\n\n case 'list_classes': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const result = await listShareClasses(\n ctx.supabase,\n input.companyId,\n input.limit,\n input.offset,\n );\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: result,\n message: result.total === 0\n ? 'No share classes found. Use create_class to add one.'\n : `${result.total} share class(es) found, sorted by seniority.`,\n ...(result.total === 0 && {\n nextAction: {\n tool: TOOL_NAME,\n action: 'create_class',\n params: { companyId: input.companyId },\n },\n }),\n });\n }\n\n case 'add_stakeholder': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const stakeholder = await createStakeholder(ctx.supabase, {\n userId: ctx.userId,\n companyId: input.companyId,\n name: input.name,\n email: input.email,\n type: input.type,\n stakeholderType: input.stakeholderType,\n institutionName: input.institutionName,\n relationshipStartDate: input.relationshipStartDate,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'add_stakeholder',\n companyId: input.companyId,\n result: 'success',\n input: { name: input.name, type: input.type },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: stakeholder,\n message: `Added stakeholder \"${stakeholder.name}\" (${stakeholder.type}).`,\n nextAction: {\n tool: TOOL_NAME,\n action: 'add_grant',\n params: { companyId: input.companyId, stakeholderId: stakeholder.id },\n },\n });\n }\n\n case 'add_grant': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Security: verify stakeholder belongs to company\n const stakeholder = await getStakeholderById(ctx.supabase, input.stakeholderId);\n if (!stakeholder) {\n throw ffError.notFound('stakeholder', input.stakeholderId);\n }\n if (stakeholder.companyId !== input.companyId) {\n throw ffError.notFound('stakeholder', input.stakeholderId);\n }\n\n // Security: verify shareClassId belongs to company (if provided)\n if (input.shareClassId) {\n const shareClass = await getShareClassById(ctx.supabase, input.shareClassId);\n if (!shareClass || shareClass.companyId !== input.companyId) {\n throw ffError.notFound('share class', input.shareClassId);\n }\n }\n\n // Step 4: Execute\n const { grant, vesting } = await createEquityGrant(ctx.supabase, {\n userId: ctx.userId,\n companyId: input.companyId,\n stakeholderId: input.stakeholderId,\n instrumentType: input.instrumentType,\n shareClassId: input.shareClassId,\n equityPlanId: input.equityPlanId,\n shares: input.shares,\n amount: input.amount,\n pricePerShare: input.pricePerShare,\n valuationCap: input.valuationCap,\n discountRate: input.discountRate,\n interestRate: input.interestRate,\n grantDate: input.grantDate,\n boardApprovalDate: input.boardApprovalDate,\n vestingMonths: input.vestingMonths,\n cliffMonths: input.cliffMonths,\n vestingStartDate: input.vestingStartDate,\n });\n\n // Compute 83(b) warning if applicable\n const warnings: string[] = [];\n if (STOCK_TYPES.has(input.instrumentType) && input.vestingMonths) {\n const deadline = compute83bDeadline(input.grantDate);\n warnings.push(\n `83(b) election deadline: ${deadline} (30 days from grant). Must be filed with the IRS within 30 days of grant date to avoid higher taxes on vesting.`,\n );\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'add_grant',\n companyId: input.companyId,\n result: 'success',\n input: { instrumentType: input.instrumentType, stakeholderId: input.stakeholderId },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: { grant, vesting, ...(warnings.length > 0 && { warnings }) },\n message: `Recorded ${input.instrumentType} grant for \"${stakeholder.name}\".`,\n nextAction: {\n tool: TOOL_NAME,\n action: 'add_grant',\n params: { companyId: input.companyId },\n },\n });\n }\n\n case 'update_grant': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Security: verify grant belongs to company\n const grant = await getEquityGrantById(ctx.supabase, input.grantId);\n if (!grant) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n if (grant.companyId !== input.companyId) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n\n // Step 4: Execute\n const updated = await updateGrantMetadata(ctx.supabase, input.grantId, {\n election83bFiled: input.election83bFiled,\n election83bDate: input.election83bDate,\n notes: input.notes,\n boardApprovalDate: input.boardApprovalDate,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'update_grant',\n companyId: input.companyId,\n result: 'success',\n input: { grantId: input.grantId },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: updated,\n message: `Updated grant metadata.`,\n });\n }\n\n case 'cancel_grant': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Security: verify grant belongs to company and is active\n const grant = await getEquityGrantById(ctx.supabase, input.grantId);\n if (!grant) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n if (grant.companyId !== input.companyId) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n if (grant.status !== 'active') {\n throw ffError.validationFailed([{\n path: 'grantId',\n message: `Cannot cancel grant with status \"${grant.status}\". Only active grants can be cancelled.`,\n }]);\n }\n\n // Step 4: Execute\n const cancelled = await updateGrantStatus(ctx.supabase, input.grantId, 'cancelled', input.reason);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'cancel_grant',\n companyId: input.companyId,\n result: 'success',\n input: { grantId: input.grantId },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: cancelled,\n message: `Grant cancelled.${input.reason ? ` Reason: ${input.reason}` : ''}`,\n });\n }\n\n case 'exercise_grant': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Security: verify grant belongs to company\n const grant = await getEquityGrantById(ctx.supabase, input.grantId);\n if (!grant) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n if (grant.companyId !== input.companyId) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n\n // Validate: must be exercisable type\n if (!EXERCISABLE_TYPES.has(grant.instrumentType)) {\n throw ffError.validationFailed([{\n path: 'grantId',\n message: `Cannot exercise ${grant.instrumentType}. Only options (ISO/NSO) and warrants can be exercised.`,\n }]);\n }\n\n // Validate: must be active\n if (grant.status !== 'active') {\n throw ffError.validationFailed([{\n path: 'grantId',\n message: `Cannot exercise grant with status \"${grant.status}\". Only active grants can be exercised.`,\n }]);\n }\n\n // Validate: shares available\n if (grant.shares == null || input.sharesToExercise > grant.shares) {\n throw ffError.validationFailed([{\n path: 'sharesToExercise',\n message: `Cannot exercise ${input.sharesToExercise} shares. Grant has ${grant.shares ?? 0} shares available.`,\n }]);\n }\n\n // Step 4: Execute\n const isFullExercise = input.sharesToExercise === grant.shares;\n let updatedGrant: typeof grant;\n let exercisedGrant: typeof grant | undefined;\n\n if (isFullExercise) {\n // Full exercise: update status\n updatedGrant = await updateGrantStatus(ctx.supabase, input.grantId, 'exercised');\n } else {\n // Partial exercise: reduce original, create exercised portion\n updatedGrant = await reduceGrantShares(ctx.supabase, input.grantId, input.sharesToExercise);\n\n const { grant: newGrant } = await createEquityGrant(ctx.supabase, {\n userId: ctx.userId,\n companyId: grant.companyId,\n stakeholderId: grant.stakeholderId,\n instrumentType: grant.instrumentType,\n shareClassId: grant.shareClassId ?? undefined,\n equityPlanId: grant.equityPlanId ?? undefined,\n shares: input.sharesToExercise,\n pricePerShare: grant.pricePerShare ?? undefined,\n grantDate: grant.grantDate,\n boardApprovalDate: grant.boardApprovalDate ?? undefined,\n status: 'exercised',\n });\n exercisedGrant = newGrant;\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'exercise_grant',\n companyId: input.companyId,\n result: 'success',\n input: { grantId: input.grantId, sharesToExercise: input.sharesToExercise },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: {\n grant: updatedGrant,\n ...(exercisedGrant && { exercisedPortion: exercisedGrant }),\n },\n message: isFullExercise\n ? `Fully exercised ${input.sharesToExercise} shares.`\n : `Partially exercised ${input.sharesToExercise} shares. ${updatedGrant.shares} shares remain.`,\n });\n }\n\n case 'convert_grant': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Security: verify grant belongs to company\n const grant = await getEquityGrantById(ctx.supabase, input.grantId);\n if (!grant) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n if (grant.companyId !== input.companyId) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n\n // Validate: must be convertible type\n if (!CONVERTIBLE_TYPES.has(grant.instrumentType)) {\n throw ffError.validationFailed([{\n path: 'grantId',\n message: `Cannot convert ${grant.instrumentType}. Only SAFEs and convertible notes can be converted.`,\n }]);\n }\n\n // Validate: must be active\n if (grant.status !== 'active') {\n throw ffError.validationFailed([{\n path: 'grantId',\n message: `Cannot convert grant with status \"${grant.status}\". Only active grants can be converted.`,\n }]);\n }\n\n // Security: verify target share class belongs to company\n const targetClass = await getShareClassById(ctx.supabase, input.shareClassId);\n if (!targetClass || targetClass.companyId !== input.companyId) {\n throw ffError.notFound('share class', input.shareClassId);\n }\n\n // Step 4: Execute\n const preMoneyShares = await getActiveShareCount(ctx.supabase, input.companyId);\n\n const conversion = computeConversion({\n instrumentType: grant.instrumentType as 'safe' | 'convertible_note',\n amount: grant.amount ?? 0,\n valuationCap: grant.valuationCap ?? undefined,\n discountRate: grant.discountRate ?? undefined,\n interestRate: grant.interestRate ?? undefined,\n grantDate: grant.grantDate,\n pricePerShare: input.pricePerShare,\n preMoneyShares,\n });\n\n // Create new equity grant for converted shares\n const { grant: newGrant } = await createEquityGrant(ctx.supabase, {\n userId: ctx.userId,\n companyId: grant.companyId,\n stakeholderId: grant.stakeholderId,\n instrumentType: 'preferred_stock',\n shareClassId: input.shareClassId,\n shares: conversion.shares,\n pricePerShare: conversion.conversionPrice,\n grantDate: grant.grantDate,\n convertedFromId: grant.id,\n });\n\n // Mark original as converted\n const convertedGrant = await updateGrantStatus(ctx.supabase, input.grantId, 'converted');\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'convert_grant',\n companyId: input.companyId,\n result: 'success',\n input: { grantId: input.grantId, shareClassId: input.shareClassId },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: {\n original: convertedGrant,\n converted: newGrant,\n conversion,\n },\n message: `Converted ${grant.instrumentType} to ${conversion.shares} shares of \"${targetClass.name}\" at $${conversion.conversionPrice.toFixed(4)}/share (${conversion.formula}).`,\n });\n }\n\n case 'ownership': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const grants = await listGrantsByCompany(ctx.supabase, input.companyId, { status: 'active' });\n\n // Empty state\n if (grants.length === 0) {\n done();\n return formatToolResponse({\n data: { stakeholders: [], totalShares: 0 },\n message: 'No equity grants recorded. Run /ff-cap-table to get started.',\n });\n }\n\n const ownershipResult = computeOwnership({\n grants,\n includeOptions: input.includeOptions,\n includeSafes: input.includeSafes,\n includeWarrants: input.includeWarrants,\n });\n\n // Enrich with stakeholder names\n const stakeholders = await listStakeholdersByCompany(ctx.supabase, input.companyId);\n const nameMap = new Map(stakeholders.map((s) => [s.id, s.name]));\n for (const entry of ownershipResult.stakeholders) {\n (entry as unknown as Record<string, unknown>).stakeholderName = nameMap.get(entry.stakeholderId) ?? 'Unknown';\n }\n\n // Step 5: Respond (read-only, no audit log)\n done();\n return formatToolResponse({\n data: ownershipResult,\n message: ownershipResult.summary,\n });\n }\n\n case 'vesting': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Verify grant belongs to company\n const vestingGrant = await getEquityGrantById(ctx.supabase, input.grantId);\n if (!vestingGrant) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n if (vestingGrant.companyId !== input.companyId) {\n throw ffError.notFound('equity grant', input.grantId);\n }\n\n // Get vesting schedule\n const vestingSchedule = await getVestingByGrantId(ctx.supabase, input.grantId);\n if (!vestingSchedule) {\n done();\n return formatToolResponse({\n data: { grantId: input.grantId, hasVesting: false },\n message: 'This grant does not have a vesting schedule.',\n });\n }\n\n // Step 4: Execute\n const asOfDate = input.asOfDate ?? new Date().toISOString().split('T')[0]!;\n const vestingResult = computeVesting({\n vestingSchedule,\n grantShares: vestingSchedule.totalShares,\n asOfDate,\n });\n\n // Step 5: Respond (read-only, no audit log)\n done();\n return formatToolResponse({\n data: vestingResult,\n message: vestingResult.description,\n });\n }\n\n case 'waterfall': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const allClasses = await listAllShareClasses(ctx.supabase, input.companyId);\n if (allClasses.length === 0) {\n done();\n return formatToolResponse({\n data: { classes: [], exitAmount: input.exitAmount },\n message: 'No share classes found. Run /ff-cap-table to get started.',\n });\n }\n\n // Get all active grants (filter by instrument type based on includeOptions)\n const waterfallGrants = await listGrantsByCompany(ctx.supabase, input.companyId, { status: 'active' });\n\n // Instrument types always included in waterfall\n const WATERFALL_INCLUDE = new Set([\n 'common_stock',\n 'preferred_stock',\n 'membership_interest',\n 'profits_interest',\n ]);\n const OPTION_INSTRUMENT_TYPES = new Set(['option_iso', 'option_nso']);\n\n const filteredGrants = waterfallGrants.filter((g) => {\n if (WATERFALL_INCLUDE.has(g.instrumentType)) return true;\n if (input.includeOptions && OPTION_INSTRUMENT_TYPES.has(g.instrumentType)) return true;\n return false;\n });\n\n // Only grants with shares\n const grantsWithShares = filteredGrants.filter((g) => g.shares != null && g.shares > 0);\n\n // Build waterfall class inputs by grouping grants per share class\n const classGrantMap = new Map<string, typeof grantsWithShares>();\n const unclassifiedGrants: typeof grantsWithShares = [];\n\n for (const g of grantsWithShares) {\n if (g.shareClassId) {\n const existing = classGrantMap.get(g.shareClassId);\n if (existing) {\n existing.push(g);\n } else {\n classGrantMap.set(g.shareClassId, [g]);\n }\n } else {\n unclassifiedGrants.push(g);\n }\n }\n\n const waterfallClassInputs: WaterfallClassInput[] = [];\n\n for (const sc of allClasses) {\n const classGrants = classGrantMap.get(sc.id) ?? [];\n const totalShares = classGrants.reduce((s, g) => s + g.shares!, 0);\n const totalInvestment = classGrants.reduce(\n (s, g) => s + (g.shares! * (g.pricePerShare ?? 0)),\n 0,\n );\n\n // Skip classes with no shares granted\n if (totalShares === 0) continue;\n\n waterfallClassInputs.push({\n classId: sc.id,\n className: sc.name,\n classType: sc.classType as 'common' | 'preferred',\n seniority: sc.seniority,\n liquidationPreference: sc.liquidationPreference ?? 0,\n participationCap: sc.participationCap ?? null,\n totalShares,\n totalInvestment,\n stakeholders: classGrants.map((g) => ({\n stakeholderId: g.stakeholderId,\n shares: g.shares!,\n investment: g.shares! * (g.pricePerShare ?? 0),\n })),\n });\n }\n\n const waterfallResult = computeWaterfall({\n exitAmount: input.exitAmount,\n classes: waterfallClassInputs,\n });\n\n // Enrich stakeholder results with names\n const waterfallStakeholders = await listStakeholdersByCompany(ctx.supabase, input.companyId);\n const waterfallNameMap = new Map(waterfallStakeholders.map((s) => [s.id, s.name]));\n for (const entry of waterfallResult.stakeholders) {\n (entry as unknown as Record<string, unknown>).stakeholderName = waterfallNameMap.get(entry.stakeholderId) ?? 'Unknown';\n }\n\n // Step 5: Respond (read-only, no audit log)\n done();\n return formatToolResponse({\n data: waterfallResult,\n message: waterfallResult.summary,\n });\n }\n\n case 'model_round': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const allGrants = await listGrantsByCompany(ctx.supabase, input.companyId, { status: 'active' });\n\n // Empty state\n if (allGrants.length === 0) {\n done();\n return formatToolResponse({\n data: { companyId: input.companyId },\n message: 'No equity grants recorded. Run /ff-cap-table to get started.',\n });\n }\n\n // Split grants into equity holders vs convertibles\n const EQUITY_TYPES = new Set([\n 'common_stock',\n 'preferred_stock',\n 'membership_interest',\n 'profits_interest',\n ]);\n const equityGrants = allGrants.filter((g) => EQUITY_TYPES.has(g.instrumentType) && g.shares != null && g.shares > 0);\n const convertibleGrants = allGrants.filter((g) => {\n if (g.instrumentType === 'safe' && !input.convertSafes) return false;\n if (g.instrumentType === 'convertible_note' && !input.convertNotes) return false;\n return CONVERTIBLE_TYPES.has(g.instrumentType) && g.amount != null && g.amount > 0;\n });\n\n // Load share classes for names\n const modelClasses = await listAllShareClasses(ctx.supabase, input.companyId);\n const classNameMap = new Map(modelClasses.map((c) => [c.id, c.name]));\n\n // Compute option pool shares from grants linked to equity plans\n const optionPoolShares = allGrants\n .filter((g) => g.equityPlanId != null && g.shares != null)\n .reduce((s, g) => s + g.shares!, 0);\n\n const modelingResult = computeRoundModeling({\n investmentAmount: input.investmentAmount,\n preMoneyValuation: input.preMoneyValuation,\n existingShares: equityGrants.map((g) => ({\n stakeholderId: g.stakeholderId,\n shares: g.shares!,\n classId: g.shareClassId,\n className: g.shareClassId ? (classNameMap.get(g.shareClassId) ?? null) : null,\n })),\n convertibles: convertibleGrants.map((g) => ({\n grantId: g.id,\n stakeholderId: g.stakeholderId,\n instrumentType: g.instrumentType as 'safe' | 'convertible_note',\n amount: g.amount!,\n valuationCap: g.valuationCap ?? undefined,\n discountRate: g.discountRate ?? undefined,\n interestRate: g.interestRate ?? undefined,\n grantDate: g.grantDate,\n })),\n currentOptionPoolShares: optionPoolShares,\n newOptionPoolPercent: input.newOptionPoolPercent,\n roundName: input.roundName,\n newShareClassName: input.newShareClassName,\n });\n\n // Enrich with stakeholder names\n const modelStakeholders = await listStakeholdersByCompany(ctx.supabase, input.companyId);\n const modelNameMap = new Map(modelStakeholders.map((s) => [s.id, s.name]));\n for (const entry of modelingResult.ownershipComparison) {\n (entry as unknown as Record<string, unknown>).stakeholderName =\n entry.stakeholderId === '__new_investor__' ? `New Investor (${modelingResult.roundName})`\n : entry.stakeholderId === '__option_pool__' ? 'Option Pool'\n : modelNameMap.get(entry.stakeholderId) ?? 'Unknown';\n }\n\n // Step 5: Respond (read-only, no audit log)\n done();\n return formatToolResponse({\n data: modelingResult,\n message: modelingResult.summary,\n });\n }\n\n case 'import': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Validate: at least one of documentId or extractedText\n if (input.documentId == null && input.extractedText == null) {\n throw ffError.validationFailed([{\n path: 'documentId',\n message: 'Either documentId or extractedText is required.',\n }]);\n }\n\n // Step 4: Execute\n let text: string;\n\n if (input.extractedText) {\n text = input.extractedText;\n } else {\n // Fetch from vault\n const doc = await getDocumentById(ctx.supabase, input.documentId!);\n if (!doc) {\n throw ffError.notFound('document', input.documentId!);\n }\n if (doc.companyId !== input.companyId) {\n throw ffError.notFound('document', input.documentId!);\n }\n if (!doc.extractedText) {\n done();\n return formatToolResponse({\n data: { documentId: input.documentId },\n message: 'This document has no extracted text. Run ff_scan on the document first, then use the extractedText parameter with the scan output.',\n nextAction: {\n tool: 'ff_scan',\n action: 'file',\n params: {},\n },\n });\n }\n text = doc.extractedText;\n }\n\n const extraction = extractEquityTerms(text, input.instrumentHint);\n\n // Step 5: Respond (read-only — user confirms via add_grant)\n done();\n return formatToolResponse({\n data: extraction.proposedGrant,\n message: `Extracted ${extraction.proposedGrant.extractedFields.length} field(s) from document (${extraction.proposedGrant.confidence} confidence). Review the proposed values and confirm via add_grant.`,\n prompt: extraction.hostAiPrompt,\n });\n }\n\n case 'export': {\n // Step 3: Authorize\n const company = await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute\n const exportGrants = await listGrantsByCompany(ctx.supabase, input.companyId);\n\n // Empty state\n if (exportGrants.length === 0) {\n done();\n return formatToolResponse({\n data: { companyId: input.companyId },\n message: 'No equity data to export. Run /ff-cap-table to get started.',\n });\n }\n\n const exportClasses = await listAllShareClasses(ctx.supabase, input.companyId);\n const exportStakeholders = await listStakeholdersByCompany(ctx.supabase, input.companyId);\n\n // Compute vesting for each grant that has a schedule\n const vestingData: Array<{ grantId: string; vesting: ReturnType<typeof computeVesting> | null }> = [];\n const asOfDate = new Date().toISOString().split('T')[0]!;\n\n for (const g of exportGrants) {\n const schedule = await getVestingByGrantId(ctx.supabase, g.id);\n if (schedule) {\n const result = computeVesting({\n vestingSchedule: schedule,\n grantShares: schedule.totalShares,\n asOfDate,\n });\n vestingData.push({ grantId: g.id, vesting: result });\n } else {\n vestingData.push({ grantId: g.id, vesting: null });\n }\n }\n\n const exportInput = {\n companyName: company.name,\n asOfDate,\n shareClasses: exportClasses,\n stakeholders: exportStakeholders,\n grants: exportGrants,\n vestingData,\n };\n\n // Format based on requested format\n switch (input.format) {\n case 'csv': {\n const csv = exportAsCsv(exportInput);\n done();\n return formatToolResponse({\n data: { format: 'csv', content: csv },\n message: `Exported cap table as CSV (${exportGrants.length} grant(s)).`,\n });\n }\n case 'markdown': {\n const md = exportAsMarkdown(exportInput);\n done();\n return formatToolResponse({\n data: { format: 'markdown', content: md },\n message: `Exported cap table as Markdown (${exportGrants.length} grant(s)).`,\n });\n }\n case 'json': {\n const json = exportAsJson(exportInput);\n done();\n return formatToolResponse({\n data: { format: 'json', content: json },\n message: `Exported cap table as JSON (${exportGrants.length} grant(s)).`,\n });\n }\n case 'xlsx': {\n const buffer = await exportAsXlsx(exportInput);\n // Write to outputPath or temp file\n const { writeFile } = await import('node:fs/promises');\n const { tmpdir } = await import('node:os');\n const { join } = await import('node:path');\n const outputPath = input.outputPath ?? join(tmpdir(), `forcefield-cap-table-${Date.now()}.xlsx`);\n await writeFile(outputPath, buffer);\n done();\n return formatToolResponse({\n data: { format: 'xlsx', filePath: outputPath, worksheets: ['Summary', 'Grants Detail', 'Vesting Schedule'] },\n message: `Exported cap table as XLSX to ${outputPath} (3 worksheets, ${exportGrants.length} grant(s)).`,\n });\n }\n }\n }\n }\n\n // Exhaustive check — all actions handled above\n throw ffError.notImplemented(`${TOOL_NAME}/${(rawInput.action as string) ?? 'unknown'}`);\n } catch (error) {\n done(true);\n\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n return formatErrorResponse(ffErr);\n }\n\n // Handle unique constraint from DB layer\n if (error instanceof Error && 'code' in error && (error as { code: string }).code === 'CONFLICT') {\n return formatErrorResponse(ffError.conflict('equity resource', error.message));\n }\n\n // Audit log for errors on company-scoped actions\n if (rawInput.companyId) {\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: (rawInput.action as string) ?? 'unknown',\n companyId: rawInput.companyId as string,\n result: 'error',\n errorCode: error instanceof Error && 'code' in error ? (error as { code: string }).code : 'UNKNOWN',\n });\n }\n\n return formatErrorResponse(error);\n }\n}\n","/**\n * tools/ff-integrate.ts — ff_integrate tool handler.\n *\n * Follows 5-step invariant: Validate → Gate → Authorize → Execute → Respond.\n * Active: set_notifications, scan_drive, scan_email, scan_notion, push_calendar.\n */\n\nimport { writeFile, unlink, mkdtemp } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport type { SessionContext } from '../auth/session.js';\nimport { FfIntegrateSchema } from '@forcefield/shared/schemas';\nimport { assertActionAllowed, TierRestrictionError } from '../auth/gating.js';\nimport { ffError } from '../errors/catalog.js';\nimport { formatToolResponse, formatErrorResponse } from '../utils/response.js';\nimport { writeAuditLog } from '../utils/audit.js';\nimport { logger } from '../utils/logger.js';\nimport { upsertPreferences } from '../db/notification-preferences.js';\nimport { validateCompanyOwnership } from '../auth/ownership.js';\nimport { getConfig } from '../config.js';\nimport { getGoogleAccessToken, GOOGLE_CALENDAR_SCOPE } from '../domain/integration/google-auth.js';\nimport { listDriveFiles, downloadDriveFile } from '../domain/integration/google-drive.js';\nimport { searchEmails, downloadAttachment } from '../domain/integration/gmail.js';\nimport { createCalendarEvent, updateCalendarEvent } from '../domain/integration/google-calendar.js';\nimport { getNotionAccessToken } from '../domain/integration/notion-auth.js';\nimport { searchNotionPages, queryNotionDatabase, getNotionPageContent } from '../domain/integration/notion.js';\nimport { extractText } from '../domain/scanning/extract-text.js';\nimport { classifyDocument } from '../domain/classification/classifier.js';\nimport { listDeadlines, updateCalendarEventId } from '../db/deadlines.js';\nimport type { DeadlineRecord } from '../db/deadlines.js';\n\nconst TOOL_NAME = 'ff_integrate';\n\n/** Map common MIME types to file extensions for temp files. */\nconst MIME_TO_EXT: Record<string, string> = {\n 'application/pdf': '.pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',\n 'text/csv': '.csv',\n 'text/plain': '.txt',\n 'text/html': '.html',\n 'text/markdown': '.md',\n 'application/rtf': '.rtf',\n};\n\nexport async function handleFfIntegrate(\n ctx: SessionContext,\n rawInput: Record<string, unknown>,\n _extra?: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: true }> {\n const done = logger.toolInvocation(TOOL_NAME, (rawInput.action as string) ?? 'unknown');\n\n try {\n // Step 1: Validate\n const parseResult = FfIntegrateSchema.safeParse(rawInput);\n if (!parseResult.success) {\n const issues = parseResult.error.issues.map((i) => ({\n path: i.path.map(String).join('.'),\n message: i.message,\n }));\n throw ffError.validationFailed(issues);\n }\n const input = parseResult.data;\n\n // Step 2: Gate\n assertActionAllowed(ctx.gating, TOOL_NAME, input.action, ctx.tier);\n\n // Steps 3-5: Authorize → Execute → Respond (per action)\n switch (input.action) {\n case 'set_notifications': {\n // Step 3: Authorize — user-level, no company ownership needed\n\n // Step 4: Execute\n const prefs = await upsertPreferences(ctx.supabase, {\n userId: ctx.userId,\n enabled: input.enabled,\n frequency: input.frequency,\n mode: input.mode,\n emailOverride: input.emailOverride,\n disabledCategories: input.disabledCategories,\n webhookUrl: input.webhookUrl,\n });\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'set_notifications',\n result: 'success',\n input: {\n enabled: input.enabled,\n frequency: input.frequency,\n mode: input.mode,\n disabledCategories: input.disabledCategories,\n webhookUrl: input.webhookUrl ? '[REDACTED]' : undefined,\n },\n });\n\n // Step 5: Respond\n done();\n const messageParts = [\n input.enabled\n ? `Email notifications enabled (${input.frequency}, ${input.mode} mode).`\n : 'Email notifications disabled.',\n ];\n if (input.disabledCategories.length > 0) {\n messageParts.push(`${input.disabledCategories.length} category(s) disabled.`);\n }\n if (input.webhookUrl) {\n messageParts.push('Webhook delivery enabled.');\n }\n\n return formatToolResponse({\n data: prefs,\n message: messageParts.join(' '),\n nextAction: input.enabled\n ? { tool: 'ff_compliance', action: 'score', params: {} }\n : undefined,\n });\n }\n\n case 'scan_drive': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute — get Google access token\n const config = getConfig();\n if (!config.GOOGLE_CLIENT_ID) {\n done();\n return formatToolResponse({\n data: null,\n message:\n 'Google integration is not configured. Set the GOOGLE_CLIENT_ID environment variable to enable Drive scanning.',\n });\n }\n\n const authResult = await getGoogleAccessToken(ctx.supabase, ctx.userId, {\n googleClientId: config.GOOGLE_CLIENT_ID,\n supabaseUrl: config.SUPABASE_URL,\n googleRedirectUri: config.GOOGLE_REDIRECT_URI,\n });\n\n if ('requiresAuth' in authResult) {\n done();\n return formatToolResponse({\n data: {\n requiresAuth: true,\n provider: 'google',\n authUrl: authResult.authUrl,\n },\n message:\n 'Google account authorization required. Open this URL in your browser to connect your Google account, then retry the scan.',\n });\n }\n\n // List files from Drive\n const files = await listDriveFiles(authResult.accessToken, {\n folderId: input.folderId,\n maxResults: input.maxResults,\n query: input.query,\n });\n\n // Download + extract + classify each file\n const results = await processFilesFromDrive(authResult.accessToken, files);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'scan_drive',\n companyId: input.companyId,\n result: 'success',\n input: { folderId: input.folderId, maxResults: input.maxResults, filesFound: files.length },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: { files: results, totalFound: files.length },\n message: `Found ${files.length} document(s) in Google Drive. ${results.length} scanned and classified.`,\n nextAction: results.length > 0\n ? { tool: 'ff_vault', action: 'list', params: { companyId: input.companyId } }\n : undefined,\n prompt: results.length > 0\n ? 'Review your existing vault documents, then upload selected files with ff_vault(action: upload).'\n : undefined,\n });\n }\n\n case 'scan_email': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute — get Google access token\n const config = getConfig();\n if (!config.GOOGLE_CLIENT_ID) {\n done();\n return formatToolResponse({\n data: null,\n message:\n 'Google integration is not configured. Set the GOOGLE_CLIENT_ID environment variable to enable email scanning.',\n });\n }\n\n const authResult = await getGoogleAccessToken(ctx.supabase, ctx.userId, {\n googleClientId: config.GOOGLE_CLIENT_ID,\n supabaseUrl: config.SUPABASE_URL,\n googleRedirectUri: config.GOOGLE_REDIRECT_URI,\n });\n\n if ('requiresAuth' in authResult) {\n done();\n return formatToolResponse({\n data: {\n requiresAuth: true,\n provider: 'google',\n authUrl: authResult.authUrl,\n },\n message:\n 'Google account authorization required. Open this URL in your browser to connect your Google account, then retry the scan.',\n });\n }\n\n // Search emails\n const messages = await searchEmails(authResult.accessToken, {\n query: input.query,\n maxResults: input.maxResults,\n });\n\n // Download + extract + classify attachments\n const results = await processEmailAttachments(authResult.accessToken, messages);\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'scan_email',\n companyId: input.companyId,\n result: 'success',\n input: { query: input.query, maxResults: input.maxResults, messagesFound: messages.length },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: { messages: results, totalFound: messages.length },\n message: `Found ${messages.length} email(s) with attachments. ${results.reduce((n, m) => n + m.attachments.length, 0)} attachment(s) scanned.`,\n nextAction: results.length > 0\n ? { tool: 'ff_vault', action: 'list', params: { companyId: input.companyId } }\n : undefined,\n prompt: results.length > 0\n ? 'Review your existing vault documents, then upload selected attachments with ff_vault(action: upload).'\n : undefined,\n });\n }\n\n case 'scan_notion': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute — get Notion access token\n const config = getConfig();\n if (!config.NOTION_CLIENT_ID) {\n done();\n return formatToolResponse({\n data: null,\n message:\n 'Notion integration is not configured. Set the NOTION_CLIENT_ID environment variable to enable Notion scanning.',\n });\n }\n\n const authResult = await getNotionAccessToken(ctx.supabase, ctx.userId, {\n notionClientId: config.NOTION_CLIENT_ID,\n supabaseUrl: config.SUPABASE_URL,\n notionRedirectUri: config.NOTION_REDIRECT_URI,\n });\n\n if ('requiresAuth' in authResult) {\n done();\n return formatToolResponse({\n data: {\n requiresAuth: true,\n provider: 'notion',\n authUrl: authResult.authUrl,\n },\n message:\n 'Notion authorization required. Open this URL in your browser to connect your Notion workspace, then retry the scan.',\n });\n }\n\n // Branch by access pattern\n const notionResults = await processNotionContent(\n authResult.accessToken,\n input,\n );\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'scan_notion',\n companyId: input.companyId,\n result: 'success',\n input: {\n pageId: input.pageId,\n databaseId: input.databaseId,\n query: input.query,\n maxResults: input.maxResults,\n pagesFound: notionResults.length,\n },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: { pages: notionResults, totalFound: notionResults.length },\n message: `Found ${notionResults.length} Notion page(s). ${notionResults.filter((p) => p.classification).length} classified.`,\n nextAction: notionResults.length > 0\n ? { tool: 'ff_vault', action: 'list', params: { companyId: input.companyId } }\n : undefined,\n prompt: notionResults.length > 0\n ? 'Review your existing vault documents, then upload selected exports with ff_vault(action: upload).'\n : undefined,\n });\n }\n\n case 'push_calendar': {\n // Step 3: Authorize\n await validateCompanyOwnership(ctx.supabase, input.companyId, ctx.userId);\n\n // Step 4: Execute — get Google access token with calendar scope\n const config = getConfig();\n if (!config.GOOGLE_CLIENT_ID) {\n done();\n return formatToolResponse({\n data: null,\n message:\n 'Google integration is not configured. Set the GOOGLE_CLIENT_ID environment variable to enable calendar push.',\n });\n }\n\n const authResult = await getGoogleAccessToken(\n ctx.supabase,\n ctx.userId,\n {\n googleClientId: config.GOOGLE_CLIENT_ID,\n supabaseUrl: config.SUPABASE_URL,\n googleRedirectUri: config.GOOGLE_REDIRECT_URI,\n },\n [GOOGLE_CALENDAR_SCOPE],\n );\n\n if ('requiresAuth' in authResult) {\n done();\n return formatToolResponse({\n data: {\n requiresAuth: true,\n provider: 'google',\n authUrl: authResult.authUrl,\n },\n message:\n 'Google Calendar authorization required. Open this URL in your browser to grant calendar access, then retry.',\n });\n }\n\n // Fetch deadlines to sync\n const deadlines = await getDeadlinesForCalendarSync(\n ctx.supabase,\n input.companyId,\n input.deadlineIds,\n );\n\n if (deadlines.length === 0) {\n done();\n return formatToolResponse({\n data: { synced: 0, created: 0, updated: 0 },\n message: 'No pending or overdue deadlines to sync to Google Calendar.',\n });\n }\n\n // Create or update calendar events\n let created = 0;\n let updated = 0;\n for (const deadline of deadlines) {\n const eventInput = {\n summary: `[Forcefield] ${deadline.filingName}`,\n description: `Category: ${deadline.category}\\nJurisdiction: ${deadline.jurisdiction}\\nStatus: ${deadline.status}`,\n date: deadline.dueDate,\n };\n\n try {\n if (deadline.calendarEventId) {\n await updateCalendarEvent(authResult.accessToken, deadline.calendarEventId, eventInput);\n await updateCalendarEventId(ctx.supabase, deadline.id, deadline.calendarEventId);\n updated++;\n } else {\n const eventId = await createCalendarEvent(authResult.accessToken, eventInput);\n await updateCalendarEventId(ctx.supabase, deadline.id, eventId);\n created++;\n }\n } catch (err) {\n logger.warn('Failed to sync deadline to Google Calendar', {\n deadlineId: deadline.id,\n error: err instanceof Error ? err.message : 'unknown',\n });\n }\n }\n\n writeAuditLog(ctx.supabase, ctx.userId, {\n tool: TOOL_NAME,\n action: 'push_calendar',\n companyId: input.companyId,\n result: 'success',\n input: { deadlineCount: deadlines.length, created, updated },\n });\n\n // Step 5: Respond\n done();\n return formatToolResponse({\n data: { synced: created + updated, created, updated },\n message: `Synced ${created + updated} deadline(s) to Google Calendar (${created} created, ${updated} updated).`,\n nextAction: { tool: 'ff_compliance', action: 'score', params: { companyId: input.companyId } },\n });\n }\n }\n\n // Exhaustive check — all actions handled above\n throw ffError.notImplemented(`${TOOL_NAME}/${(rawInput.action as string) ?? 'unknown'}`);\n } catch (error) {\n done(true);\n\n if (error instanceof TierRestrictionError) {\n const ffErr = ffError.tierRestricted(error.tool, error.action);\n return formatErrorResponse(ffErr);\n }\n\n return formatErrorResponse(error);\n }\n}\n\n// --- Helpers ---\n\ninterface DriveFileResult {\n fileName: string;\n mimeType: string;\n driveFileId: string;\n modifiedTime: string;\n classification: string;\n textPreview: string;\n}\n\nasync function processFilesFromDrive(\n accessToken: string,\n files: Array<{ id: string; name: string; mimeType: string; modifiedTime: string }>,\n): Promise<DriveFileResult[]> {\n const results: DriveFileResult[] = [];\n\n for (const file of files) {\n try {\n const { buffer, mimeType } = await downloadDriveFile(accessToken, file.id, file.mimeType);\n const ext = MIME_TO_EXT[mimeType] ?? '.bin';\n\n // Write buffer to temp file for extractText\n const tmpDir = await mkdtemp(join(tmpdir(), 'ff-drive-'));\n const tmpPath = join(tmpDir, `file${ext}`);\n await writeFile(tmpPath, buffer);\n\n try {\n const extraction = await extractText(tmpPath);\n const classification = classifyDocument(extraction.text, file.name);\n\n results.push({\n fileName: file.name,\n mimeType: file.mimeType,\n driveFileId: file.id,\n modifiedTime: file.modifiedTime,\n classification,\n textPreview: extraction.text.slice(0, 200),\n });\n } finally {\n // Clean up temp file\n await unlink(tmpPath).catch(() => {});\n }\n } catch (err) {\n logger.warn('Failed to process Drive file', {\n fileId: file.id,\n fileName: file.name,\n error: err instanceof Error ? err.message : 'unknown',\n });\n }\n }\n\n return results;\n}\n\ninterface EmailResult {\n messageId: string;\n subject: string;\n from: string;\n date: string;\n attachments: Array<{\n filename: string;\n mimeType: string;\n classification: string;\n textPreview: string;\n }>;\n}\n\nasync function processEmailAttachments(\n accessToken: string,\n messages: Array<{\n id: string;\n subject: string;\n from: string;\n date: string;\n attachments: Array<{ attachmentId: string; filename: string; mimeType: string }>;\n }>,\n): Promise<EmailResult[]> {\n const results: EmailResult[] = [];\n\n for (const msg of messages) {\n const attachmentResults: EmailResult['attachments'] = [];\n\n for (const att of msg.attachments) {\n try {\n const buffer = await downloadAttachment(accessToken, msg.id, att.attachmentId);\n const ext = MIME_TO_EXT[att.mimeType] ?? extFromFilename(att.filename);\n\n // Write buffer to temp file for extractText\n const tmpDir = await mkdtemp(join(tmpdir(), 'ff-email-'));\n const tmpPath = join(tmpDir, `file${ext}`);\n await writeFile(tmpPath, buffer);\n\n try {\n const extraction = await extractText(tmpPath);\n const classification = classifyDocument(extraction.text, att.filename);\n\n attachmentResults.push({\n filename: att.filename,\n mimeType: att.mimeType,\n classification,\n textPreview: extraction.text.slice(0, 200),\n });\n } finally {\n await unlink(tmpPath).catch(() => {});\n }\n } catch (err) {\n logger.warn('Failed to process email attachment', {\n messageId: msg.id,\n filename: att.filename,\n error: err instanceof Error ? err.message : 'unknown',\n });\n }\n }\n\n if (attachmentResults.length > 0) {\n results.push({\n messageId: msg.id,\n subject: msg.subject,\n from: msg.from,\n date: msg.date,\n attachments: attachmentResults,\n });\n }\n }\n\n return results;\n}\n\nfunction extFromFilename(filename: string): string {\n const dotIndex = filename.lastIndexOf('.');\n return dotIndex >= 0 ? filename.slice(dotIndex) : '.bin';\n}\n\n// --- Notion helpers ---\n\ninterface NotionPageResult {\n pageId: string;\n title: string;\n url: string;\n lastEditedTime?: string;\n classification: string | null;\n textPreview: string;\n}\n\nasync function processNotionContent(\n accessToken: string,\n input: { pageId?: string; databaseId?: string; query?: string; maxResults: number },\n): Promise<NotionPageResult[]> {\n if (input.pageId) {\n // Single page fetch\n try {\n const page = await getNotionPageContent(accessToken, input.pageId);\n const classification = page.content ? classifyDocument(page.content, page.title) : null;\n return [{\n pageId: input.pageId,\n title: page.title,\n url: page.url,\n classification,\n textPreview: page.content.slice(0, 200),\n }];\n } catch (err) {\n logger.warn('Failed to fetch Notion page', {\n pageId: input.pageId,\n error: err instanceof Error ? err.message : 'unknown',\n });\n return [];\n }\n }\n\n // Get page list (database query or workspace search)\n let pages: Array<{ id: string; title: string; url: string; lastEditedTime: string }>;\n\n if (input.databaseId) {\n pages = await queryNotionDatabase(accessToken, input.databaseId, {\n maxResults: input.maxResults,\n });\n } else {\n pages = await searchNotionPages(accessToken, {\n query: input.query,\n maxResults: input.maxResults,\n });\n }\n\n // Fetch content + classify each page\n const results: NotionPageResult[] = [];\n for (const page of pages) {\n try {\n const content = await getNotionPageContent(accessToken, page.id);\n const classification = content.content ? classifyDocument(content.content, page.title) : null;\n results.push({\n pageId: page.id,\n title: page.title,\n url: page.url,\n lastEditedTime: page.lastEditedTime,\n classification,\n textPreview: content.content.slice(0, 200),\n });\n } catch (err) {\n logger.warn('Failed to process Notion page', {\n pageId: page.id,\n title: page.title,\n error: err instanceof Error ? err.message : 'unknown',\n });\n }\n }\n\n return results;\n}\n\n// --- Calendar helpers ---\n\ninterface CalendarDeadline {\n id: string;\n filingName: string;\n jurisdiction: string;\n category: string;\n dueDate: string;\n status: string;\n calendarEventId: string | null;\n}\n\n/**\n * Fetch deadlines for calendar sync. If specific IDs given, fetch those;\n * otherwise fetch all pending + overdue deadlines for the company.\n */\nasync function getDeadlinesForCalendarSync(\n supabase: Parameters<typeof listDeadlines>[0],\n companyId: string,\n deadlineIds?: string[],\n): Promise<CalendarDeadline[]> {\n if (deadlineIds && deadlineIds.length > 0) {\n // Fetch specific deadlines by listing all and filtering\n // (Supabase JS doesn't support .in() well with typed client for all cases)\n const { items } = await listDeadlines(supabase, companyId, {}, 1000, 0);\n const idSet = new Set(deadlineIds);\n return items.filter((d) => idSet.has(d.id)).map(mapToCalendarDeadline);\n }\n\n // Fetch pending deadlines\n const pending = await listDeadlines(supabase, companyId, { status: 'pending' }, 1000, 0);\n // Fetch overdue deadlines\n const overdue = await listDeadlines(supabase, companyId, { status: 'overdue' }, 1000, 0);\n\n return [...pending.items, ...overdue.items].map(mapToCalendarDeadline);\n}\n\nfunction mapToCalendarDeadline(d: DeadlineRecord): CalendarDeadline {\n return {\n id: d.id,\n filingName: d.filingName,\n jurisdiction: d.jurisdiction,\n category: d.category,\n dueDate: d.dueDate,\n status: d.status,\n calendarEventId: d.calendarEventId,\n };\n}\n","/**\n * db/notification-preferences.ts — Notification preferences CRUD.\n *\n * Follows deadlines.ts pattern: camelCase mapped from snake_case,\n * supabase client passed as parameter, null for not-found.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface NotificationPreferencesRecord {\n id: string;\n userId: string;\n enabled: boolean;\n frequency: string;\n mode: string;\n emailOverride: string | null;\n disabledCategories: string[];\n webhookUrl: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface NotificationPreferencesUpsertInput {\n userId: string;\n enabled: boolean;\n frequency: string;\n mode: string;\n emailOverride?: string | null;\n disabledCategories?: string[];\n webhookUrl?: string | null;\n}\n\n// --- Mapper ---\n\nfunction mapRow(row: Record<string, unknown>): NotificationPreferencesRecord {\n return {\n id: row.id as string,\n userId: row.user_id as string,\n enabled: row.enabled as boolean,\n frequency: row.frequency as string,\n mode: row.mode as string,\n emailOverride: (row.email_override as string) ?? null,\n disabledCategories: (row.disabled_categories as string[]) ?? [],\n webhookUrl: (row.webhook_url as string) ?? null,\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n}\n\n// --- CRUD ---\n\n/**\n * Get notification preferences for a user.\n * Returns null if no preferences row exists.\n */\nexport async function getPreferencesByUserId(\n supabase: TypedSupabaseClient,\n userId: string,\n): Promise<NotificationPreferencesRecord | null> {\n const { data, error } = await supabase\n .from('notification_preferences')\n .select('*')\n .eq('user_id', userId)\n .maybeSingle();\n\n if (error) throw error;\n if (!data) return null;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n\n/**\n * Create or update notification preferences for a user.\n * Uses upsert on user_id conflict.\n */\nexport async function upsertPreferences(\n supabase: TypedSupabaseClient,\n input: NotificationPreferencesUpsertInput,\n): Promise<NotificationPreferencesRecord> {\n const { data, error } = await supabase\n .from('notification_preferences')\n .upsert(\n {\n user_id: input.userId,\n enabled: input.enabled,\n frequency: input.frequency,\n mode: input.mode,\n email_override: input.emailOverride ?? null,\n disabled_categories: input.disabledCategories ?? [],\n webhook_url: input.webhookUrl ?? null,\n },\n { onConflict: 'user_id' },\n )\n .select()\n .single();\n\n if (error) throw error;\n\n return mapRow(data as unknown as Record<string, unknown>);\n}\n","/**\n * db/integration-credentials.ts — Integration token operations via Supabase RPC.\n *\n * Follows the sensitive.ts RPC pattern: all encryption/decryption happens in\n * PostgreSQL (SECURITY DEFINER functions). The JS client never sees the key.\n */\n\nimport type { TypedSupabaseClient } from './client.js';\n\n// --- Types ---\n\nexport interface IntegrationTokens {\n accessToken: string;\n refreshToken: string | null;\n expiresAt: string;\n}\n\n// --- Untyped RPC helper ---\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype UntypedRpc = (fn: string, params: Record<string, unknown>) => PromiseLike<{ data: any; error: any }>;\n\nfunction rpc(supabase: TypedSupabaseClient): UntypedRpc {\n return (supabase as unknown as { rpc: UntypedRpc }).rpc.bind(supabase);\n}\n\n// --- Operations ---\n\n/**\n * Retrieve decrypted integration tokens for a user + provider.\n * Returns null if no credentials stored.\n */\nexport async function getIntegrationTokens(\n supabase: TypedSupabaseClient,\n userId: string,\n provider: string,\n): Promise<{ tokens: IntegrationTokens; scopes: string[]; expiresAt: string | null } | null> {\n const { data, error } = await rpc(supabase)('get_integration_tokens', {\n p_user_id: userId,\n p_provider: provider,\n });\n\n if (error) throw error;\n\n const rows = (data ?? []) as Array<{ tokens_json: string; scopes: string[]; expires_at: string | null }>;\n if (rows.length === 0) return null;\n\n const row = rows[0]!;\n const tokens: IntegrationTokens = JSON.parse(row.tokens_json);\n\n return {\n tokens,\n scopes: row.scopes,\n expiresAt: row.expires_at,\n };\n}\n\n/**\n * Store encrypted integration tokens via RPC (upsert).\n */\nexport async function storeIntegrationTokens(\n supabase: TypedSupabaseClient,\n userId: string,\n provider: string,\n tokens: IntegrationTokens,\n scopes: string[],\n): Promise<void> {\n const { error } = await rpc(supabase)('store_integration_tokens', {\n p_user_id: userId,\n p_provider: provider,\n p_tokens_json: JSON.stringify(tokens),\n p_scopes: scopes,\n p_expires_at: tokens.expiresAt,\n });\n\n if (error) throw error;\n}\n\n/**\n * Create an OAuth state token for CSRF protection.\n * Returns a 32-byte hex state string with 10-minute expiry.\n */\nexport async function createOAuthState(\n supabase: TypedSupabaseClient,\n userId: string,\n provider: string,\n scopes: string[],\n): Promise<string> {\n const { data, error } = await rpc(supabase)('create_oauth_state', {\n p_user_id: userId,\n p_provider: provider,\n p_scopes: scopes,\n });\n\n if (error) throw error;\n\n return data as string;\n}\n","/**\n * domain/integration/google-auth.ts — Google OAuth URL building + token retrieval.\n *\n * Handles the auth dance:\n * 1. No credentials → create OAuth state → return authUrl for user\n * 2. Expired credentials → call refresh Edge Function → return fresh token\n * 3. Valid credentials → return token\n */\n\nimport type { TypedSupabaseClient } from '../../db/client.js';\nimport { getIntegrationTokens, createOAuthState } from '../../db/integration-credentials.js';\nimport { logger } from '../../utils/logger.js';\n\n/** Google OAuth2 scopes needed for Drive + Gmail scanning. */\nexport const GOOGLE_SCOPES = [\n 'https://www.googleapis.com/auth/drive.readonly',\n 'https://www.googleapis.com/auth/gmail.readonly',\n];\n\n/** Google Calendar scope for creating/updating events. */\nexport const GOOGLE_CALENDAR_SCOPE = 'https://www.googleapis.com/auth/calendar';\n\n/** Buffer before token expiry to trigger refresh (2 minutes). */\nconst EXPIRY_BUFFER_MS = 2 * 60 * 1000;\n\nexport type GoogleAccessTokenResult =\n | { accessToken: string }\n | { requiresAuth: true; authUrl: string };\n\n/**\n * Build a Google OAuth2 authorization URL.\n */\nexport function buildGoogleAuthUrl(clientId: string, state: string, scopes: string[]): string {\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: '', // Set by caller or Edge Function config\n response_type: 'code',\n scope: scopes.join(' '),\n state,\n access_type: 'offline',\n prompt: 'consent',\n });\n\n return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;\n}\n\n/**\n * Get a valid Google access token for the user.\n *\n * Returns either { accessToken } or { requiresAuth, authUrl }.\n * The authUrl should be presented to the user to initiate the OAuth flow.\n *\n * If `requiredScopes` is provided and the stored credentials don't cover all\n * of them, returns requiresAuth with the union of existing + required scopes\n * so the user can do incremental re-authorization.\n */\nexport async function getGoogleAccessToken(\n supabase: TypedSupabaseClient,\n userId: string,\n config: { googleClientId: string; supabaseUrl: string; googleRedirectUri?: string },\n requiredScopes?: string[],\n): Promise<GoogleAccessTokenResult> {\n // 1. Check existing tokens\n const existing = await getIntegrationTokens(supabase, userId, 'google');\n\n if (!existing) {\n // No credentials → need auth\n const scopes = requiredScopes\n ? [...new Set([...GOOGLE_SCOPES, ...requiredScopes])]\n : GOOGLE_SCOPES;\n return buildAuthResponse(supabase, userId, config, scopes);\n }\n\n // 1b. Check scope coverage\n if (requiredScopes) {\n const storedScopes = new Set(existing.scopes);\n const missing = requiredScopes.filter((s) => !storedScopes.has(s));\n if (missing.length > 0) {\n // Need incremental re-auth with union of scopes\n const unionScopes = [...new Set([...existing.scopes, ...requiredScopes])];\n return buildAuthResponse(supabase, userId, config, unionScopes);\n }\n }\n\n const { tokens } = existing;\n\n // 2. Check if token is still valid (with buffer)\n const expiresAt = new Date(tokens.expiresAt).getTime();\n if (Date.now() + EXPIRY_BUFFER_MS < expiresAt) {\n return { accessToken: tokens.accessToken };\n }\n\n // 3. Token expired → try refresh\n if (!tokens.refreshToken) {\n // No refresh token → need full re-auth\n return buildAuthResponse(supabase, userId, config);\n }\n\n try {\n const refreshResult = await callRefreshEdgeFunction(\n config.supabaseUrl,\n userId,\n );\n return { accessToken: refreshResult.accessToken };\n } catch (err) {\n // Refresh failed → need re-auth\n logger.warn('Google token refresh failed, requiring re-authorization', {\n error: err instanceof Error ? err.message : 'unknown',\n });\n return buildAuthResponse(supabase, userId, config);\n }\n}\n\n/**\n * Build a requiresAuth response with OAuth URL.\n * Accepts optional scopes override for incremental re-authorization.\n */\nasync function buildAuthResponse(\n supabase: TypedSupabaseClient,\n userId: string,\n config: { googleClientId: string; supabaseUrl: string; googleRedirectUri?: string },\n scopes: string[] = GOOGLE_SCOPES,\n): Promise<{ requiresAuth: true; authUrl: string }> {\n const state = await createOAuthState(supabase, userId, 'google', scopes);\n\n const redirectUri =\n config.googleRedirectUri ?? `${config.supabaseUrl}/functions/v1/google-oauth-callback`;\n\n const params = new URLSearchParams({\n client_id: config.googleClientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: scopes.join(' '),\n state,\n access_type: 'offline',\n prompt: 'consent',\n });\n\n const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;\n\n return { requiresAuth: true, authUrl };\n}\n\n/**\n * Call the refresh-google-token Edge Function.\n */\nasync function callRefreshEdgeFunction(\n supabaseUrl: string,\n userId: string,\n): Promise<{ accessToken: string; expiresAt: string }> {\n const url = `${supabaseUrl}/functions/v1/refresh-google-token`;\n\n // Use service role key from env (available in MCP server context)\n const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;\n if (!serviceRoleKey) {\n throw new Error('SUPABASE_SERVICE_ROLE_KEY not available for token refresh');\n }\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${serviceRoleKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ user_id: userId, provider: 'google' }),\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Token refresh failed (${response.status}): ${body}`);\n }\n\n const data = (await response.json()) as { access_token: string; expires_at: string };\n return { accessToken: data.access_token, expiresAt: data.expires_at };\n}\n","/**\n * domain/integration/google-drive.ts — Google Drive v3 REST API client.\n *\n * Direct fetch() to REST endpoints — no googleapis npm dependency.\n * Only uses drive.readonly scope.\n */\n\nconst DRIVE_API = 'https://www.googleapis.com/drive/v3';\n\n/** MIME types we consider scannable documents. */\nconst DOCUMENT_MIME_TYPES = [\n 'application/pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // docx\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // xlsx\n 'text/csv',\n 'text/plain',\n 'text/html',\n 'text/markdown',\n 'application/rtf',\n // Google native types (require export)\n 'application/vnd.google-apps.document',\n 'application/vnd.google-apps.spreadsheet',\n];\n\nexport interface DriveFile {\n id: string;\n name: string;\n mimeType: string;\n modifiedTime: string;\n size?: string;\n}\n\n/**\n * List files from Google Drive, filtered to document types.\n */\nexport async function listDriveFiles(\n accessToken: string,\n options: { folderId?: string; maxResults?: number; query?: string } = {},\n): Promise<DriveFile[]> {\n const { folderId, maxResults = 20, query } = options;\n\n // Build query: document types only\n const mimeFilter = DOCUMENT_MIME_TYPES.map((m) => `mimeType='${m}'`).join(' or ');\n const queryParts = [`(${mimeFilter})`, 'trashed=false'];\n\n if (folderId) {\n queryParts.push(`'${folderId}' in parents`);\n }\n\n if (query) {\n queryParts.push(`fullText contains '${query}'`);\n }\n\n const params = new URLSearchParams({\n q: queryParts.join(' and '),\n fields: 'files(id,name,mimeType,modifiedTime,size)',\n pageSize: String(Math.min(maxResults, 100)),\n orderBy: 'modifiedTime desc',\n });\n\n const response = await fetch(`${DRIVE_API}/files?${params.toString()}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Drive API error (${response.status}): ${body}`);\n }\n\n const data = (await response.json()) as { files: DriveFile[] };\n return data.files ?? [];\n}\n\n/** Google Docs/Sheets types that need export instead of direct download. */\nconst GOOGLE_NATIVE_TYPES: Record<string, string> = {\n 'application/vnd.google-apps.document': 'application/pdf',\n 'application/vnd.google-apps.spreadsheet': 'text/csv',\n};\n\n/**\n * Download a file from Google Drive.\n * Google Docs/Sheets are exported; binary files are downloaded directly.\n */\nexport async function downloadDriveFile(\n accessToken: string,\n fileId: string,\n mimeType: string,\n): Promise<{ buffer: Buffer; mimeType: string }> {\n const exportMime = GOOGLE_NATIVE_TYPES[mimeType];\n\n let response: Response;\n let resultMime: string;\n\n if (exportMime) {\n // Google native → export\n const params = new URLSearchParams({ mimeType: exportMime });\n response = await fetch(`${DRIVE_API}/files/${fileId}/export?${params.toString()}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n resultMime = exportMime;\n } else {\n // Binary file → direct download\n response = await fetch(`${DRIVE_API}/files/${fileId}?alt=media`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n resultMime = mimeType;\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Drive download error (${response.status}): ${body}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return { buffer: Buffer.from(arrayBuffer), mimeType: resultMime };\n}\n","/**\n * domain/integration/gmail.ts — Gmail v1 REST API client.\n *\n * Direct fetch() to REST endpoints — no googleapis npm dependency.\n * Only uses gmail.readonly scope.\n */\n\nconst GMAIL_API = 'https://gmail.googleapis.com/gmail/v1/users/me';\n\nexport interface EmailAttachment {\n attachmentId: string;\n filename: string;\n mimeType: string;\n size: number;\n}\n\nexport interface EmailMessage {\n id: string;\n subject: string;\n from: string;\n date: string;\n snippet: string;\n attachments: EmailAttachment[];\n}\n\n/**\n * Search Gmail for messages with document attachments.\n */\nexport async function searchEmails(\n accessToken: string,\n options: { query?: string; maxResults?: number } = {},\n): Promise<EmailMessage[]> {\n const {\n query = 'has:attachment (filename:pdf OR filename:docx OR filename:xlsx OR filename:csv)',\n maxResults = 10,\n } = options;\n\n // Step 1: List message IDs\n const listParams = new URLSearchParams({\n q: query,\n maxResults: String(Math.min(maxResults, 50)),\n });\n\n const listResp = await fetch(`${GMAIL_API}/messages?${listParams.toString()}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!listResp.ok) {\n const body = await listResp.text().catch(() => '');\n throw new Error(`Gmail list error (${listResp.status}): ${body}`);\n }\n\n const listData = (await listResp.json()) as {\n messages?: Array<{ id: string; threadId: string }>;\n resultSizeEstimate?: number;\n };\n\n if (!listData.messages || listData.messages.length === 0) {\n return [];\n }\n\n // Step 2: Fetch each message's metadata + attachment info\n const messages: EmailMessage[] = [];\n\n for (const msg of listData.messages) {\n const msgResp = await fetch(\n `${GMAIL_API}/messages/${msg.id}?format=metadata&metadataHeaders=Subject&metadataHeaders=From&metadataHeaders=Date`,\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n\n if (!msgResp.ok) continue;\n\n const msgData = (await msgResp.json()) as GmailMessageResponse;\n\n const headers = msgData.payload?.headers ?? [];\n const subject = getHeader(headers, 'Subject') ?? '(no subject)';\n const from = getHeader(headers, 'From') ?? '';\n const date = getHeader(headers, 'Date') ?? '';\n\n // Extract attachments from parts\n const attachments = extractAttachments(msgData.payload);\n\n messages.push({\n id: msg.id,\n subject,\n from,\n date,\n snippet: msgData.snippet ?? '',\n attachments,\n });\n }\n\n return messages;\n}\n\n/**\n * Download a specific attachment from a Gmail message.\n * Returns the raw binary content.\n */\nexport async function downloadAttachment(\n accessToken: string,\n messageId: string,\n attachmentId: string,\n): Promise<Buffer> {\n const response = await fetch(\n `${GMAIL_API}/messages/${messageId}/attachments/${attachmentId}`,\n { headers: { Authorization: `Bearer ${accessToken}` } },\n );\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Gmail attachment error (${response.status}): ${body}`);\n }\n\n const data = (await response.json()) as { data: string; size: number };\n\n // Gmail returns base64url-encoded data\n return Buffer.from(data.data, 'base64url');\n}\n\n// --- Internal types ---\n\ninterface GmailHeader {\n name: string;\n value: string;\n}\n\ninterface GmailPart {\n partId?: string;\n mimeType?: string;\n filename?: string;\n headers?: GmailHeader[];\n body?: { attachmentId?: string; size?: number; data?: string };\n parts?: GmailPart[];\n}\n\ninterface GmailMessageResponse {\n id: string;\n snippet?: string;\n payload?: GmailPart;\n}\n\n// --- Helpers ---\n\nfunction getHeader(headers: GmailHeader[], name: string): string | undefined {\n return headers.find((h) => h.name.toLowerCase() === name.toLowerCase())?.value;\n}\n\n/**\n * Recursively extract attachment metadata from message parts.\n */\nfunction extractAttachments(payload?: GmailPart): EmailAttachment[] {\n const attachments: EmailAttachment[] = [];\n\n if (!payload) return attachments;\n\n function walk(part: GmailPart): void {\n if (part.filename && part.filename.length > 0 && part.body?.attachmentId) {\n attachments.push({\n attachmentId: part.body.attachmentId,\n filename: part.filename,\n mimeType: part.mimeType ?? 'application/octet-stream',\n size: part.body.size ?? 0,\n });\n }\n\n if (part.parts) {\n for (const child of part.parts) {\n walk(child);\n }\n }\n }\n\n walk(payload);\n return attachments;\n}\n","/**\n * domain/integration/google-calendar.ts — Google Calendar v3 REST client.\n *\n * Creates, updates, and deletes all-day calendar events for compliance deadlines.\n * Uses direct fetch() to Calendar v3, same pattern as google-drive.ts / gmail.ts.\n */\n\nconst CALENDAR_API = 'https://www.googleapis.com/calendar/v3/calendars/primary/events';\n\n/** Reminder minutes: 30 days, 14 days, 7 days, 1 day before. */\nconst REMINDER_MINUTES = [43200, 20160, 10080, 1440];\n\nexport interface CalendarEventInput {\n summary: string;\n description: string;\n /** Due date in YYYY-MM-DD format. */\n date: string;\n}\n\n/**\n * Create an all-day calendar event. Returns the created event's ID.\n */\nexport async function createCalendarEvent(\n accessToken: string,\n event: CalendarEventInput,\n): Promise<string> {\n const body = buildEventBody(event);\n\n const response = await fetch(CALENDAR_API, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Google Calendar create failed (${response.status}): ${text}`);\n }\n\n const data = (await response.json()) as { id: string };\n return data.id;\n}\n\n/**\n * Update an existing calendar event by ID.\n */\nexport async function updateCalendarEvent(\n accessToken: string,\n eventId: string,\n event: CalendarEventInput,\n): Promise<void> {\n const body = buildEventBody(event);\n\n const response = await fetch(`${CALENDAR_API}/${encodeURIComponent(eventId)}`, {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Google Calendar update failed (${response.status}): ${text}`);\n }\n}\n\n/**\n * Delete a calendar event by ID.\n */\nexport async function deleteCalendarEvent(\n accessToken: string,\n eventId: string,\n): Promise<void> {\n const response = await fetch(`${CALENDAR_API}/${encodeURIComponent(eventId)}`, {\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n // 204 No Content or 410 Gone are both acceptable\n if (!response.ok && response.status !== 410) {\n const text = await response.text().catch(() => '');\n throw new Error(`Google Calendar delete failed (${response.status}): ${text}`);\n }\n}\n\n// --- Internal ---\n\nfunction buildEventBody(event: CalendarEventInput) {\n // All-day event: end date is the day after start (exclusive end)\n const endDate = nextDay(event.date);\n\n return {\n summary: event.summary,\n description: event.description,\n start: { date: event.date },\n end: { date: endDate },\n reminders: {\n useDefault: false,\n overrides: REMINDER_MINUTES.map((minutes) => ({\n method: 'popup' as const,\n minutes,\n })),\n },\n };\n}\n\n/** Return YYYY-MM-DD for the day after the given date. */\nfunction nextDay(dateStr: string): string {\n const d = new Date(dateStr + 'T00:00:00Z');\n d.setUTCDate(d.getUTCDate() + 1);\n return d.toISOString().split('T')[0]!;\n}\n","/**\n * domain/integration/notion-auth.ts — Notion OAuth URL building + token retrieval.\n *\n * Simpler than Google auth: Notion access tokens never expire, so there's\n * no refresh flow. If credentials exist, return them. If not, start OAuth.\n */\n\nimport type { TypedSupabaseClient } from '../../db/client.js';\nimport { getIntegrationTokens, createOAuthState } from '../../db/integration-credentials.js';\n\nexport type NotionAccessTokenResult =\n | { accessToken: string }\n | { requiresAuth: true; authUrl: string };\n\n/**\n * Get a valid Notion access token for the user.\n *\n * Returns either { accessToken } or { requiresAuth, authUrl }.\n * No expiry check needed — Notion tokens don't expire.\n */\nexport async function getNotionAccessToken(\n supabase: TypedSupabaseClient,\n userId: string,\n config: { notionClientId: string; supabaseUrl: string; notionRedirectUri?: string },\n): Promise<NotionAccessTokenResult> {\n const existing = await getIntegrationTokens(supabase, userId, 'notion');\n\n if (existing) {\n return { accessToken: existing.tokens.accessToken };\n }\n\n // No credentials → need auth\n const state = await createOAuthState(supabase, userId, 'notion', []);\n\n const redirectUri =\n config.notionRedirectUri ?? `${config.supabaseUrl}/functions/v1/notion-oauth-callback`;\n\n const params = new URLSearchParams({\n client_id: config.notionClientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n state,\n owner: 'user',\n });\n\n const authUrl = `https://api.notion.com/v1/oauth/authorize?${params.toString()}`;\n\n return { requiresAuth: true, authUrl };\n}\n","/**\n * domain/integration/notion.ts — Notion REST API client.\n *\n * Direct fetch() to REST endpoints — no @notionhq/client dependency.\n * Uses the internal integration token (OAuth access_token).\n */\n\nconst NOTION_API = 'https://api.notion.com/v1';\nconst NOTION_VERSION = '2022-06-28';\n\nfunction notionHeaders(accessToken: string): Record<string, string> {\n return {\n Authorization: `Bearer ${accessToken}`,\n 'Notion-Version': NOTION_VERSION,\n 'Content-Type': 'application/json',\n };\n}\n\nexport interface NotionPage {\n id: string;\n title: string;\n url: string;\n lastEditedTime: string;\n}\n\nexport interface NotionPageContent {\n title: string;\n content: string;\n url: string;\n}\n\n// --- Notion API response types ---\n\ninterface NotionRichText {\n plain_text: string;\n}\n\ninterface NotionProperty {\n type: string;\n title?: NotionRichText[];\n}\n\ninterface NotionApiPage {\n id: string;\n url: string;\n last_edited_time: string;\n properties: Record<string, NotionProperty>;\n}\n\ninterface NotionBlock {\n type: string;\n [key: string]: unknown;\n}\n\n// Block types we extract text from\nconst TEXT_BLOCK_TYPES = [\n 'paragraph',\n 'heading_1',\n 'heading_2',\n 'heading_3',\n 'bulleted_list_item',\n 'numbered_list_item',\n 'toggle',\n 'to_do',\n 'callout',\n 'quote',\n 'code',\n] as const;\n\n/**\n * Extract title from a Notion page's properties.\n */\nfunction extractTitle(page: NotionApiPage): string {\n for (const prop of Object.values(page.properties)) {\n if (prop.type === 'title' && prop.title) {\n return prop.title.map((t) => t.plain_text).join('');\n }\n }\n return 'Untitled';\n}\n\n/**\n * Search for pages in the Notion workspace.\n */\nexport async function searchNotionPages(\n accessToken: string,\n options: { query?: string; maxResults?: number } = {},\n): Promise<NotionPage[]> {\n const { query, maxResults = 10 } = options;\n\n const body: Record<string, unknown> = {\n filter: { property: 'object', value: 'page' },\n page_size: Math.min(maxResults, 100),\n };\n\n if (query) {\n body.query = query;\n }\n\n const response = await fetch(`${NOTION_API}/search`, {\n method: 'POST',\n headers: notionHeaders(accessToken),\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Notion API error (${response.status}): ${text}`);\n }\n\n const data = (await response.json()) as { results: NotionApiPage[] };\n\n return data.results.map((page) => ({\n id: page.id,\n title: extractTitle(page),\n url: page.url,\n lastEditedTime: page.last_edited_time,\n }));\n}\n\n/**\n * Query entries from a Notion database.\n */\nexport async function queryNotionDatabase(\n accessToken: string,\n databaseId: string,\n options: { maxResults?: number } = {},\n): Promise<NotionPage[]> {\n const { maxResults = 10 } = options;\n\n const response = await fetch(`${NOTION_API}/databases/${databaseId}/query`, {\n method: 'POST',\n headers: notionHeaders(accessToken),\n body: JSON.stringify({ page_size: Math.min(maxResults, 100) }),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Notion API error (${response.status}): ${text}`);\n }\n\n const data = (await response.json()) as { results: NotionApiPage[] };\n\n return data.results.map((page) => ({\n id: page.id,\n title: extractTitle(page),\n url: page.url,\n lastEditedTime: page.last_edited_time,\n }));\n}\n\n/**\n * Get text content from a Notion page's blocks.\n */\nexport async function getNotionPageContent(\n accessToken: string,\n pageId: string,\n): Promise<NotionPageContent> {\n // Fetch page metadata for title\n const pageResp = await fetch(`${NOTION_API}/pages/${pageId}`, {\n headers: notionHeaders(accessToken),\n });\n\n if (!pageResp.ok) {\n const text = await pageResp.text().catch(() => '');\n throw new Error(`Notion API error (${pageResp.status}): ${text}`);\n }\n\n const pageData = (await pageResp.json()) as NotionApiPage;\n const title = extractTitle(pageData);\n\n // Fetch blocks\n const blocksResp = await fetch(`${NOTION_API}/blocks/${pageId}/children?page_size=100`, {\n headers: notionHeaders(accessToken),\n });\n\n if (!blocksResp.ok) {\n const text = await blocksResp.text().catch(() => '');\n throw new Error(`Notion API error (${blocksResp.status}): ${text}`);\n }\n\n const blocksData = (await blocksResp.json()) as { results: NotionBlock[] };\n\n // Extract text from supported block types\n const textParts: string[] = [];\n for (const block of blocksData.results) {\n if (TEXT_BLOCK_TYPES.includes(block.type as (typeof TEXT_BLOCK_TYPES)[number])) {\n const blockContent = block[block.type] as { rich_text?: NotionRichText[] } | undefined;\n if (blockContent?.rich_text) {\n const text = blockContent.rich_text.map((t) => t.plain_text).join('');\n if (text) textParts.push(text);\n }\n }\n }\n\n return {\n title,\n content: textParts.join('\\n'),\n url: pageData.url,\n };\n}\n"],"mappings":"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","names":["readFile","join","dirname","fileURLToPath","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","response","mapRow","readdir","stat","join","readFile","readFile","fullText","TOOL_NAME","stat","formatSize","readdir","join","parseDate","clampDay","formatDate","lastDayOfMonth","amount","cachedIndex","TOOL_NAME","TOOL_NAME","readFile","stat","basename","extname","mapRow","mapRow","TOOL_NAME","extname","stat","basename","readFile","formatSize","randomUUID","normalize","resolve","mapRow","TOOL_NAME","randomUUID","mapRow","mapRow","mapRow","addMonths","DISCLAIMER","toCents","postMoneyValuation","totalPoolShares","TOOL_NAME","md","writeFile","tmpdir","join","join","mapRow","mapRow","rpc","rpc","EXPIRY_BUFFER_MS","EXPIRY_BUFFER_MS","TOOL_NAME","join","join","dirname","fileURLToPath","SERVER_VERSION","z","readFile"]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}