@productbrain/mcp 0.0.1-beta.4513 → 0.0.1-beta.4530

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.
@@ -549,6 +549,9 @@ var ROUTE_TYPE_ENTRIES = {
549
549
  "organisation.hierarchy.show": "query",
550
550
  "organisation.hierarchy.setParent": "mutation",
551
551
  "organisation.hierarchy.clearParent": "mutation",
552
+ "acceptPolicy.approve": "mutation",
553
+ "acceptPolicy.retire": "mutation",
554
+ "acceptPolicy.list": "query",
552
555
  "chain.seed": "mutation",
553
556
  "chain.listCollections": "query",
554
557
  "chain.getCollection": "query",
@@ -1669,4 +1672,4 @@ export {
1669
1672
  requireVendorTriageAccess,
1670
1673
  recoverSessionState
1671
1674
  };
1672
- //# sourceMappingURL=chunk-SD33RK7E.js.map
1675
+ //# sourceMappingURL=chunk-3M2TTUHD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/analytics.ts","../src/auth.ts","../src/cli/config-writer.ts","../src/generated/routeLatencyBudget.generated.ts","../src/lib/gatewaySeamStore.ts","../src/gatewaySeam.ts","../src/client.ts","../src/lib/conversation.ts","../src/lib/toolActionCounts.ts","../src/lib/deploymentUrlResolver.ts","../src/prod-fallthrough.ts"],"sourcesContent":["/**\n * PostHog analytics for SynergyOS maintainers — tracks MCP usage (sessions, tool calls).\n * Not user-facing. Key is injected at build time via SYNERGYOS_POSTHOG_KEY.\n * Override with POSTHOG_MCP_KEY for self-hosted deployments.\n */\n\nimport { userInfo } from \"node:os\";\nimport { PostHog } from \"posthog-node\";\n\nlet client: PostHog | null = null;\nlet distinctId = \"anonymous\";\n\nconst POSTHOG_HOST = \"https://eu.i.posthog.com\";\n\n/** Injected at build time: SYNERGYOS_POSTHOG_KEY env when running `npm run build`/publish. */\ndeclare const __SYNERGYOS_POSTHOG_KEY__: string;\n\n/** Only write to stderr when MCP_DEBUG=1 for quieter default DX. */\nfunction log(msg: string): void {\n if (process.env.MCP_DEBUG === \"1\") {\n process.stderr.write(msg);\n }\n}\n\nfunction getBuildTimeKey(): string {\n try {\n return __SYNERGYOS_POSTHOG_KEY__;\n } catch {\n // Not replaced by bundler (e.g. running via tsx in tests) — treat as absent.\n return \"\";\n }\n}\n\nexport function initAnalytics(): void {\n const apiKey = process.env.POSTHOG_MCP_KEY || getBuildTimeKey();\n if (!apiKey) {\n log(\"[MCP-ANALYTICS] No PostHog key — tracking disabled (set SYNERGYOS_POSTHOG_KEY at build time for publish)\\n\");\n return;\n }\n\n client = new PostHog(apiKey, {\n host: POSTHOG_HOST,\n flushAt: 1,\n flushInterval: 5000,\n featureFlagsPollingInterval: 30_000,\n });\n distinctId = process.env.MCP_USER_ID || fallbackDistinctId();\n\n log(`[MCP-ANALYTICS] Initialized — host=${POSTHOG_HOST} distinctId=${distinctId}\\n`);\n}\n\nfunction fallbackDistinctId(): string {\n try {\n return userInfo().username;\n } catch {\n return `os-${process.pid}`;\n }\n}\n\nexport function trackSessionStarted(\n workspaceId: string,\n serverVersion: string,\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_session_started\",\n properties: {\n workspace_id: workspaceId,\n server_version: serverVersion,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackToolCall(\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n workspaceId: string,\n errorMsg?: string,\n): void {\n const properties: Record<string, unknown> = {\n tool: fn,\n status,\n duration_ms: durationMs,\n workspace_id: workspaceId,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n };\n if (errorMsg) properties.error = errorMsg;\n\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_tool_called\",\n properties,\n });\n}\n\n/**\n * Per-tool+action call telemetry (WP-484 S1, Q3). Fired from\n * `runWithToolContext` in client.ts — the one chokepoint every compound tool\n * already wraps its handler body in. Feeds the next consolidation decision\n * (§2 of the design: no per-MCP-tool call-frequency signal existed before this).\n */\nexport function trackCompoundToolAction(\n tool: string,\n action: string | undefined,\n workspaceId: string,\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_compound_tool_action\",\n properties: {\n tool,\n action: action ?? null,\n workspace_id: workspaceId,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackSetupStarted(): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_started\",\n properties: {\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackSetupCompleted(\n chosenClient: string,\n outcome: \"config_written\" | \"config_existed\" | \"snippet_shown\" | \"write_error\",\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_completed\",\n properties: {\n client: chosenClient,\n outcome,\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackQualityVerdict(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n context: string;\n passed: boolean;\n source: string;\n criteria_total: number;\n criteria_failed: number;\n llm_scheduled: boolean;\n /** WP-475 E3: rubric methodology that produced the verdict (v1-vs-v2 seam). */\n methodology_version?: string;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_generated\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackQualityCheck(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n passed: boolean;\n source: string;\n llm_status?: string;\n llm_duration_ms?: number;\n llm_error?: string;\n has_roger_martin: boolean;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_checked\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport type ClassifierReasonCategory =\n | \"auto-routed\"\n | \"low-confidence\"\n | \"ambiguous\"\n | \"non-provisioned\";\n\ntype CaptureClassifierTelemetryProps = {\n predicted_collection: string;\n confidence: number;\n auto_routed: boolean;\n reason_category: ClassifierReasonCategory;\n explicit_collection_provided: boolean;\n};\n\nfunction trackCaptureClassifierEvent(\n event: \"mcp_capture_classifier_evaluated\" | \"mcp_capture_classifier_auto_routed\" | \"mcp_capture_classifier_fallback\",\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event,\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics are advisory and must never break capture flow.\n }\n}\n\nexport function trackCaptureClassifierEvaluated(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_evaluated\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierAutoRouted(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_auto_routed\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierFallback(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_fallback\", workspaceId, props);\n}\n\n/** GLO-26 / TEN-156: every SSOT commit for PostHog funnels (split auto vs manual). */\nexport function trackChainEntryCommitted(\n workspaceId: string,\n props: {\n entry_id: string;\n collection?: string;\n commit_method: \"auto\" | \"manual\";\n surface:\n | \"mcp_commit_tool\"\n | \"mcp_capture\"\n | \"mcp_wrapup\";\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"chain_entry_committed\",\n properties: {\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n ...props,\n },\n });\n } catch {\n // Analytics must never break the tool path.\n }\n}\n\nexport function trackKnowledgeGap(\n workspaceId: string,\n props: {\n query: string;\n tool: string;\n action: string;\n gap_type: \"search_zero\" | \"context_task_empty\" | \"context_entry_isolated\" | \"context_graph_empty\";\n collection_scope?: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"knowledge_gap_detected\",\n properties: {\n ...props,\n query: props.query.slice(0, 200),\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the tool response path.\n }\n}\n\n// ── BET-272 S6 / STD-155: Capture intelligence observability ────────────────\n\n/** Fires when formative quality hints are returned at capture time. */\nexport function trackCaptureQualityHints(\n workspaceId: string,\n props: {\n collection: string;\n hint_count: number;\n hint_fields: string[];\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"mcp_capture_quality_hints\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break capture flow.\n }\n}\n\n/** Fires when relation suggestions are returned at capture time. */\nexport function trackCaptureRelationSuggestions(\n workspaceId: string,\n props: {\n collection: string;\n suggestion_count: number;\n relation_types: string[];\n avg_confidence: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"mcp_capture_relation_suggestions\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break capture flow.\n }\n}\n\n// ── BET-288 S0: Collection classification confusion matrix telemetry ────────\n\n/**\n * Fires on every collection classification (auto-routed, fallback, explicit-provided).\n * Captures the full confusion signal: predicted collection, runner-up, thinkingLayer,\n * and confidence scores. This is the baseline measurement for BET-288 algorithm changes.\n */\nexport function trackCollectionClassified(\n workspaceId: string,\n props: {\n collection_slug: string;\n thinking_layer: string | null;\n confidence: number;\n classified_by: \"llm\" | \"heuristic\";\n confidence_tier: \"high\" | \"medium\" | \"low\";\n alternative_slug: string | null;\n alternative_confidence: number | null;\n explicit_collection_provided: boolean;\n auto_routed: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"collection_classified\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\n// ── BET-289 S5: Field-level writing guidance telemetry ────────────────────────\n\n/** Fires when field guidance is injected into a capture prompt or response. */\nexport function trackFieldGuidanceApplied(\n workspaceId: string,\n props: {\n collection: string;\n guided_field_count: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_guidance_applied\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\n/** Fires when commit-time heuristic detects field guidance violations. */\nexport function trackFieldQualityWarning(\n workspaceId: string,\n props: {\n warning_count: number;\n warning_types: string[];\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_quality_warning\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit flow.\n }\n}\n\n/** Fires when skipGuidanceCheck is used to bypass guidance validation. */\nexport function trackFieldQualityOverride(\n workspaceId: string,\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_quality_override\",\n properties: {\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit flow.\n }\n}\n\n// ── WP-306 S3: Write-Back Measurement (capture rate insights) ──────────────\n\n/** Fires on session close to track session capture rate. */\nexport function trackSessionCaptureRate(\n workspaceId: string,\n props: {\n entries_created: number;\n entries_modified: number;\n relations_created: number;\n had_captures: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"session_capture_rate\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break session closure.\n }\n}\n\n/** Fires when captureAudit is triggered (activity but no captures). */\nexport function trackZeroCaptureAuditFired(\n workspaceId: string,\n props: {\n suggestion_count: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"zero_capture_audit_fired\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break wrapup flow.\n }\n}\n\n// ── WP-316 S1b: Capture contract observability ──────────────────────────────\n\n/** Fires when a capture-contract resource request references an unknown collection slug. */\nexport function trackCaptureContractMiss(\n workspaceId: string,\n props: {\n slug: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"capture_contract_miss\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the resource response path.\n }\n}\n\n/** Fires when writeBackHints are included in an orient response. */\nexport function trackWriteBackHintServed(\n workspaceId: string,\n props: {\n hint_count: number;\n has_task: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"write_back_hint_served\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break orient flow.\n }\n}\n\n/**\n * WP-316 S1a: Fires when entries action=commit fails with a structured error code.\n * Enables observability on which validation errors block commits most often.\n */\nexport function trackCommitErrorByCode(\n workspaceId: string,\n props: {\n error_code: string;\n missing_field_count: number;\n field_error_count: number;\n entry_id: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"commit_error_by_code\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit error path.\n }\n}\n\n// ── WP-316 S2: Classifier divergence tracking ─────────────────────────────\n\n/**\n * Fires when an agent provides an explicit collection that differs from the\n * classifier's top suggestion. Measures how often agents override the classifier\n * and whether those overrides are to known alternatives or completely off-roster.\n */\nexport function trackClassifierDivergence(\n workspaceId: string,\n props: {\n classifier_collection: string;\n agent_collection: string;\n classifier_confidence: number;\n classifier_tier: string;\n agent_in_candidates: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"classifier_divergence\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\nexport function getPostHogClient(): PostHog | null {\n return client;\n}\n\nexport async function shutdownAnalytics(): Promise<void> {\n await client?.shutdown();\n}\n","/**\n * Request-scoped auth for HTTP transport mode.\n *\n * stdio: API key from PRODUCTBRAIN_API_KEY env, one user per process.\n * http: API key from Bearer header per request, many users per process.\n *\n * AsyncLocalStorage propagates the token through the async call chain\n * so client.ts resolves the correct API key and state per request.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { createHash } from \"node:crypto\";\n\n// ── Key Hashing (Fix 3 — session binding) ───────────────────────────────\n\n/**\n * Short one-way hash of an API key used to bind MCP sessions to a specific key.\n * Not a secret — stored in the session entry to detect session hijacking.\n */\nexport function hashKey(key: string): string {\n return createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 16);\n}\n\n// ── Request Context ─────────────────────────────────────────────────────\n\ninterface RequestAuth {\n apiKey: string;\n /**\n * WP-479 review fix (Codex re-review): the HTTP transport's `Mcp-Session-Id` for this request.\n * Session-lifecycle state (the active agentSessionId + oriented/closed flags) keys on this so two\n * concurrent HTTP streams SHARING an API key don't overwrite each other's active session. Absent\n * in STDIO mode (one process = one session) and on the pre-session initialize request.\n */\n mcpSessionId?: string;\n}\n\nconst requestStore = new AsyncLocalStorage<RequestAuth>();\n\nexport function runWithAuth<T>(auth: RequestAuth, fn: () => T | Promise<T>): T | Promise<T> {\n return requestStore.run(auth, fn);\n}\n\nexport function getRequestApiKey(): string | undefined {\n return requestStore.getStore()?.apiKey;\n}\n\n/** The current HTTP request's `Mcp-Session-Id`, when present (HTTP transport, post-initialize). */\nexport function getRequestMcpSessionId(): string | undefined {\n return requestStore.getStore()?.mcpSessionId;\n}\n\n// ── Per-Key State (HTTP mode) ───────────────────────────────────────────\n\nexport interface KeyState {\n workspaceId: string | null;\n workspaceSlug: string | null;\n workspaceName: string | null;\n workspaceCreatedAt: number | null;\n /** BET-76 FEAT-111: Cached at workspace resolution time. Defaults to 'open'. */\n workspaceGovernanceMode: \"open\" | \"consensus\" | \"role\" | null;\n agentSessionId: string | null;\n apiKeyId: string | null;\n apiKeyScope: \"read\" | \"readwrite\";\n sessionOriented: boolean;\n sessionClosed: boolean;\n lastAccess: number;\n /** DEC-789 S2: Convex deployment URL this key belongs to, resolved at key-check time. */\n deploymentUrl: string | null;\n}\n\nconst SESSION_TTL_MS = 30 * 60 * 1000;\nconst MAX_KEYS = 100;\nconst keyStateMap = new Map<string, KeyState>();\n\nfunction newKeyState(): KeyState {\n return {\n workspaceId: null,\n workspaceSlug: null,\n workspaceName: null,\n workspaceCreatedAt: null,\n workspaceGovernanceMode: null,\n agentSessionId: null,\n apiKeyId: null,\n apiKeyScope: \"readwrite\",\n sessionOriented: false,\n sessionClosed: false,\n lastAccess: Date.now(),\n deploymentUrl: null,\n };\n}\n\nexport function getKeyState(apiKey: string): KeyState {\n let s = keyStateMap.get(apiKey);\n if (!s) {\n s = newKeyState();\n keyStateMap.set(apiKey, s);\n evictStale();\n }\n s.lastAccess = Date.now();\n return s;\n}\n\nfunction evictStale(): void {\n if (keyStateMap.size <= MAX_KEYS) return;\n const now = Date.now();\n for (const [key, s] of keyStateMap) {\n if (now - s.lastAccess > SESSION_TTL_MS) keyStateMap.delete(key);\n }\n if (keyStateMap.size > MAX_KEYS) {\n const sorted = [...keyStateMap.entries()].sort((a, b) => a[1].lastAccess - b[1].lastAccess);\n for (let i = 0; i < sorted.length - MAX_KEYS; i++) {\n keyStateMap.delete(sorted[i][0]);\n }\n }\n}\n","/**\n * Multi-client MCP config detection and writer.\n *\n * Supports:\n * - Cursor: .cursor/mcp.json in cwd (project-level)\n * - Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)\n * %APPDATA%/Claude/claude_desktop_config.json (Windows)\n *\n * The writer reads existing config, merges the new server entry (never\n * overwrites existing entries), and writes back. Falls back to printing\n * a snippet for unsupported OS or unknown formats.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\n\nexport interface McpClientInfo {\n name: string;\n configPath: string;\n}\n\nconst SERVER_ENTRY_KEY = \"Product Brain\";\nconst LEGACY_ENTRY_KEY = \"productbrain\";\n\n/**\n * Canonical npx package specifier. Update here when exiting beta.\n * Frontend mirror: src/lib/constants/mcp.ts\n * Business rule: BR-84 (Chain)\n */\nexport const MCP_NPX_PACKAGE = \"@productbrain/mcp@beta\";\n\nfunction buildServerEntry(apiKey: string) {\n return {\n command: \"npx\",\n args: [\"-y\", MCP_NPX_PACKAGE],\n env: { PRODUCTBRAIN_API_KEY: apiKey },\n };\n}\n\n// ── Detection ───────────────────────────────────────────────────────────\n\nfunction getCursorConfigPath(): string {\n return join(process.cwd(), \".cursor\", \"mcp.json\");\n}\n\nfunction getClaudeDesktopConfigPath(): string | null {\n const os = platform();\n if (os === \"darwin\") {\n return join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n }\n if (os === \"win32\") {\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"Claude\", \"claude_desktop_config.json\");\n }\n // Linux: no official Claude Desktop location yet\n return null;\n}\n\nexport function resolveClient(name: \"Cursor\" | \"Claude Desktop\"): McpClientInfo | null {\n if (name === \"Cursor\") {\n return { name, configPath: getCursorConfigPath() };\n }\n const configPath = getClaudeDesktopConfigPath();\n return configPath ? { name, configPath } : null;\n}\n\n// ── Writing ─────────────────────────────────────────────────────────────\n\nfunction readJsonSafe(path: string): Record<string, any> {\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"));\n } catch {\n return {};\n }\n}\n\n/**\n * Write or merge the Product Brain server entry into a client config file.\n * Migrates legacy \"productbrain\" key to \"Product Brain\" when present.\n * Returns true if the config was written, false if already present.\n */\nexport async function writeClientConfig(\n client: McpClientInfo,\n apiKey: string,\n): Promise<boolean> {\n const config = readJsonSafe(client.configPath);\n\n const serversKey = \"mcpServers\";\n if (!config[serversKey]) config[serversKey] = {};\n\n // Migrate legacy \"productbrain\" key or update existing Product Brain with new API key\n if (config[serversKey][LEGACY_ENTRY_KEY]) {\n const legacy = config[serversKey][LEGACY_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = {\n ...buildServerEntry(apiKey),\n env: { ...legacy.env, PRODUCTBRAIN_API_KEY: legacy.env?.PRODUCTBRAIN_API_KEY ?? apiKey },\n };\n delete config[serversKey][LEGACY_ENTRY_KEY];\n } else {\n const existing = config[serversKey][SERVER_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = existing\n ? { ...existing, env: { ...existing.env, PRODUCTBRAIN_API_KEY: apiKey } }\n : buildServerEntry(apiKey);\n }\n\n const dir = dirname(client.configPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(client.configPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n return true;\n}\n","// GENERATED by scripts/generate-route-latency-budget.mjs from packages/kernel-client/src/routeLatencyBudget.ts\n// and packages/kernel-client/src/generated/routes.generated.ts.\n// DO NOT EDIT. Run `npm run route-budget:codegen` to regenerate.\n//\n// WP-575 (TEN-2917, TEN-1126), commits_to DEC-64. This file is emitted IDENTICALLY into\n// packages/mcp-server and packages/cli so the two connectors cannot disagree about how\n// long a gateway route may take — the defect this work package removes was exactly that\n// disagreement (MCP hardcoded a blanket 10s, the CLI set none at all).\n//\n// The rationale — why route TYPE is the structural predictor of LLM exposure, and how\n// each value is grounded in DEC-64 rather than invented — lives with the declaration in\n// packages/kernel-client/src/routeLatencyBudget.ts. Read that file before changing a\n// number here; changing it HERE does nothing, the next codegen run overwrites it.\n//\n// Why a copy and not an import: both connectors consume @productbrain/kernel-client\n// TYPE-ONLY (mcp-server via a tsconfig paths alias for Railway's isolated Docker build,\n// DEC-701; cli as a devDependency it must not emit into its published dist/). A budget\n// sets an AbortSignal, so it is runtime code and cannot be type-erased. See the\n// generator header for the full constraint.\n\nexport type GatewayRouteType = 'query' | 'mutation' | 'action';\n\n/** The budget table, copied verbatim from the SSOT. Milliseconds. */\nexport const ROUTE_LATENCY_BUDGET_MS = {\n\tquery: 10_000,\n\tmutation: 10_000,\n\taction: 30_000,\n} as const;\n\n/**\n * PER-ROUTE OVERRIDES — routes whose OWN server-side deadline exceeds their type's budget\n * (PR #533 review, Codex P1). Route type predicts LLM EXPOSURE, not LLM COUNT: a flat\n * action budget assumed every action is one inline LLM call, and shipped actions declare\n * far longer deadlines. Bounding those at the type budget aborts a call the server is\n * still legitimately working on. Each value is derived from a deadline the SERVER\n * declares — see the SSOT for the citation behind every number.\n */\nexport const ROUTE_LATENCY_BUDGET_OVERRIDE_MS = {\n\t// convex/intelligence/onboardingChat.ts:333 declares `timeoutMs: 45_000` for the extraction\n\t// LLM call — 15s BEYOND the flat action budget. 60s clears it with room for the surrounding\n\t// request/response work the 45s covers none of.\n\t'onboarding.chat': 60_000,\n\t// convex/intelligence/spineCheck.ts:210 sets SPINE_CHECK_WALL_CLOCK_BUDGET_MS = 4 * 60_000\n\t// for its SEQUENTIAL strategy probes (:225-239, deliberately not Promise.all), and :215\n\t// reserves a tail of one 25s probe + an optional 25s story pass + 10s. The server's own\n\t// design ceiling is scheduleSpineCheck's 5-min MAX_RUN_IN_FLIGHT_MS lease (:206-209).\n\t// 5.5 min sits above that ceiling and far under Convex's 10-min action cap.\n\t'quality.spineCheck': 330_000,\n} as const;\n\n/**\n * The widest declared budget — the fail-safe default for a route name this map does not\n * know. Derived from the tables rather than restated, so it cannot drift if a value moves.\n * Spans the overrides too, so the unknown-route default is never TIGHTER than a budget\n * already granted to a known route.\n */\nexport const WIDEST_ROUTE_LATENCY_BUDGET_MS: number = Math.max(\n\t...Object.values(ROUTE_LATENCY_BUDGET_MS),\n\t...Object.values(ROUTE_LATENCY_BUDGET_OVERRIDE_MS),\n);\n\n/** Every gateway route's Convex function type, from convex/http.ts's registry. */\nconst ROUTE_TYPE_ENTRIES: Readonly<Record<string, GatewayRouteType>> = {\n\t\"resolveWorkspace\": \"query\",\n\t\"feedback.submit\": \"mutation\",\n\t\"feedback.listOwn\": \"query\",\n\t\"feedback.list\": \"action\",\n\t\"feedback.note\": \"action\",\n\t\"feedback.group\": \"action\",\n\t\"feedback.status\": \"action\",\n\t\"organisation.status\": \"query\",\n\t\"organisation.assignMembership\": \"mutation\",\n\t\"organisation.removeMembership\": \"mutation\",\n\t\"organisation.setOwnership\": \"mutation\",\n\t\"organisation.clearOwnership\": \"mutation\",\n\t\"organisation.activate\": \"mutation\",\n\t\"organisation.upgrade\": \"mutation\",\n\t\"organisation.upgradeRung2\": \"mutation\",\n\t\"organisation.upgradeRung3\": \"mutation\",\n\t\"organisation.upgradeRung4\": \"mutation\",\n\t\"organisation.updateGovernanceMode\": \"mutation\",\n\t\"organisation.hierarchy.show\": \"query\",\n\t\"organisation.hierarchy.setParent\": \"mutation\",\n\t\"organisation.hierarchy.clearParent\": \"mutation\",\n\t\"acceptPolicy.approve\": \"mutation\",\n\t\"acceptPolicy.retire\": \"mutation\",\n\t\"acceptPolicy.list\": \"query\",\n\t\"chain.seed\": \"mutation\",\n\t\"chain.listCollections\": \"query\",\n\t\"chain.getCollection\": \"query\",\n\t\"chain.getCollectionFields\": \"query\",\n\t\"chain.auditCollections\": \"query\",\n\t\"chain.exportDefinitions\": \"query\",\n\t\"chain.createCollection\": \"mutation\",\n\t\"chain.updateCollection\": \"mutation\",\n\t\"chain.listEntries\": \"query\",\n\t\"chain.getEntry\": \"query\",\n\t\"chain.batchGetEntries\": \"query\",\n\t\"chain.createEntry\": \"action\",\n\t\"chain.updateEntry\": \"mutation\",\n\t\"chain.restoreArchivedEntry\": \"mutation\",\n\t\"chain.moveToCollection\": \"mutation\",\n\t\"chain.shapeAdvisories\": \"query\",\n\t\"chain.shapeAdvisorySummary\": \"query\",\n\t\"chain.showShapeAdvisory\": \"query\",\n\t\"chain.dispositionShapeAdvisory\": \"mutation\",\n\t\"conflicts.list\": \"query\",\n\t\"conflicts.resolve\": \"mutation\",\n\t\"conflicts.summary\": \"query\",\n\t\"conflicts.snooze\": \"mutation\",\n\t\"conflicts.reverdict\": \"mutation\",\n\t\"direction.list\": \"query\",\n\t\"direction.refresh\": \"action\",\n\t\"direction.defer\": \"mutation\",\n\t\"question.create\": \"mutation\",\n\t\"question.adopt\": \"mutation\",\n\t\"question.assign\": \"mutation\",\n\t\"question.snooze\": \"mutation\",\n\t\"question.decline\": \"mutation\",\n\t\"question.answer\": \"mutation\",\n\t\"question.forceClose\": \"mutation\",\n\t\"question.list\": \"query\",\n\t\"chain.classifyCollection\": \"action\",\n\t\"chain.classifyStrategyCategory\": \"query\",\n\t\"chain.batchClassifyHeuristic\": \"query\",\n\t\"chain.resolveCollection\": \"action\",\n\t\"chain.searchEntries\": \"query\",\n\t\"chain.searchByCanonicalName\": \"query\",\n\t\"chain.commitEntry\": \"mutation\",\n\t\"chain.verifyEntry\": \"mutation\",\n\t\"chain.batchCommitConstellation\": \"mutation\",\n\t\"chain.listEntryHistory\": \"query\",\n\t\"chain.listEntryVersions\": \"query\",\n\t\"chain.createEntryRelation\": \"mutation\",\n\t\"chain.createEntryRelations\": \"mutation\",\n\t\"chain.removeEntryRelation\": \"mutation\",\n\t\"chain.normalizeEntryDataLLM\": \"action\",\n\t\"chain.decomposeContent\": \"action\",\n\t\"chain.normalizeEntryDataPreview\": \"action\",\n\t\"chain.listEntryRelations\": \"query\",\n\t\"chain.scoreLinkCandidates\": \"query\",\n\t\"chain.evaluateCoherence\": \"query\",\n\t\"chain.listAutoLinkSuggestions\": \"query\",\n\t\"chain.acceptAutoLinkSuggestion\": \"mutation\",\n\t\"chain.dismissAutoLinkSuggestion\": \"mutation\",\n\t\"chain.quarantineAutoLinkSuggestion\": \"mutation\",\n\t\"chain.resurrectAutoLinkSuggestion\": \"mutation\",\n\t\"chain.expireAutoLinkSuggestion\": \"mutation\",\n\t\"chain.clusterAutoLinkSuggestions\": \"query\",\n\t\"chain.batchApplyAutoLinkSuggestions\": \"action\",\n\t\"chain.validateCommitConstellation\": \"query\",\n\t\"chain.getCaptureContract\": \"query\",\n\t\"chain.gatherContext\": \"query\",\n\t\"chain.getConstellation\": \"query\",\n\t\"chain.auditBet\": \"query\",\n\t\"agentKnowledge.facilitateEnvelope\": \"action\",\n\t\"agentKnowledge.wrapupEnvelope\": \"action\",\n\t\"agentKnowledge.captureEnvelope\": \"action\",\n\t\"chain.graphSuggestLinks\": \"query\",\n\t\"chain.graphGatherContext\": \"query\",\n\t\"chain.assembleBuildContext\": \"query\",\n\t\"chain.qualityCheck\": \"query\",\n\t\"chain.changeDetection\": \"query\",\n\t\"chain.structuralAggregation\": \"query\",\n\t\"chain.detectSemanticConflicts\": \"action\",\n\t\"chain.taskAwareGatherContext\": \"query\",\n\t\"chain.journeyAwareGatherContext\": \"query\",\n\t\"chain.resolveTaskStartup\": \"query\",\n\t\"chain.getBindingGovernanceView\": \"query\",\n\t\"chain.resolveTaskStartupHybrid\": \"action\",\n\t\"chain.taskAwareHybridGatherContext\": \"action\",\n\t\"chain.gatherFromSeeds\": \"query\",\n\t\"chain.getEntryNeighborhood\": \"query\",\n\t\"chain.deepChainWalk\": \"action\",\n\t\"chain.recordBriefRun\": \"mutation\",\n\t\"chain.getLastBriefRun\": \"query\",\n\t\"chain.incrementalChanges\": \"query\",\n\t\"chain.compoundQuery\": \"action\",\n\t\"chain.dismissSuggestion\": \"mutation\",\n\t\"chain.recordSessionSignal\": \"mutation\",\n\t\"chain.workspaceReadiness\": \"query\",\n\t\"chain.getCaptureHealth\": \"query\",\n\t\"chain.getGroundingHealth\": \"query\",\n\t\"chain.recordGroundingOutcome\": \"mutation\",\n\t\"chain.suggestLinksForCapture\": \"query\",\n\t\"chain.setOnboardingCompleted\": \"mutation\",\n\t\"chain.checkCardinalityWarning\": \"query\",\n\t\"chain.ingestDocument\": \"action\",\n\t\"chain.getOrientEntries\": \"query\",\n\t\"chain.getOrientView\": \"action\",\n\t\"chain.getRitualsSurface\": \"query\",\n\t\"chain.getGovernanceWithRelations\": \"action\",\n\t\"chain.classifyGovernance\": \"query\",\n\t\"chain.getVocabulary\": \"query\",\n\t\"scoreboard.get\": \"action\",\n\t\"rework.report\": \"mutation\",\n\t\"chain.listLabels\": \"query\",\n\t\"chain.createLabel\": \"mutation\",\n\t\"chain.updateLabel\": \"mutation\",\n\t\"chain.deleteLabel\": \"mutation\",\n\t\"chain.applyLabel\": \"mutation\",\n\t\"chain.removeLabel\": \"mutation\",\n\t\"chain.listEntriesByLabel\": \"query\",\n\t\"setup.getActiveSurface\": \"query\",\n\t\"setup.materializeSetup\": \"action\",\n\t\"setup.recordTamperRefusal\": \"mutation\",\n\t\"setup.recordTransition\": \"mutation\",\n\t\"setup.getCurrentSetupState\": \"query\",\n\t\"setup.listAssetsForUser\": \"query\",\n\t\"setup.ingestSetupAsset\": \"mutation\",\n\t\"setup.ingestSetupAssetWithBody\": \"action\",\n\t\"setup.fetchAssetBody\": \"action\",\n\t\"setup.auditAssetBodies\": \"action\",\n\t\"setup.repairAssetBody\": \"action\",\n\t\"setup.listFailedAuditReceipts\": \"action\",\n\t\"setup.markPersonalSetupAssetDormantFromSync\": \"mutation\",\n\t\"setup.resolveSemanticRefs\": \"query\",\n\t\"setup.updateLastProjectedHash\": \"mutation\",\n\t\"setup.getSkillSystemHealth\": \"query\",\n\t\"setup.recordActivationReceipt\": \"mutation\",\n\t\"setup.recordSetupInvocation\": \"mutation\",\n\t\"setup.getUserActivationState\": \"query\",\n\t\"setup.getPbSetupState\": \"query\",\n\t\"setup.getSkillBody\": \"action\",\n\t\"setup.stampMcpOnlySurface\": \"mutation\",\n\t\"setup.stampDetectedSurfaces\": \"mutation\",\n\t\"setup.getKey32Snapshot\": \"query\",\n\t\"setup.getKey33Snapshot\": \"query\",\n\t\"gaps.record\": \"mutation\",\n\t\"gaps.resolve\": \"mutation\",\n\t\"gaps.top\": \"query\",\n\t\"gaps.stats\": \"query\",\n\t\"agent.startSession\": \"mutation\",\n\t\"agent.resumeSession\": \"mutation\",\n\t\"agent.closeSession\": \"mutation\",\n\t\"agent.markOriented\": \"mutation\",\n\t\"agent.touchSession\": \"mutation\",\n\t\"agent.recordActivity\": \"mutation\",\n\t\"agent.getSession\": \"query\",\n\t\"agent.getActiveSession\": \"query\",\n\t\"agent.recentSessions\": \"query\",\n\t\"agent.activityStats\": \"query\",\n\t\"agent.getActivityByDay\": \"query\",\n\t\"agent.validateSession\": \"query\",\n\t\"agent.getSessionWrapup\": \"query\",\n\t\"agent.recordWrapup\": \"mutation\",\n\t\"agent.reportOrientMetric\": \"mutation\",\n\t\"agent.listSessions\": \"query\",\n\t\"agent.showConversation\": \"query\",\n\t\"usage.getWorkspaceSummary\": \"query\",\n\t\"quality.evaluateHeuristicAndSchedule\": \"mutation\",\n\t\"quality.reEvaluateEntry\": \"mutation\",\n\t\"quality.evaluateAtCapture\": \"action\",\n\t\"quality.evaluateAtCommit\": \"action\",\n\t\"quality.evaluateForReview\": \"action\",\n\t\"quality.getCachedVerdict\": \"query\",\n\t\"quality.getLatestVerdictForEntry\": \"query\",\n\t\"quality.spineCheck\": \"action\",\n\t\"quality.scheduleSpineCheck\": \"mutation\",\n\t\"quality.getLatestSpineVerdict\": \"query\",\n\t\"onboarding.chat\": \"action\",\n\t\"workspace.health\": \"query\",\n\t\"workspace.healthAll\": \"query\",\n\t\"workspace.backfill\": \"mutation\",\n\t\"workspace.backfillAll\": \"mutation\",\n\t\"authorityDomains.readiness\": \"query\",\n\t\"authorityDomains.add\": \"mutation\",\n\t\"authorityDomains.propose\": \"action\",\n\t\"authorityDomains.review\": \"query\",\n\t\"authorityDomains.queueKnownTag\": \"mutation\",\n\t\"authorityDomains.ratify\": \"mutation\",\n\t\"authorityDomains.reject\": \"mutation\",\n\t\"authorityDomains.discardPending\": \"mutation\",\n\t\"authorityDomains.recordSample\": \"mutation\",\n\t\"authorityDomains.benchmark\": \"action\",\n\t\"authorityDomains.activateCutover\": \"mutation\",\n\t\"authorityDomains.principleDistribution\": \"query\",\n\t\"performance.getVitalsSummary\": \"query\",\n\t\"performance.getApiOverview\": \"query\",\n\t\"performance.getRouteBreakdown\": \"query\",\n\t\"performance.getSlowSamples\": \"query\",\n\t\"performance.getSampleCount\": \"query\",\n\t\"chainwork.listTypes\": \"query\",\n\t\"chainwork.getChainType\": \"query\",\n\t\"chainwork.scoreRun\": \"query\",\n\t\"chainwork.getArtifact\": \"query\",\n\t\"chainwork.getWorkflowRun\": \"query\",\n\t\"chainwork.getLatestWorkflowRun\": \"query\",\n\t\"chainwork.recordWorkflowCheckpoint\": \"mutation\",\n\t\"chainwork.finalizeWorkflowRun\": \"action\",\n\t\"chainwork.submitToKG\": \"action\",\n\t\"chainwork.generate\": \"action\",\n\t\"chainwork.getLastVerifiedBrief\": \"query\",\n\t\"gitchain.createChain\": \"mutation\",\n\t\"gitchain.editLink\": \"mutation\",\n\t\"gitchain.updateChain\": \"mutation\",\n\t\"gitchain.getChain\": \"query\",\n\t\"gitchain.listChains\": \"query\",\n\t\"gitchain.getHistory\": \"query\",\n\t\"gitchain.listCommits\": \"query\",\n\t\"gitchain.commitChain\": \"mutation\",\n\t\"gitchain.diffVersions\": \"mutation\",\n\t\"gitchain.runGate\": \"query\",\n\t\"gitchain.createBranch\": \"mutation\",\n\t\"gitchain.listBranches\": \"mutation\",\n\t\"gitchain.checkConflicts\": \"mutation\",\n\t\"gitchain.mergeBranch\": \"mutation\",\n\t\"gitchain.addComment\": \"mutation\",\n\t\"gitchain.resolveComment\": \"mutation\",\n\t\"gitchain.listComments\": \"mutation\",\n\t\"gitchain.revertChain\": \"mutation\",\n\t\"staging.getCommittedSourceRefs\": \"query\",\n\t\"staging.commitStagingEntryWithClassification\": \"action\",\n\t\"governance.listProposals\": \"query\",\n\t\"governance.countOpenProposals\": \"query\",\n\t\"governance.respondToProposal\": \"mutation\",\n\t\"maps.createMap\": \"mutation\",\n\t\"maps.createAudienceMapSet\": \"mutation\",\n\t\"maps.addToSlot\": \"mutation\",\n\t\"maps.removeFromSlot\": \"mutation\",\n\t\"maps.replaceInSlot\": \"mutation\",\n\t\"maps.commitMap\": \"mutation\",\n\t\"maps.getMap\": \"query\",\n\t\"maps.listMaps\": \"query\",\n\t\"maps.getJourneyMapWithEnrichment\": \"query\",\n\t\"maps.listJourneyMaps\": \"query\",\n\t\"maps.listMapCommits\": \"query\",\n};\n\n/**\n * NULL-PROTOTYPE, deliberately (PR #533 review, Codex P2).\n *\n * A plain object literal inherits Object.prototype, so an unknown route name that\n * collides with an inherited member — `toString`, `constructor`, `valueOf` — resolves to a\n * TRUTHY function instead of falling through to the widest-budget default below. That\n * yielded `AbortSignal.timeout(undefined)`, which THROWS before the call ever reaches the\n * gateway — turning the fail-safe default into a hard failure.\n *\n * The SSOT scans an array (`GATEWAY_ROUTES.find`) and has no such hole, so a plain-object\n * copy DISAGREED with the source it is generated from — precisely the connector drift this\n * file exists to end. A null prototype restores parity for every consumer of the map at\n * once (`latencyBudgetMsForRoute` here, `routeMayMutate` in both connectors' seams) rather\n * than guarding each lookup and leaving the next one to rediscover it.\n */\nexport const ROUTE_TYPE_BY_NAME: Readonly<Record<string, GatewayRouteType>> = Object.assign(\n\tObject.create(null) as Record<string, GatewayRouteType>,\n\tROUTE_TYPE_ENTRIES,\n);\n\n/** Budget for a route TYPE. Total over the three kinds the registry can produce. */\nexport function latencyBudgetMsForRouteType(type: GatewayRouteType): number {\n\treturn ROUTE_LATENCY_BUDGET_MS[type];\n}\n\n/**\n * Budget for a route by NAME.\n *\n * FAILS SAFE, DELIBERATELY: an unknown name gets the WIDEST budget, not the tightest and\n * not a throw. Guessing low on a route we cannot classify would abort a call still\n * legitimately in flight and re-create the exact false-failure this work package removes.\n * Guessing high only delays surfacing a genuinely stuck call — and an unknown name is\n * almost always a caller bug the gateway rejects in milliseconds anyway.\n */\nexport function latencyBudgetMsForRoute(routeName: string): number {\n\t// `Object.hasOwn`, not truthiness: the override table is a plain literal, so a bare\n\t// lookup would resolve INHERITED members to a truthy value. Same hole the null\n\t// prototype closes above, guarded here because this table stays a plain literal.\n\tif (Object.hasOwn(ROUTE_LATENCY_BUDGET_OVERRIDE_MS, routeName)) {\n\t\treturn (ROUTE_LATENCY_BUDGET_OVERRIDE_MS as Record<string, number>)[routeName];\n\t}\n\tconst type = ROUTE_TYPE_BY_NAME[routeName];\n\treturn type ? latencyBudgetMsForRouteType(type) : WIDEST_ROUTE_LATENCY_BUDGET_MS;\n}\n","/**\n * The gateway seam's MEMORY — what each tenant's calls have added up to, kept per workspace.\n *\n * Split out of ./gatewaySeam.ts during the PR #533 review round at the 500-LOC ratchet's\n * insistence (STD-2 / DEC-1504), and the ratchet was reading the design correctly: classifying\n * how one call failed and remembering what a tenant's calls have cost are different jobs with\n * different reasons to change. gatewaySeam.ts keeps the error taxonomy and the recording\n * orchestration; this module owns the stores and the invariant that makes them safe.\n *\n * THAT INVARIANT IS TENANT PARTITIONING. In HTTP transport one MCP process multiplexes many\n * tenants (../http.ts's per-request `runWithAuth`), so every structure here is keyed by\n * workspace, bounded per workspace, and readable only by naming the scopes you own. Both\n * stores share one LRU (`touchBoundedWorkspaceMap`) precisely so they can never disagree about\n * which tenants are retained — a buffer that evicted a workspace the counters kept would\n * report one tenant's rates beside another tenant's call list.\n *\n * ../gatewaySeam.ts re-exports this module's public surface, so ../client.ts and the test mocks\n * that enumerate client.js's exports keep working unchanged.\n */\n// ─── Audit buffer ─────────────────────────────────────────────────────\n\nexport interface AuditEntry {\n ts: string;\n /**\n * Monotonic per-process recording order — the key `getAuditLog` merges a caller's buckets by.\n *\n * `ts` cannot do this job: it is ISO-8601 with MILLISECOND resolution, and several gateway\n * calls routinely land inside one millisecond, so sorting by it leaves same-ms entries in\n * whatever order the buckets happened to be visited. The old single global buffer got true\n * insertion order for free; partitioning it per workspace is what makes an explicit ordering\n * key necessary. Internal to the seam — the audit view renders `ts`, never this.\n */\n seq: number;\n fn: string;\n workspace: string;\n status: \"ok\" | \"error\";\n durationMs: number;\n error?: string;\n /** For compound tools: tool name and action for audit display */\n toolContext?: { tool: string; action?: string };\n /**\n * WP-575: the declared latency budget this call was given, in ms. Recorded on every entry\n * (not just failures) so an observed duration can be read against the budget it was\n * actually judged by, rather than against whatever the budget happens to be today.\n */\n budgetMs?: number;\n /** WP-575: true when this call failed by hitting `budgetMs`, not by a server rejection. */\n timedOut?: boolean;\n}\n\n/**\n * PER-WORKSPACE, not one global ring (PR #533 review, Codex P2).\n *\n * This buffer pre-dates the work package (it lived in client.ts) and was a single process-wide\n * array evicted with `shift()`. In HTTP transport one process multiplexes many tenants, and\n * `lib/auditView.ts` filters to the caller AFTER eviction has already happened — so any tenant\n * making 50 calls silently erased every other tenant's entries. The quiet tenant's\n * `health action=audit` then reported nothing at all while its own lifetime timeout counters\n * were nonzero: a noisy neighbour deleting exactly the KEY-79 evidence this work package\n * exists to make readable.\n *\n * The cap is now PER WORKSPACE, so 50 is a floor on what each tenant can see rather than a\n * pool they compete for, and the map of workspaces is bounded by the same `MAX_SEAM_WORKSPACES`\n * LRU as the counters — same `touchBoundedWorkspaceMap` helper, so the two can never disagree\n * about which tenants are retained.\n */\nconst AUDIT_BUFFER_SIZE = 50;\nconst auditBufferByWorkspace = new Map<string, AuditEntry[]>();\n/** Deliberately NOT reset by `__resetGatewaySeamCountersForTest` — order must stay strictly increasing for the life of the process, and nothing reads it as a count. */\nlet nextAuditSeq = 0;\n\n/**\n * This caller's slice of the audit buffer, oldest first, merged across the scopes it owns.\n *\n * Takes scopes for the same reason `getMergedGatewaySeamCounters` does — a session's first\n * call is filed under `cacheScope()` before `resolveWorkspace` returns — and the two MUST\n * merge over the same scope set or the log and the counters would describe different callers.\n * There is deliberately no \"give me everything\" shape: that is what leaked across tenants.\n *\n * Merged by `seq`, the recording order — within one workspace that is exactly what the old\n * global buffer returned; across a caller's two scopes it interleaves them, which is what\n * \"this caller's recent calls\" means. See `AuditEntry.seq` for why not `ts`.\n */\nexport function getAuditLog(scopes: readonly string[]): readonly AuditEntry[] {\n const merged: AuditEntry[] = [];\n for (const scope of new Set(scopes)) {\n const bucket = auditBufferByWorkspace.get(scope);\n if (bucket) merged.push(...bucket);\n }\n return merged.sort((a, b) => a.seq - b.seq);\n}\n\n// ─── Rolling latency + error counters (WP-575 element 5) ──────────────\n\n/**\n * WHY COUNTERS AND NOT JUST THE BUFFER: `auditBuffer` holds the last 50 entries, so it\n * answers \"what happened recently\" but cannot answer \"what is the failure rate\" — the older\n * entries are gone. KEY-79 measures exactly that (batch success rate, chain.createEntry\n * false-failure rate), and until now the only way to report it was to self-grade. These\n * counters are cumulative for the process lifetime and never evict, so the rate is real.\n *\n * `timeouts` is tracked SEPARATELY from `errors` on purpose, and it is the number this work\n * package exists to drive to zero: a timeout is the class of failure where the server may\n * actually have succeeded, so lumping it in with genuine server rejections is what made the\n * false-failure rate unmeasurable in the first place. Note `timeouts` is a SUBSET of\n * `errors` — every timeout is also counted there, so `errors` remains \"all failures.\"\n */\nexport interface GatewaySeamCounters {\n calls: number;\n errors: number;\n timeouts: number;\n /** Summed wall-clock ms across all calls — divide by `calls` for the mean. */\n totalDurationMs: number;\n /** Slowest single call observed, in ms. */\n maxDurationMs: number;\n /** Per-route breakdown, same fields, for attributing a rate to the route that caused it. */\n byRoute: Record<string, { calls: number; errors: number; timeouts: number; totalDurationMs: number; maxDurationMs: number }>;\n}\n\n/** A null-prototype `byRoute` — see `emptySeamCounters` for why it must never be a plain `{}`. */\nfunction emptyByRoute(): GatewaySeamCounters[\"byRoute\"] {\n return Object.create(null) as GatewaySeamCounters[\"byRoute\"];\n}\n\n/**\n * `byRoute` is NULL-PROTOTYPE, deliberately (PR #533 review round 3, Copilot).\n *\n * It is indexed by an arbitrary route NAME at three sites — `recordSeamCounters`, the snapshot\n * below, and `getMergedGatewaySeamCounters` — each via `byRoute[fn] ??= {...}`. On a plain `{}`\n * a route named `toString`/`constructor`/`valueOf` resolves to the INHERITED function, which is\n * neither null nor undefined, so `??=` does not assign and the following `route.calls += 1`\n * writes onto a function and yields **NaN** — silently corrupting the very counters KEY-79 is\n * measured from, with no error to notice. This is the same Object.prototype collision this PR\n * already closed for the route→type map; `byRoute` was the sibling left unswept.\n */\nfunction emptySeamCounters(): GatewaySeamCounters {\n return { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0, byRoute: emptyByRoute() };\n}\n\n/**\n * PR #533 review, Codex P2: in HTTP transport mode one process multiplexes MANY tenants\n * (see http.ts's per-request `runWithAuth`), so a single module-scope counters object — what\n * this used to be — let any authenticated caller read every OTHER tenant's process-lifetime\n * call/error/timeout history through `workspace action=audit`. Keying by workspace makes the\n * isolation structural: `getGatewaySeamCounters` can only ever return the slice for the\n * workspace it's asked for, so a caller who doesn't know another tenant's workspaceId cannot\n * reach that tenant's counters. In stdio mode there is exactly one workspace, so this degrades\n * to the old single-bucket behavior with no observable change.\n *\n * BOUNDED MAP: an HTTP process can be handed at most `MAX_SESSIONS` (200, http.ts) concurrent\n * sessions, and each session belongs to one workspace — so 200 is also the ceiling on distinct\n * workspaces a single process can plausibly be serving at once. Cap at that same number so a\n * long-lived multi-tenant process can't grow this map without limit.\n *\n * EVICT LEAST-RECENTLY-RECORDED, not oldest-inserted. This started as insertion-order FIFO\n * borrowed from `_sessionLifecycleByStream`, and that precedent does not transfer: its keys are\n * write-once per stream, so FIFO and LRU coincide there. A `workspaceId` is a STABLE key reused\n * for the life of the process, so under FIFO the workspace that has been active LONGEST is the\n * first evicted — the single most active tenant loses its counters the moment workspace #201\n * appears, and its next `workspace action=audit` reports zeros as if it had never made a call.\n * That silently defeats the KEY-79 observability this fix exists to provide. Recording a call\n * re-inserts the key so it moves to the end of the Map's insertion order, making eviction\n * genuinely least-recently-used — the same intent as http.ts's `lastAccess` sort, without\n * carrying a timestamp per bucket.\n */\nconst MAX_SEAM_WORKSPACES = 200;\nconst seamCountersByWorkspace = new Map<string, GatewaySeamCounters>();\n\n/**\n * Fetch-or-create a workspace's bucket, refreshing its LRU position and evicting the\n * least-recently-recorded workspace once the map is full.\n *\n * Shared by the counters and the audit buffer rather than written twice: both are keyed by\n * workspace, both are bounded by the same ceiling, and both must retain the same tenants — a\n * buffer that evicted a workspace the counters kept (or vice versa) would report a tenant's\n * rates against another tenant's call list. The eviction reasoning is in the comment above.\n */\nfunction touchBoundedWorkspaceMap<V>(map: Map<string, V>, workspace: string, create: () => V): V {\n const existing = map.get(workspace);\n if (existing !== undefined) {\n // Move to the end so eviction below is least-recently-RECORDED, not oldest-inserted.\n // A Map re-`set` of an existing key keeps the value and refreshes its insertion position.\n map.delete(workspace);\n map.set(workspace, existing);\n return existing;\n }\n if (map.size >= MAX_SEAM_WORKSPACES) {\n const leastRecentlyRecorded = map.keys().next().value;\n if (leastRecentlyRecorded !== undefined) map.delete(leastRecentlyRecorded);\n }\n const created = create();\n map.set(workspace, created);\n return created;\n}\n\nfunction getOrCreateWorkspaceCounters(workspace: string): GatewaySeamCounters {\n return touchBoundedWorkspaceMap(seamCountersByWorkspace, workspace, emptySeamCounters);\n}\n\nfunction getOrCreateWorkspaceAuditBuffer(workspace: string): AuditEntry[] {\n return touchBoundedWorkspaceMap(auditBufferByWorkspace, workspace, () => []);\n}\n\n/**\n * Snapshot of ONE workspace's gateway seam counters — deep-copied so a caller can hold it\n * across further calls without it mutating underneath them.\n *\n * PR #533 review, Codex P2: `workspace` is required, not optional — there is no \"give me\n * everything\" call shape, because that shape is exactly what let one tenant read another's\n * history. A workspace with no recorded calls yet gets a fresh empty snapshot, never another\n * workspace's data and never the old global aggregate.\n */\nexport function getGatewaySeamCounters(workspace: string): GatewaySeamCounters {\n const counters = seamCountersByWorkspace.get(workspace);\n if (!counters) return emptySeamCounters();\n return {\n calls: counters.calls,\n errors: counters.errors,\n timeouts: counters.timeouts,\n totalDurationMs: counters.totalDurationMs,\n maxDurationMs: counters.maxDurationMs,\n // Copied onto a null prototype, not left as `Object.fromEntries`' plain object — the\n // snapshot is indexed by route name downstream (`getMergedGatewaySeamCounters`) and would\n // reintroduce the inherited-member collision `emptySeamCounters` documents.\n byRoute: Object.assign(emptyByRoute(), Object.fromEntries(Object.entries(counters.byRoute).map(([k, v]) => [k, { ...v }]))),\n };\n}\n\n/**\n * Snapshot merged across SEVERAL buckets belonging to the SAME caller.\n *\n * Exists because a session's calls are not all recorded under one key. `audit()` attributes a\n * call to `state().workspaceId ?? cacheScope()` (client.ts), and `workspaceId` is only set once\n * `resolveWorkspace` RETURNS — so every session's first call, `resolveWorkspace` itself, is\n * recorded under `cacheScope()`. Filtering the audit view to the resolved workspaceId alone\n * therefore hid that call forever, under-counting every tenant's own view of itself (including\n * in stdio mode, where the pre-fix global aggregate had shown it).\n *\n * Merging is safe precisely because `cacheScope()` is derived from the CALLER'S OWN API key —\n * both buckets are the same tenant, so this widens a caller's view of itself without ever\n * reaching another tenant's. Callers must only pass scopes they have proven belong to them.\n */\nexport function getMergedGatewaySeamCounters(scopes: readonly string[]): GatewaySeamCounters {\n const merged = emptySeamCounters();\n for (const scope of new Set(scopes)) {\n const part = getGatewaySeamCounters(scope);\n merged.calls += part.calls;\n merged.errors += part.errors;\n merged.timeouts += part.timeouts;\n merged.totalDurationMs += part.totalDurationMs;\n merged.maxDurationMs = Math.max(merged.maxDurationMs, part.maxDurationMs);\n for (const [route, r] of Object.entries(part.byRoute)) {\n const into = (merged.byRoute[route] ??= { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0 });\n into.calls += r.calls;\n into.errors += r.errors;\n into.timeouts += r.timeouts;\n into.totalDurationMs += r.totalDurationMs;\n into.maxDurationMs = Math.max(into.maxDurationMs, r.maxDurationMs);\n }\n }\n return merged;\n}\n\n/**\n * Test-only reset — the counters are process-lifetime cumulative by design.\n *\n * Clears the audit buffer too: both are per-workspace seam state written by the same\n * `recordGatewayCall`, so resetting one and leaving the other would let a test's assertions\n * about \"this tenant's calls\" read entries from the previous test.\n */\nexport function __resetGatewaySeamCountersForTest(): void {\n seamCountersByWorkspace.clear();\n auditBufferByWorkspace.clear();\n}\n\n/**\n * Append one recorded call to its workspace's buffer, stamping the ordering `seq`.\n *\n * The seq stamp lives here, with the store, rather than at the caller: it is the store's\n * ordering key and nothing outside should be able to mint one out of order.\n */\nexport function appendAuditEntry(entry: Omit<AuditEntry, \"seq\">): void {\n const stored: AuditEntry = { ...entry, seq: nextAuditSeq++ };\n const bucket = getOrCreateWorkspaceAuditBuffer(stored.workspace);\n bucket.push(stored);\n if (bucket.length > AUDIT_BUFFER_SIZE) bucket.shift();\n}\n\nexport function recordSeamCounters(\n workspace: string,\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n timedOut: boolean,\n): void {\n const seamCounters = getOrCreateWorkspaceCounters(workspace);\n const route = (seamCounters.byRoute[fn] ??= {\n calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0,\n });\n\n seamCounters.calls += 1;\n seamCounters.totalDurationMs += durationMs;\n if (durationMs > seamCounters.maxDurationMs) seamCounters.maxDurationMs = durationMs;\n route.calls += 1;\n route.totalDurationMs += durationMs;\n if (durationMs > route.maxDurationMs) route.maxDurationMs = durationMs;\n\n if (status === \"error\") {\n seamCounters.errors += 1;\n route.errors += 1;\n if (timedOut) {\n seamCounters.timeouts += 1;\n route.timeouts += 1;\n }\n }\n}\n/**\n * Human-readable seam summary for `health action=audit` (WP-575 element 5).\n *\n * Lives here, with the counters, rather than in the tool: the tool renders an audit view,\n * this module decides what the numbers MEAN — notably that a timeout gets its own line\n * instead of being folded into the error rate, because it is the failure class where the\n * server may actually have SUCCEEDED.\n */\nexport function formatGatewaySeamSummary(seam: GatewaySeamCounters): string {\n const lines = [\n \"\\n\\n---\\n\\n# Gateway seam (process lifetime)\\n\",\n `Calls: ${seam.calls} \\u2014 errors: ${seam.errors}, of which timeouts: ${seam.timeouts}`,\n ];\n if (seam.calls > 0) {\n lines.push(`Mean: ${Math.round(seam.totalDurationMs / seam.calls)}ms \\u2014 slowest: ${seam.maxDurationMs}ms`);\n }\n if (seam.timeouts > 0) {\n lines.push(`\\u26a0 ${seam.timeouts} call(s) hit their latency budget \\u2014 those outcomes are unknown, not failed.`);\n }\n return lines.join(\"\\n\");\n}\n","/**\n * The gateway seam — what happened on a `/api/aki` call, as opposed to how it was made.\n *\n * Split out of `./client.ts` during WP-575 (TEN-2917) at the 500-LOC ratchet's insistence\n * (STD-2 / DEC-1504), and the ratchet was reading the design correctly: client.ts's job is\n * to MAKE the call — resolve the deployment, attach auth, parse the envelope, cache reads.\n * Recording what the call cost, classifying how it failed, and deciding whether the caller\n * may safely retry are a different job with a different reason to change. Everything here\n * is downstream-of-the-response; nothing here knows how to send one.\n *\n * Deliberately free of any dependency on client.ts, so there is no import cycle: the two\n * pieces of per-call context this module cannot derive (the resolved workspace and the\n * active tool context) are PASSED IN by the caller rather than reached for.\n *\n * `./client.ts` re-exports this module's public surface, so existing importers (and the\n * test mocks that enumerate client.js's exports) keep working unchanged.\n *\n * What a tenant's calls ADD UP TO — the per-workspace audit buffer and the rolling counters —\n * moved to ./lib/gatewaySeamStore.ts in the PR #533 review round (STD-2 / DEC-1504 again:\n * classifying one failure and remembering a tenant's history are different jobs). Its surface\n * is re-exported below so this module stays the single import point for the seam.\n */\nimport { trackToolCall } from \"./analytics.js\";\nimport { ROUTE_TYPE_BY_NAME } from \"./generated/routeLatencyBudget.generated.js\";\nimport { appendAuditEntry, recordSeamCounters, type AuditEntry } from \"./lib/gatewaySeamStore.js\";\n\nexport {\n formatGatewaySeamSummary,\n getAuditLog,\n getGatewaySeamCounters,\n getMergedGatewaySeamCounters,\n __resetGatewaySeamCountersForTest,\n type AuditEntry,\n type GatewaySeamCounters,\n} from \"./lib/gatewaySeamStore.js\";\n\n/** Convex `/api/aki` error body — 4xx/5xx include `error`; structured codes include `code`. */\nexport class KernelCallError extends Error {\n readonly status: number;\n readonly code?: string;\n /** WP-316 S1a: Structured commit validation — required field keys missing from entry.data. */\n readonly missingRequiredFields?: string[];\n /** WP-316 S1a: Structured commit validation — field-level data errors. */\n readonly fieldErrors?: string[];\n /**\n * WP-465 slice ⑤: structured diagnostics carried by an `ok:false` kernel envelope\n * (e.g. `coherencyRefusals`, `blockers`). The gateway forwards these verbatim at\n * HTTP 200; without preserving them here a refused/blocked envelope would collapse\n * into a bare code+message and the caller could not surface the per-offender routes.\n */\n readonly diagnostics?: Record<string, unknown>;\n constructor(\n message: string,\n status: number,\n code?: string,\n missingRequiredFields?: string[],\n fieldErrors?: string[],\n diagnostics?: Record<string, unknown>,\n ) {\n super(message);\n this.name = \"KernelCallError\";\n this.status = status;\n this.code = code;\n this.missingRequiredFields = missingRequiredFields;\n this.fieldErrors = fieldErrors;\n this.diagnostics = diagnostics;\n }\n}\n\n/**\n * A gateway call that hit its declared latency budget (WP-575, TEN-2917).\n *\n * WHY THIS IS ITS OWN TYPE and not folded into the generic network error client.ts used to\n * throw for every `fetch` rejection: a budget abort and a connection refusal are opposite\n * facts about the server, and the caller has to be able to tell them apart. A refusal proves\n * nothing ran. A budget abort proves only that WE stopped waiting — the server may well have\n * completed the write. Collapsing both into \"network error\" is what let MCP report a hard\n * failure on `chain.createEntry` calls that had SUCCEEDED, and left callers with no way to\n * say so. `mayHaveLanded` carries that distinction structurally so a caller can surface a\n * partial-success signal instead of a bare failure (see tools/smart-capture.ts).\n *\n * The `name` below is a LOAD-BEARING contract, pinned by a test: smart-capture discriminates\n * on it rather than `instanceof`, because class identity survives neither a mocked module\n * nor module duplication by a bundler.\n */\nexport class GatewayTimeoutError extends Error {\n /** Gateway route name that was aborted. */\n readonly fn: string;\n /** The declared budget, in ms, that this route was given (see routeLatencyBudget). */\n readonly budgetMs: number;\n /** Wall-clock ms actually spent before the abort. */\n readonly elapsedMs: number;\n /**\n * True when the aborted call could have landed a write server-side despite this client\n * giving up. Derived by `mayHaveLandedOnTimeout` from the route contract's own function\n * type (`mutation`/`action` mutate; `query` does not) AND the call's own arguments (a\n * `preview: true` dry run writes nothing) — never guessed from the name.\n */\n readonly mayHaveLanded: boolean;\n constructor(fn: string, budgetMs: number, elapsedMs: number, mayHaveLanded: boolean) {\n super(\n `MCP call \"${fn}\" exceeded its ${budgetMs}ms latency budget (waited ${elapsedMs}ms).` +\n (mayHaveLanded\n ? \" This route writes, so the server may have completed it — verify before retrying.\"\n : \"\"),\n );\n this.name = \"GatewayTimeoutError\";\n this.fn = fn;\n this.budgetMs = budgetMs;\n this.elapsedMs = elapsedMs;\n this.mayHaveLanded = mayHaveLanded;\n }\n}\n\n/**\n * Does this route mutate state? (WP-575)\n *\n * Read off the route contract's own Convex function type — `mutation` and `action` can\n * write, `query` cannot — never inferred from the route's NAME. Name-based guessing is how\n * client.ts's read-cache `isWrite` has to work (it predates the typed contract and covers\n * cache invalidation, where over-invalidating is merely wasteful), but the stakes here are\n * different: this decides whether the agent is told a timed-out write may have landed.\n * Telling it \"nothing landed\" when something did is the worse error, so an UNKNOWN route\n * fails safe to `true` — same direction as the budget's own unknown-name fallback.\n */\nexport function routeMayMutate(fn: string): boolean {\n const type = ROUTE_TYPE_BY_NAME[fn];\n return type === undefined || type !== \"query\";\n}\n\n/**\n * Was this call a DRY RUN? (PR #533 review, Codex P2)\n *\n * `preview: true` is a gateway-wide contract meaning \"run every validation, write nothing\" —\n * not a per-route convenience. Every registered route that accepts the argument returns before\n * its first write, verified one by one: `chain.createEntry`\n * (convex/agentKnowledge/entries.ts:498, returning at :583-599 with the first write at :601 —\n * and its action wrapper skips the contradiction detector entirely at :915),\n * `chain.commitEntry` (:2537, before createPublishedVersion/createProposalForEntry/\n * recordSessionActivity), `chain.createEntryRelation`\n * (convex/agentKnowledge/relations.ts:152 and :186, before any insert or scheduler), and\n * `quality.evaluateHeuristicAndSchedule` (convex/intelligence/qualityCoaching.ts:1101,\n * documented \"NEVER persist, schedule, or stamp\", before the insert at :1110).\n */\nexport function isDryRunCall(args: unknown): boolean {\n return (args as { preview?: unknown } | null | undefined)?.preview === true;\n}\n\n/**\n * Could a timed-out call have LANDED A WRITE? (PR #533 review, Codex P2)\n *\n * The route's type answers \"can this route write at all\"; the call's own arguments answer\n * \"was this particular call asking it to\". Both are needed, and deciding it here — once,\n * where both are in scope — is what keeps the four surfaces that report a timeout consistent:\n * `GatewayTimeoutError`'s message, lib/captureTimeoutOutcome.ts, lib/batchTimeoutCohort.ts,\n * and the batch-preview markdown in lib/batchCaptureOutput.ts. Deriving it from the type\n * alone told a dry run its write \"may have completed\" and warned against retrying — beside\n * that same response's own \"no DB writes\" line.\n *\n * The fail-safe direction is unchanged for everything else: an unknown route still mutates\n * as far as we know (`routeMayMutate`), because claiming \"nothing landed\" when something did\n * is the worse error. A dry run is the one case where \"nothing landed\" is a CONTRACT, not a\n * guess — see `isDryRunCall` for the route-by-route verification.\n */\nexport function mayHaveLandedOnTimeout(fn: string, args: unknown): boolean {\n return routeMayMutate(fn) && !isDryRunCall(args);\n}\n\n/**\n * Classify a failed gateway call and throw. Shared by BOTH phases a call can fail in — the\n * headers phase (`fetch` rejects) and the body phase (`res.json()` rejects while the abort\n * signal is still live, PR #533 review Codex P2) — because the phase changes what happened,\n * not what it MEANS.\n *\n * WP-575: a budget abort and a connection failure are opposite facts, and only the former\n * leaves the server's own outcome unknown. `AbortSignal.timeout` rejects with a DOMException\n * named \"TimeoutError\"; a caller-driven abort surfaces as \"AbortError\". Treat both as \"we\n * stopped waiting\", never as \"the server failed.\"\n *\n * `record` is injected rather than called directly so this module keeps owing nothing to\n * client.ts (see the file header) — the caller supplies the workspace/tool context.\n *\n * `args` is taken rather than a pre-computed `mayHaveLanded` so the derivation stays inside\n * this module (`mayHaveLandedOnTimeout`): two call sites computing it themselves is how the\n * two connectors drifted apart in the first place.\n */\nexport function throwClassifiedGatewayFailure(\n err: any,\n fn: string,\n args: unknown,\n budgetMs: number,\n elapsedMs: number,\n phase: \"network\" | \"response body\",\n record: (auditMsg: string, timedOut: boolean) => void,\n): never {\n if (err?.name === \"TimeoutError\" || err?.name === \"AbortError\") {\n const timeoutErr = new GatewayTimeoutError(fn, budgetMs, elapsedMs, mayHaveLandedOnTimeout(fn, args));\n record(timeoutErr.message, true);\n throw timeoutErr;\n }\n const detail = err?.message ?? String(err);\n record(phase === \"network\" ? detail : `${phase}: ${detail}`, false);\n throw new Error(`MCP call \"${fn}\" ${phase} error: ${detail}`);\n}\n\n\n/**\n * What an unreadable response BODY means — and it depends on the status.\n *\n * `fetch` resolves at headers while the budget signal is still live, so a stalled body aborts\n * after the call already succeeded at the transport level. Two different situations land here\n * and they deserve opposite answers (PR #533 review round 6):\n *\n * - **Status was OK** — a 2xx whose body never arrived. The server's outcome is genuinely\n * unknown, which is the timeout this work package exists to name. Classify and throw.\n * - **Status was NOT OK** — the server reached a verdict and reported it; only its error body\n * stalled. Calling that a budget timeout trades a real status and code (a 429's\n * `retryAfterSeconds`, a 4xx's validation code) for \"the server may have completed it\n * anyway, verify before retrying\" — about a call the server explicitly failed. Yield an\n * empty body instead and let the caller's own status branch report it, which is what\n * non-OK responses have always done.\n *\n * Lives here rather than in the caller's catch because this is the taxonomy's question, not\n * the call-maker's: client.ts sends requests, this module decides what a failure MEANT.\n */\nexport function emptyBodyOrThrowClassified<T>(\n err: any,\n fn: string,\n args: unknown,\n budgetMs: number,\n elapsedMs: number,\n statusOk: boolean,\n record: (auditMsg: string, timedOut: boolean) => void,\n): T {\n if (statusOk) throwClassifiedGatewayFailure(err, fn, args, budgetMs, elapsedMs, \"response body\", record);\n return {} as T;\n}\n\n// ─── Recording one call ───────────────────────────────────────────────\n\nfunction shouldLogAudit(status: \"ok\" | \"error\"): boolean {\n return status === \"error\" || process.env.MCP_DEBUG === \"1\";\n}\n\n/**\n * Record one completed gateway call: buffer entry, cumulative counters, PostHog, stderr.\n *\n * `workspace` and `toolContext` are parameters rather than module-level lookups precisely so\n * this module owes nothing to client.ts — see the file header.\n */\nexport function recordGatewayCall(params: {\n fn: string;\n status: \"ok\" | \"error\";\n durationMs: number;\n workspace: string;\n errorMsg?: string;\n toolContext?: { tool: string; action?: string } | null;\n /** WP-575: the budget this call was judged by, and whether it was what killed it. */\n budgetMs?: number;\n timedOut?: boolean;\n}): void {\n const { fn, status, durationMs, workspace, errorMsg, toolContext, budgetMs, timedOut } = params;\n const ts = new Date().toISOString();\n\n const entry: Omit<AuditEntry, \"seq\"> = { ts, fn, workspace, status, durationMs };\n if (errorMsg) entry.error = errorMsg;\n if (toolContext) entry.toolContext = toolContext;\n if (budgetMs !== undefined) entry.budgetMs = budgetMs;\n if (timedOut) entry.timedOut = true;\n appendAuditEntry(entry);\n\n recordSeamCounters(workspace, fn, status, durationMs, timedOut === true);\n trackToolCall(fn, status, durationMs, workspace, errorMsg);\n\n if (!shouldLogAudit(status)) return;\n\n const base =\n `[MCP-AUDIT] ${ts} fn=${fn} workspace=${workspace} status=${status} duration=${durationMs}ms` +\n `${budgetMs !== undefined ? ` budget=${budgetMs}ms` : \"\"}${timedOut ? \" timedOut=true\" : \"\"}`;\n process.stderr.write(\n status === \"error\" && errorMsg ? `${base} error=${JSON.stringify(errorMsg)}\\n` : `${base}\\n`,\n );\n}\n\n","/**\n * MCP client — communicates with the Convex HTTP Action gateway.\n *\n * Dual mode:\n * stdio — single user, API key from env, module-level state\n * http — multi-user, API key from AsyncLocalStorage, per-key state\n *\n * Configuration:\n * PRODUCTBRAIN_API_KEY — pb_sk_* key (stdio mode; http mode gets it per-request)\n * CONVEX_SITE_URL — (optional) Convex deployment URL, defaults to cloud\n */\n\nimport type { GatewayRouteReturnByName } from \"@productbrain/kernel-client\";\n// WP-575 (TEN-2917): budget DECLARED at the route contract, delivered as a drift-checked copy.\nimport { latencyBudgetMsForRoute } from \"./generated/routeLatencyBudget.generated.js\";\n// WP-575 / STD-2: what a call COST and how it FAILED lives in ./gatewaySeam.ts — this file\n// makes the call, that one records it. Re-exported below so existing importers still resolve.\nimport { KernelCallError, emptyBodyOrThrowClassified, recordGatewayCall, throwClassifiedGatewayFailure } from \"./gatewaySeam.js\"; // GatewayTimeoutError/routeMayMutate deliberately absent: re-exported below (needs no import) and matched by `name`, respectively (review round 3).\nexport {\n GatewayTimeoutError,\n KernelCallError,\n getAuditLog,\n getGatewaySeamCounters,\n formatGatewaySeamSummary,\n __resetGatewaySeamCountersForTest,\n type AuditEntry,\n type GatewaySeamCounters,\n} from \"./gatewaySeam.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { trackCompoundToolAction } from \"./analytics.js\";\nimport { getRequestApiKey, getRequestMcpSessionId, getKeyState, hashKey, type KeyState } from \"./auth.js\";\nimport type { NextAction } from \"./envelope.js\";\nimport { MCP_NPX_PACKAGE } from \"./cli/config-writer.js\";\nimport { warnOnProdFallthrough } from \"./prod-fallthrough.js\";\nimport { resolveConversationId } from \"./lib/conversation.js\";\nimport type { AgentSessionStartNotice } from \"./lib/sessionNotices.js\"; // WP-584 offset (STD-2/DEC-1504)\nimport { recordToolAction } from \"./lib/toolActionCounts.js\"; // WP-584 offset (STD-2/DEC-1504)\nimport { parseFallbackUrls, probeDeploymentCandidates } from \"./lib/deploymentUrlResolver.js\"; // WP-575 offset (STD-2/DEC-1504)\n\n// ─── Conversation identity (WP-479 E2) ─────────────────────────────────\n\n// The MCP server is a subprocess of the harness (Claude Code / Cursor / Codex) — it resolves its\n// conversation identity ONCE at process startup from its own env and holds it for the process\n// lifetime (there is no per-request or per-key variation: one MCP server process is one\n// conversation). `undefined` means \"not yet resolved\"; `null` is a legitimate resolved value\n// (no identity signal present — legacy behavior).\nlet _conversationId: string | null | undefined;\n\nexport function getConversationId(): string | null {\n // In HTTP transport mode a single process multiplexes many `Mcp-Session-Id` streams. The\n // env-derived conversation id is process-global, so it can't identify a stream — and returning null\n // (an earlier fix) is worse: `startSession`'s null-conversation reuse then collapses every HTTP\n // stream sharing an API key onto ONE server `agentSessions` row (Codex P1). The `Mcp-Session-Id` IS\n // the stable per-stream identity, so use it as the conversation id for HTTP starts/recovery — each\n // stream becomes its own server-side conversation. STDIO (one process = one conversation) keeps the\n // env-derived id.\n const mcpSid = getRequestMcpSessionId();\n if (mcpSid) return `mcp:${mcpSid}`;\n if (getRequestApiKey()) return null; // HTTP request before a session id is assigned (initialize)\n if (_conversationId === undefined) _conversationId = resolveConversationId();\n return _conversationId;\n}\n\n// ─── Tool Context (for audit action logging) ─────────────────────────────\n\nconst toolContextStore = new AsyncLocalStorage<{ tool: string; action?: string }>();\n\n/**\n * Run a callback with tool context for audit logging.\n * Compound tools should wrap their handler with this so workspace action=audit can distinguish\n * e.g. entries action=get from entries action=search.\n */\nexport function runWithToolContext<T>(\n ctx: { tool: string; action?: string },\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n recordToolAction(ctx.tool, ctx.action);\n trackCompoundToolAction(ctx.tool, ctx.action, state().workspaceId ?? \"unresolved\");\n return toolContextStore.run(ctx, fn);\n}\n\nfunction getToolContext(): { tool: string; action?: string } | null {\n return toolContextStore.getStore() ?? null;\n}\n\nexport const DEFAULT_CLOUD_URL = \"https://gateway.productbrain.io\";\n\n// ─── Read Cache (Batch A: sub-200ms repeat calls) ─────────────────────\n\nconst CACHE_TTL_MS = 60_000; // 60s per plan\nconst CACHEABLE_FNS = [\n \"chain.getOrientEntries\",\n \"chain.gatherContext\",\n \"chain.graphGatherContext\",\n \"chain.taskAwareGatherContext\",\n \"chain.journeyAwareGatherContext\",\n \"chain.assembleBuildContext\",\n] as const;\n\nfunction isCacheable(fn: string): boolean {\n return (CACHEABLE_FNS as readonly string[]).includes(fn);\n}\n\n// PR #405 review (Codex P2): `chain.evaluateCoherence` (read-only) fell through this pattern\n// (no `evaluate` verb recognized), misclassifying every successful coherence check on the\n// orient/start/wrapup hot path as a write and discarding cacheable results for no reason.\n// PR #517 round 1 (Codex P2): same fix for `chain.shapeAdvisories`/`chain.showShapeAdvisory`.\n// PR #519 review round 3 (Codex P2): `chain.shapeAdvisorySummary` (orientShapeAdvisorySummary,\n// read-only) fell through this same gap — a successful read was misclassified as a write,\n// clearing the shared 60s orient/context cache for every workspace in the process.\nconst READ_PATTERN =\n /^(chain\\.(get|list|search|batchGet|gather|graph|task|journey|assemble|workspace|score|absence|evaluate|shapeAdvisories|showShapeAdvisory|shapeAdvisorySummary)|chainwork\\.(get|list|score)|maps\\.(get|list)|gitchain\\.(get|list|diff|history|runGate))/i;\n\nfunction isWrite(fn: string): boolean {\n if (fn.startsWith(\"agent.\")) return false;\n return !READ_PATTERN.test(fn);\n}\n\ninterface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nconst readCache = new Map<string, CacheEntry<unknown>>();\n\nfunction cacheKey(fn: string, args: Record<string, unknown>): string {\n return `${fn}:${JSON.stringify(args)}`;\n}\n\nfunction getCached<T>(fn: string, args: Record<string, unknown>): T | undefined {\n if (!isCacheable(fn)) return undefined;\n const key = cacheKey(fn, args);\n const entry = readCache.get(key) as CacheEntry<T> | undefined;\n if (!entry || Date.now() > entry.expiresAt) {\n if (entry) readCache.delete(key);\n return undefined;\n }\n return entry.data;\n}\n\nfunction setCached<T>(fn: string, args: Record<string, unknown>, data: T): void {\n if (!isCacheable(fn)) return;\n const key = cacheKey(fn, args);\n readCache.set(key, { data, expiresAt: Date.now() + CACHE_TTL_MS });\n}\n\nfunction invalidateReadCache(): void {\n readCache.clear();\n}\n\n// ─── State Management ─────────────────────────────────────────────────\n\nconst _stdioState: KeyState = {\n workspaceId: null,\n workspaceSlug: null,\n workspaceName: null,\n workspaceCreatedAt: null,\n workspaceGovernanceMode: null,\n agentSessionId: null,\n apiKeyId: null,\n apiKeyScope: \"readwrite\",\n sessionOriented: false,\n sessionClosed: false,\n lastAccess: 0,\n deploymentUrl: null,\n};\n\n/**\n * Returns the active client state.\n * stdio: module-level singleton. http: per-API-key state from AsyncLocalStorage.\n */\nfunction state(): KeyState {\n const reqKey = getRequestApiKey();\n if (reqKey) return getKeyState(reqKey);\n return _stdioState;\n}\n\n/**\n * Cache partition key for the current request.\n * http — hashed API key (per-workspace isolation; hashKey is already used for\n * session binding in auth.ts, so the raw secret is never used as a map key).\n * stdio — fixed \"stdio\" sentinel (single user per process).\n *\n * The OAuth access_token IS the permanent pb_sk_ API key (stable per workspace\n * across refreshes — TEN-1143), so this key never churns mid-session.\n * Every per-workspace cache (collectionCache, smart-capture profile/hub) keys on this.\n */\nexport function cacheScope(): string {\n const key = getRequestApiKey();\n return key ? hashKey(key) : \"stdio\";\n}\n\n/**\n * Returns the active API key (request-scoped in HTTP mode, env in stdio mode).\n */\nfunction getActiveApiKey(): string {\n const fromRequest = getRequestApiKey();\n if (fromRequest) return fromRequest;\n const fromEnv = process.env.PRODUCTBRAIN_API_KEY;\n if (!fromEnv) throw new Error(\"No API key available — set PRODUCTBRAIN_API_KEY or provide Bearer token\");\n return fromEnv;\n}\n\n// ─── Agent Session State ──────────────────────────────────────────────\n\n/**\n * WP-479 review fix (Codex re-review): the SESSION-LIFECYCLE fields (active agentSessionId +\n * oriented/closed flags) must be isolated per HTTP `Mcp-Session-Id`, not shared across a key's\n * concurrent streams — otherwise two streams sharing a key overwrite each other's active session and\n * later orient/write calls use the sibling's session. Workspace + cache state legitimately stays\n * per-API-key on `state()`; only these three fields move to a per-stream store. In STDIO mode (or the\n * pre-session initialize request) there is no Mcp-Session-Id, so they stay on the singleton/per-key\n * `state()` — one process/one key is one session there.\n */\ninterface AgentSessionLifecycle {\n agentSessionId: string | null;\n sessionOriented: boolean;\n sessionClosed: boolean;\n}\nconst _sessionLifecycleByStream = new Map<string, AgentSessionLifecycle>();\nconst MAX_SESSION_STREAMS = 500;\n\nfunction sessionLifecycle(): AgentSessionLifecycle {\n const mcpSid = getRequestMcpSessionId();\n if (!mcpSid) return state(); // STDIO / pre-session — the per-key/singleton state carries them\n const key = `${cacheScope()}:${mcpSid}`;\n let lc = _sessionLifecycleByStream.get(key);\n if (!lc) {\n // Bound memory: a terminated stream's entry is stale; FIFO-evict the oldest when full.\n if (_sessionLifecycleByStream.size >= MAX_SESSION_STREAMS) {\n const oldest = _sessionLifecycleByStream.keys().next().value;\n if (oldest !== undefined) _sessionLifecycleByStream.delete(oldest);\n }\n lc = { agentSessionId: null, sessionOriented: false, sessionClosed: false };\n _sessionLifecycleByStream.set(key, lc);\n }\n return lc;\n}\n\nexport function getAgentSessionId(): string | null {\n return sessionLifecycle().agentSessionId;\n}\n\nexport function isSessionOriented(): boolean {\n return sessionLifecycle().sessionOriented;\n}\n\nexport function setSessionOriented(value: boolean): void {\n sessionLifecycle().sessionOriented = value;\n}\n\nexport function getApiKeyScope(): \"read\" | \"readwrite\" {\n return state().apiKeyScope;\n}\n\nexport function isSessionClosed(): boolean {\n return sessionLifecycle().sessionClosed;\n}\n\nexport interface AgentSessionStartResult {\n sessionId: string;\n initiatedBy: string;\n toolsScope: \"read\" | \"readwrite\";\n workspaceName: string;\n feedbackQueueNew?: number; feedbackOldestNewAt?: number;\n notices?: AgentSessionStartNotice[];\n adoptDiscoveryHint?: string; // WP-638 S4 §10.3 — \"3\" or, at the scan cap, \"20+\".\n}\n\n/**\n * Start an agent session. Creates a session record in Convex.\n * toolsScope is derived server-side from the API key — not passed as a parameter.\n * WP-479 E2: concurrent sessions on one key are expected, not collapsed — an existing active\n * session is never superseded; per-key quota is enforced instead (TEN-2570).\n */\nexport async function startAgentSession(): Promise<AgentSessionStartResult> {\n const workspaceId = await getWorkspaceId();\n const s = state();\n if (!s.apiKeyId) {\n throw new Error(\"Cannot start session: API key ID not resolved. Ensure workspace resolution completed.\");\n }\n\n const result = await kernelCall<GatewayRouteReturnByName<\"agent.startSession\">>(\"agent.startSession\", {\n workspaceId,\n apiKeyId: s.apiKeyId,\n clientKind: \"mcp\",\n // WP-479 E2: resolved once at process startup and held — see getConversationId() above.\n conversationId: getConversationId() ?? undefined,\n });\n\n // Session-lifecycle fields are per-Mcp-Session-Id (Codex re-review); apiKeyScope stays per-key.\n const lc = sessionLifecycle();\n if (lc.agentSessionId) {\n resetTouchThrottle(lc.agentSessionId);\n }\n lc.agentSessionId = result.sessionId;\n s.apiKeyScope = result.toolsScope;\n lc.sessionOriented = false;\n lc.sessionClosed = false;\n resetTouchThrottle(result.sessionId);\n\n return result;\n}\n\n/**\n * Close the current agent session. After this, write tools are blocked\n * even if the MCP connection stays open.\n */\nexport async function closeAgentSession(): Promise<void> {\n const lc = sessionLifecycle();\n if (!lc.agentSessionId) return;\n const sessionId = lc.agentSessionId;\n try {\n await kernelCall<GatewayRouteReturnByName<\"agent.closeSession\">>(\"agent.closeSession\", {\n sessionId,\n status: \"closed\",\n });\n } finally {\n resetTouchThrottle(sessionId);\n lc.sessionClosed = true;\n lc.agentSessionId = null;\n lc.sessionOriented = false;\n }\n}\n\n/**\n * Mark current session as orphaned (used on disconnect/crash).\n */\nexport async function orphanAgentSession(): Promise<void> {\n const lc = sessionLifecycle();\n if (!lc.agentSessionId) return;\n const sessionId = lc.agentSessionId;\n try {\n await kernelCall<GatewayRouteReturnByName<\"agent.closeSession\">>(\"agent.closeSession\", {\n sessionId,\n status: \"orphaned\",\n });\n } catch {\n // Best-effort on disconnect\n } finally {\n resetTouchThrottle(sessionId);\n lc.agentSessionId = null;\n lc.sessionOriented = false;\n }\n}\n\n/**\n * Touch the session to update lastToolCallAt. Fire-and-forget.\n * Throttled to at most once per 5s to prevent OCC conflicts when\n * multiple tool calls complete in parallel.\n */\nconst _lastTouchAtBySession = new Map<string, number>();\nconst TOUCH_THROTTLE_MS = 5_000;\n\nexport function touchSessionActivity(): void {\n const sessionId = sessionLifecycle().agentSessionId;\n if (!sessionId) return;\n\n const now = Date.now();\n const lastTouchAt = _lastTouchAtBySession.get(sessionId) ?? 0;\n if (now - lastTouchAt < TOUCH_THROTTLE_MS) return;\n _lastTouchAtBySession.set(sessionId, now);\n\n kernelCall<GatewayRouteReturnByName<\"agent.touchSession\">>(\"agent.touchSession\", {\n sessionId,\n }).catch(() => {});\n}\n\nexport function resetTouchThrottle(sessionId?: string | null): void {\n if (sessionId) {\n _lastTouchAtBySession.delete(sessionId);\n return;\n }\n _lastTouchAtBySession.clear();\n}\n\n/**\n * Record structured activity on the current session.\n */\nexport async function recordSessionActivity(activity: {\n entryCreated?: string;\n entryModified?: string;\n relationCreated?: boolean;\n gateFailure?: boolean;\n contradictionWarning?: boolean;\n strategyLinkWarnedForEntryId?: string;\n}): Promise<void> {\n const sessionId = sessionLifecycle().agentSessionId;\n if (!sessionId) return;\n try {\n await kernelCall<GatewayRouteReturnByName<\"agent.recordActivity\">>(\"agent.recordActivity\", {\n sessionId,\n ...activity,\n });\n } catch {\n // Non-critical — don't fail the tool call over activity tracking\n }\n}\n\n// ─── Audit ────────────────────────────────────────────────────────────\n\n/**\n * Bootstrap for stdio mode: set CONVEX_SITE_URL default and warn on missing key.\n * API key is validated lazily on first kernelCall so the server can start and handle\n * signals (SIGTERM) even before credentials are provided (e.g. in tests).\n */\nexport function bootstrap(): void {\n const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;\n process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;\n warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });\n const pbKey = process.env.PRODUCTBRAIN_API_KEY;\n if (!pbKey?.startsWith(\"pb_sk_\")) {\n process.stderr.write(\n \"[MCP] Warning: PRODUCTBRAIN_API_KEY is not set or invalid. \" +\n \"Tool calls will fail until a valid key is provided.\\n\"\n );\n }\n}\n\n/**\n * Bootstrap for HTTP mode: only set CONVEX_SITE_URL.\n * API key validation happens per-request via Bearer token.\n */\nexport function bootstrapHttp(): void {\n const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;\n process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;\n warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });\n}\n\n/** @deprecated Use bootstrap() instead. Alias kept for callers in transition. */\nexport const bootstrapCloudMode = bootstrap;\n\nfunction getEnv(key: string): string {\n const value = process.env[key];\n if (!value) throw new Error(`${key} environment variable is required`);\n return value;\n}\n\n/**\n * DEC-789 S2: Resolve the Convex deployment URL for the active API key.\n *\n * Warm path (normal): key-check already ran during OAuth authorize, so\n * state().deploymentUrl is set — return it immediately.\n *\n * No-fallback path (default / single-deployment): when CONVEX_FALLBACK_URLS\n * is not set, return CONVEX_SITE_URL directly without probing. This preserves\n * the original single-URL behavior and avoids extra fetch calls in tests and\n * stdio mode.\n *\n * Cold-start path (after Railway restart with CONVEX_FALLBACK_URLS configured):\n * keyStateMap was wiped; probe candidate URLs (lib/deploymentUrlResolver.ts) in order until\n * one responds ok:true to /api/key-check, store the result so subsequent calls are instant.\n *\n * Must be called from within a runWithAuth context (state() and\n * getActiveApiKey() both require it).\n */\nasync function resolveDeploymentUrl(): Promise<string> {\n const s = state();\n if (s.deploymentUrl) return s.deploymentUrl;\n\n const primaryUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_CLOUD_URL).replace(/\\/$/, \"\");\n const fallbacks = parseFallbackUrls(process.env.CONVEX_FALLBACK_URLS);\n\n // No fallbacks configured — single-deployment setup, use primary directly.\n // This is the common case: preserves the original behavior, no extra fetch calls.\n if (fallbacks.length === 0) {\n return primaryUrl;\n }\n\n // Multi-deployment cold-start: probe candidates in order.\n const candidates = [primaryUrl, ...fallbacks.map((u) => u.replace(/\\/$/, \"\"))];\n\n let apiKey: string;\n try {\n apiKey = getActiveApiKey();\n } catch {\n // No API key available — return primary and let the tool call fail normally.\n return primaryUrl;\n }\n\n const found = await probeDeploymentCandidates(candidates, apiKey);\n if (found) {\n s.deploymentUrl = found;\n return found;\n }\n\n // All probes failed — return the first candidate and let the tool call fail with its normal error.\n return candidates[0];\n}\n\n/**\n * Thin adapter over ./gatewaySeam.ts's `recordGatewayCall` — supplies the two pieces of\n * per-call context that module deliberately does not reach for (see its header).\n *\n * PR #533 review, Codex P2: the workspace fallback used to be the literal string\n * \"unresolved\" — a single bucket EVERY caller whose workspace hadn't resolved yet shared. In\n * HTTP mode that's still a cross-tenant collision (two different not-yet-resolved API keys\n * would land in the same \"unresolved\" audit/counters bucket), just a narrower one than the\n * original unfiltered leak. `cacheScope()` already exists for exactly this shape of problem\n * (hashed per-API-key in HTTP mode, a fixed \"stdio\" sentinel in stdio mode — see its own doc\n * comment) and is what collectionCache/smart-capture already partition on, so reuse it here\n * instead of inventing a second per-caller identity scheme.\n */\nfunction audit(\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n errorMsg?: string,\n meta?: { budgetMs?: number; timedOut?: boolean },\n): void {\n recordGatewayCall({\n fn, status, durationMs, errorMsg,\n workspace: state().workspaceId ?? cacheScope(),\n toolContext: getToolContext(),\n budgetMs: meta?.budgetMs,\n timedOut: meta?.timedOut,\n });\n}\n\n// ─── HTTP Client ──────────────────────────────────────────────────────\n\n// NextAction imported from ./envelope.js — local MCP-layer definition (mirrors convex/lib/envelopeContract.ts;\n// BR-113 prevents direct convex/ imports from MCP package, so shape is kept in sync manually)\n\ninterface GatewaySuccessResponse<T> {\n ok: true;\n summary: string;\n data: T;\n next?: NextAction[];\n _meta?: { durationMs?: number };\n}\n\ninterface GatewayErrorResponse {\n ok: false;\n code?: string;\n message?: string;\n error?: string;\n missingRequiredFields?: string[];\n fieldErrors?: unknown[];\n /** WP-465 slice ⑤: structured per-offender diagnostics (coherencyRefusals, blockers, …). */\n diagnostics?: Record<string, unknown>;\n}\n\ntype GatewayResponse<T> = GatewaySuccessResponse<T> | GatewayErrorResponse;\n\n// STD-101: Single SSOT for TOUCH_EXCLUDED — shared by kernelCall (callGateway) and kernelCallEnvelope.\n// Exported for testability — tests assert membership to prevent OCC cascade regressions.\nexport const TOUCH_EXCLUDED = new Set([\n \"agent.touchSession\",\n \"agent.startSession\",\n \"agent.markOriented\",\n \"agent.recordActivity\",\n \"agent.recordWrapup\",\n \"agent.closeSession\",\n // WP-376 α.3: orient byte report is itself a heartbeat-equivalent observability\n // write that already patches the same agentSessions row. A follow-up touchSession\n // would create a redundant second write per orient call (DEC-50 OCC anti-pattern).\n \"agent.reportOrientMetric\",\n]);\n\n/**\n * Private: shared HTTP fetch + error handling for kernelCall and kernelCallEnvelope.\n * Returns the parsed envelope fields without touching the read cache or session.\n */\n/**\n * Connector identity for the gateway's context.served shared seam (DEC-1207). Sent as the\n * `x-pb-source` header so the server-side emit attributes MCP — the dominant agent surface\n * (INS-1706) that emitted nothing under the prior CLI-only seam.\n */\nconst MCP_TELEMETRY_SOURCE = \"mcp\";\n\nasync function callGateway<T>(fn: string, args: Record<string, unknown>): Promise<{\n data: T;\n summary: string;\n next?: NextAction[];\n _meta?: { durationMs?: number };\n}> {\n const siteUrl = await resolveDeploymentUrl();\n const apiKey = getActiveApiKey();\n\n // WP-575 (TEN-2917): the budget is DECLARED at the route contract\n // (packages/kernel-client/src/routeLatencyBudget.ts) and reaches this connector as a\n // drift-checked generated copy. It replaces the blanket `AbortSignal.timeout(10_000)`\n // that used to apply to every route alike — including `chain.createEntry`, whose real\n // 14-20s round-trip meant this client reported hard failures on writes that had SUCCEEDED.\n const budgetMs = latencyBudgetMsForRoute(fn);\n const start = Date.now();\n\n let res: Response;\n try {\n res = await fetch(`${siteUrl}/api/aki`, {\n method: \"POST\",\n signal: AbortSignal.timeout(budgetMs),\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n // DEC-1207: attribute this connector for the gateway's context.served seam. MCP is\n // the dominant agent surface (INS-1706); without this header it was emitting nothing.\n \"x-pb-source\": MCP_TELEMETRY_SOURCE,\n },\n body: JSON.stringify({ fn, args }),\n });\n } catch (err: any) {\n throwClassifiedGatewayFailure(err, fn, args, budgetMs, Date.now() - start, \"network\",\n (m, t) => audit(fn, \"error\", Date.now() - start, m, { budgetMs, timedOut: t }));\n }\n\n // WP-575 / PR #533 review (Codex P2): the abort signal is STILL LIVE while the body streams,\n // and `fetch` resolves at headers — so a slow body can blow the budget HERE, outside the try\n // above, and escape as a raw DOMException nothing classifies. Same treatment, later phase.\n let json: GatewayResponse<T>;\n try {\n json = (await res.json()) as GatewayResponse<T>;\n } catch (err: any) {\n json = emptyBodyOrThrowClassified<GatewayResponse<T>>(err, fn, args, budgetMs, Date.now() - start, res.ok,\n (m, t) => audit(fn, \"error\", Date.now() - start, m, { budgetMs, timedOut: t }));\n }\n\n if (!res.ok || json.ok === false) {\n const errJson = json as GatewayErrorResponse;\n const msg = errJson.error ?? errJson.message ?? \"unknown error\";\n audit(fn, \"error\", Date.now() - start, errJson.code ? `${msg} [${errJson.code}]` : msg, { budgetMs });\n throw new KernelCallError(\n `MCP call \"${fn}\" failed (${res.status}): ${msg}`,\n res.status,\n errJson.code,\n Array.isArray(errJson.missingRequiredFields) ? errJson.missingRequiredFields : undefined,\n Array.isArray(errJson.fieldErrors) ? (errJson.fieldErrors as string[]) : undefined,\n errJson.diagnostics && typeof errJson.diagnostics === \"object\" ? errJson.diagnostics : undefined,\n );\n }\n\n audit(fn, \"ok\", Date.now() - start, undefined, { budgetMs });\n\n const { data, summary, next, _meta } = json as GatewaySuccessResponse<T>;\n return {\n data: data as T,\n summary: summary || fn,\n next,\n _meta,\n };\n}\n\n/**\n * Low-level call to the HTTP Action gateway.\n * Workspace scoping is enforced server-side from the API key — callers\n * don't need to (and can't) override the workspace.\n *\n * Read cache: orient and context-gather responses are cached for 60s.\n * Cache is invalidated on any write (safe-by-default: everything not\n * matching a known read pattern is treated as a write).\n */\nexport async function kernelCall<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const cached = getCached<T>(fn, args);\n if (cached !== undefined) {\n return cached;\n }\n\n const { data } = await callGateway<T>(fn, args);\n\n if (isWrite(fn)) {\n invalidateReadCache();\n } else {\n setCached(fn, args, data);\n }\n\n // Exclude session bookkeeping calls from the heartbeat touch. These mutations\n // already target the active session document, so immediately heartbeating after\n // them only adds avoidable OCC pressure on the same row.\n if (getAgentSessionId() && !TOUCH_EXCLUDED.has(fn)) {\n touchSessionActivity();\n }\n\n return data;\n}\n\n/**\n * Calls the gateway and returns the kernel success envelope\n * ({ ok: true, summary, data, next?, _meta? }) instead of stripping to just data.\n *\n * Use for MCP tools that need thin passthrough — forwarding summary, next actions,\n * or timing metadata to the MCP client.\n *\n * Note: Throws KernelCallError on gateway errors (non-200 responses). Full\n * envelope error passthrough (ok:false at HTTP 200) deferred to WP-321 S6+\n * when DEC-571 HTTP semantics are activated on error paths.\n *\n * Does NOT use the read cache (kernel _meta.durationMs would be stale from cache;\n * thin passthrough tools need fresh timing). Does NOT call invalidateReadCache\n * (read-only by contract).\n */\nexport async function kernelCallEnvelope<T>(\n fn: string,\n args: Record<string, unknown> = {},\n): Promise<{\n ok: true;\n summary: string;\n data: T;\n next?: NextAction[];\n _meta?: { durationMs?: number };\n}> {\n const { data, summary, next, _meta } = await callGateway<T>(fn, args);\n\n if (getAgentSessionId() && !TOUCH_EXCLUDED.has(fn)) {\n touchSessionActivity();\n }\n\n return { ok: true, summary, data, next, _meta };\n}\n\n// ─── Workspace Resolution ─────────────────────────────────────────────\n\nconst resolveInFlightMap = new Map<string, Promise<string>>();\n\nexport async function getWorkspaceId(): Promise<string> {\n const s = state();\n if (s.workspaceId) return s.workspaceId;\n\n const apiKey = getActiveApiKey();\n const existing = resolveInFlightMap.get(apiKey);\n if (existing) return existing;\n\n const promise = resolveWorkspaceWithRetry().finally(() => resolveInFlightMap.delete(apiKey));\n resolveInFlightMap.set(apiKey, promise);\n return promise;\n}\n\nasync function resolveWorkspaceWithRetry(maxRetries = 2): Promise<string> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const workspace = await kernelCall<{\n _id: string;\n name: string;\n slug: string;\n createdAt?: number;\n keyScope?: string;\n keyId?: string;\n governanceMode?: \"open\" | \"consensus\" | \"role\";\n } | null>(\"resolveWorkspace\", {});\n\n if (!workspace) {\n throw new Error(\n \"API key is valid but no workspace is associated. \" +\n `Run \\`npx ${MCP_NPX_PACKAGE} setup\\` or regenerate your key.`\n );\n }\n\n const s = state();\n s.workspaceId = workspace._id;\n s.workspaceSlug = workspace.slug;\n s.workspaceName = workspace.name;\n s.workspaceCreatedAt = workspace.createdAt ?? null;\n s.workspaceGovernanceMode = workspace.governanceMode ?? \"open\";\n if (workspace.keyScope) s.apiKeyScope = workspace.keyScope as \"read\" | \"readwrite\";\n if (workspace.keyId) s.apiKeyId = workspace.keyId;\n return s.workspaceId;\n } catch (err: any) {\n lastError = err;\n // WP-575 / PR #533 review (Codex P2): a budget abort must stay RETRYABLE. This regex\n // matched the OLD \"network error\" text, so the typed error silently dropped the retry\n // on the first call every session makes. It is a query — nothing to duplicate.\n const isTransient =\n err?.name === \"GatewayTimeoutError\" ||\n /network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(err.message);\n if (!isTransient || attempt === maxRetries) break;\n const delay = 1000 * (attempt + 1);\n process.stderr.write(\n `[MCP] Workspace resolution failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms...\\n`\n );\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n throw lastError!;\n}\n\nexport interface WorkspaceContext {\n workspaceId: string;\n workspaceSlug: string;\n workspaceName: string;\n createdAt: number | null;\n /** BET-76 FEAT-111: Cached from workspace resolution. Defaults to 'open'. */\n governanceMode: \"open\" | \"consensus\" | \"role\";\n}\n\nexport async function getWorkspaceContext(): Promise<WorkspaceContext> {\n const workspaceId = await getWorkspaceId();\n const s = state();\n return {\n workspaceId,\n workspaceSlug: s.workspaceSlug ?? \"unknown\",\n workspaceName: s.workspaceName ?? \"unknown\",\n createdAt: s.workspaceCreatedAt,\n governanceMode: s.workspaceGovernanceMode ?? \"open\",\n };\n}\n\n/**\n * TEN-1810: Re-fetches only governanceMode from the workspace without invalidating\n * stable identifiers (workspaceId, slug, name). Safe to call before every capture.\n * Single round-trip, no retry loop — workspace is already known at this point.\n */\nexport async function refreshWorkspaceGovernanceMode(): Promise<\"open\" | \"consensus\" | \"role\"> {\n const workspace = await kernelCall<{\n governanceMode?: \"open\" | \"consensus\" | \"role\";\n } | null>(\"resolveWorkspace\", {});\n const mode: \"open\" | \"consensus\" | \"role\" = workspace?.governanceMode ?? \"open\";\n const s = state();\n s.workspaceGovernanceMode = mode;\n return mode;\n}\n\nexport async function kernelQuery<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const workspaceId = await getWorkspaceId();\n return kernelCall<T>(fn, { ...args, workspaceId });\n}\n\nexport async function kernelMutation<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const workspaceId = await getWorkspaceId();\n return kernelCall<T>(fn, { ...args, workspaceId });\n}\n\n/**\n * @deprecated Use kernelQuery (reads) or kernelMutation (writes) instead.\n * Kept temporarily for backward compatibility — identical to both.\n */\nexport async function mcpAction<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const workspaceId = await getWorkspaceId();\n return kernelCall<T>(fn, { ...args, workspaceId });\n}\n\n/**\n * Gate check: throws if no active, oriented session exists.\n *\n * Used for read tools that require session context per SOS-iszqu7:\n * structured Chain data for agent consumption requires an active session.\n * Lighter than requireWriteAccess — does not check key scope.\n */\nexport function requireActiveSession(): void {\n const lc = sessionLifecycle();\n\n if (!lc.agentSessionId) {\n throw new Error(\n \"Active session required (SOS-iszqu7). Call `session action=start` then `orient` first.\"\n );\n }\n\n if (lc.sessionClosed) {\n throw new Error(\n \"Session has been closed (SOS-iszqu7). Start a new session with `session action=start`.\"\n );\n }\n\n if (!lc.sessionOriented) {\n throw new Error(\n \"Orientation required before accessing build context (SOS-iszqu7). Call `orient` first.\"\n );\n }\n}\n\n/**\n * Gate check: throws if the agent is not allowed to write.\n *\n * Enforces:\n * 1. Session must exist (always required — no REQUIRE_AGENT_SESSION flag)\n * 2. Session must not be closed\n * 3. Session must be oriented\n * 4. Key scope must be readwrite\n */\nexport function requireWriteAccess(): void {\n const lc = sessionLifecycle();\n const s = state();\n\n if (!lc.agentSessionId) {\n throw new Error(\n \"Agent session required for write operations. Call `session action=start` first.\"\n );\n }\n\n if (lc.sessionClosed) {\n throw new Error(\n \"Agent session has been closed. Write tools are no longer available.\"\n );\n }\n\n if (!lc.sessionOriented) {\n throw new Error(\n \"Orientation required before writing to the Chain. Call 'orient' first.\"\n );\n }\n\n if (s.apiKeyScope === \"read\") {\n throw new Error(\n \"This API key has read-only scope. Write tools are not available.\"\n );\n }\n}\n\n/**\n * Gate for vendor feedback-triage actions (queue/note/group/status); list is member read-back.\n *\n * Mirrors the server contract exactly (convex/productFeedback.ts\n * `validateVendorTriageSession` → `intelligence/agentSessions:validateSessionForCaller`\n * with `requireActive: true`, `requireScope: 'readwrite'`, and deliberately NO\n * `requireOriented`): feedback rows are vendor telemetry, not Chain entries, and the\n * session-start queue hint points straight at `feedback action=queue` — gating triage\n * on orientation client-side would reject a call the server accepts.\n *\n * Enforces:\n * 1. Session must exist\n * 2. Session must not be closed\n * 3. Key scope must be readwrite\n *\n * Unlike `requireWriteAccess`, orientation is NOT required.\n */\nexport function requireVendorTriageAccess(): void {\n const lc = sessionLifecycle();\n const s = state();\n\n if (!lc.agentSessionId) {\n throw new Error(\n \"Agent session required for feedback triage. Call `session action=start` first.\"\n );\n }\n\n if (lc.sessionClosed) {\n throw new Error(\n \"Agent session has been closed. Feedback triage is no longer available.\"\n );\n }\n\n if (s.apiKeyScope === \"read\") {\n throw new Error(\n \"This API key has read-only scope. Feedback triage is not available.\"\n );\n }\n}\n\n/**\n * Recover session orientation state from Convex on restart.\n * If the session is active and oriented in Convex, restore local state.\n */\nexport async function recoverSessionState(): Promise<void> {\n const s = state();\n if (!s.workspaceId) return;\n try {\n const session = await kernelCall<{\n _id: string;\n status: string;\n oriented: boolean;\n toolsScope: string;\n } | null>(\"agent.getActiveSession\", {\n workspaceId: s.workspaceId,\n // WP-479 E2: required disambiguation arg for the per-apiKey-quota re-scoped\n // getActiveSession — absent identity (null) still resolves to the keyless/legacy path\n // server-side.\n conversationId: getConversationId() ?? undefined,\n });\n\n if (session && session.status === \"active\") {\n const lc = sessionLifecycle();\n lc.agentSessionId = session._id;\n lc.sessionOriented = session.oriented;\n s.apiKeyScope = session.toolsScope as \"read\" | \"readwrite\";\n lc.sessionClosed = false;\n }\n } catch {\n // Recovery is best-effort\n }\n}\n","/**\n * Conversation identity resolution — WP-479 E2 (client half), MCP server side.\n *\n * Deliberate duplicate of packages/cli/src/lib/conversation.ts — the two packages are published\n * independently (no shared workspace dependency between them), so this is a minimal, intentional\n * fork rather than a cross-package import. Keep the two files' resolution logic in sync by hand\n * if the priority order or normalization rule changes.\n *\n * The MCP server is a subprocess of the harness (Claude Code / Cursor / Codex), so the same env\n * channels that a Bash-tool child process would see are visible here too. Resolved once at\n * startup (see startAgentSession / recoverSessionState in ../client.ts) and held for the process\n * lifetime — there is no `--conversation` flag equivalent on this surface, so there is no\n * explicit-override parameter here (unlike the CLI twin).\n *\n * Priority order (harness findings: INS-2122, founder-run tests 2026-07-10):\n * 1. `PB_CONVERSATION_ID` — the universal seam any harness adapter can set.\n * 2. `CLAUDE_CODE_SESSION_ID` — present in Claude Code 2.1.206+ Bash shells (version-dependent,\n * treat as detected, never asserted).\n * 3. `CURSOR_CONVERSATION_ID` — confirmed live in cursor-agent 2026.07.01 shell env.\n * 4. `CODEX_TUI_SESSION_LOG_PATH` — Codex has no native conversation id; this Superset-injected\n * log path is unique per session run and stands in as the discriminator. It always contains\n * `/`, so normalization (below) always hashes it — no special-casing needed here.\n * 5. none of the above → `null` (legacy behavior — no conversation disambiguation).\n */\n\nimport { createHash } from \"node:crypto\";\n\n/** Above this raw length, or containing any char outside the join-key-safe set, hash instead. */\nconst MAX_RAW_LEN = 128;\n\n/** Join-key-safe charset. */\nconst SAFE_CHARS_RE = /^[A-Za-z0-9._-]+$/;\n\n/**\n * Normalize a raw candidate conversation id into a value that is always safe to use as a Convex\n * join key. Never truncates (a `slice()` would collide two distinct long ids into one lineage) —\n * oversized or unsafe-charset ids are replaced whole with their sha256 hex digest.\n */\nexport function normalizeConversationId(raw: string): string {\n if (raw.length > MAX_RAW_LEN || !SAFE_CHARS_RE.test(raw)) {\n return createHash(\"sha256\").update(raw).digest(\"hex\");\n }\n return raw;\n}\n\nfunction pickRawConversationId(): string | null {\n const candidates = [\n process.env.PB_CONVERSATION_ID,\n process.env.CLAUDE_CODE_SESSION_ID,\n process.env.CURSOR_CONVERSATION_ID,\n process.env.CODEX_TUI_SESSION_LOG_PATH,\n ];\n for (const candidate of candidates) {\n if (candidate && candidate.trim().length > 0) return candidate.trim();\n }\n return null;\n}\n\n/**\n * Resolve the effective conversation id for this MCP server process. Pure env-based feature\n * detection — with none of the channel env vars set, returns `null` and callers fall back to\n * legacy (no-conversation) behavior.\n */\nexport function resolveConversationId(): string | null {\n const raw = pickRawConversationId();\n if (!raw) return null;\n return normalizeConversationId(raw);\n}\n","/**\n * Compound-tool action telemetry (WP-484 S1, Q3: decided in). Per-tool+action call counter.\n * `runWithToolContext` (client.ts) is already the one chokepoint every compound tool wraps its\n * handler body in (Code Integrity: derive from what the system already tracks, don't add a\n * second call site per tool) — §2 found NO existing per-MCP-tool call-frequency telemetry, so\n * this is what makes the *next* consolidation decision data-driven. In-memory for local/test\n * visibility; mirrored to PostHog (`mcp_compound_tool_action`) separately via `trackCompoundToolAction`.\n *\n * Extracted out of client.ts (not inlined) — STD-2/DEC-1504: client.ts is grandfathered\n * shrink-only; this is the WP-584 offset for that PR's session-start notice type addition.\n */\nconst toolActionCounts = new Map<string, number>();\n\nfunction actionCountKey(tool: string, action?: string): string {\n return action ? `${tool}:${action}` : tool;\n}\n\nexport function recordToolAction(tool: string, action?: string): void {\n const key = actionCountKey(tool, action);\n toolActionCounts.set(key, (toolActionCounts.get(key) ?? 0) + 1);\n}\n\n/** Snapshot of per-tool+action call counts for this process (diagnostics/tests). */\nexport function getToolActionCounts(): Record<string, number> {\n return Object.fromEntries(toolActionCounts);\n}\n\n/** Test-only: reset the counter between test cases. */\nexport function resetToolActionCounts(): void {\n toolActionCounts.clear();\n}\n","/**\n * Cold-start deployment probing (DEC-789 S2) — pulled out of client.ts (module-health ratchet,\n * STD-2/DEC-1504: client.ts is grandfathered shrink-only, so a new fix recovers the room it\n * needs by splitting out a genuine responsibility rather than widening the baseline).\n *\n * \"Parse the fallback URL list, then probe each candidate's /api/key-check until one answers\n * ok:true\" is self-contained — it takes a candidate list and an API key and returns the first\n * live deployment (or null), with no dependency on client.ts's own state. client.ts's\n * `resolveDeploymentUrl` still owns WHEN to probe (warm-path short-circuit, no-fallback\n * short-circuit, caching the winner on `state().deploymentUrl`) — this module owns HOW.\n */\n\n/** DEC-789 S2: parse comma-separated fallback deployment URLs from env. */\nexport function parseFallbackUrls(raw: string | undefined): string[] {\n if (!raw) return [];\n return raw.split(\",\").map((u) => u.trim()).filter(Boolean);\n}\n\n/**\n * Probe each candidate in order; return the first that answers ok:true to `/api/key-check`, or\n * null if every candidate failed.\n *\n * WP-575: the 3s timeout here is DELIBERATELY not route-derived, and is not the class of\n * timeout that work package's budget replaced. `/api/key-check` is a different endpoint from\n * the `/api/aki` gateway — it is not a registered route, has no entry in the route contract, and\n * no budget to derive from. It is also a REACHABILITY PROBE whose whole job is to pick the live\n * deployment out of several candidates by failing fast: a longer wait here would multiply across\n * candidates and delay every call, and a probe giving up costs nothing (the caller falls through\n * to the primary URL and the real call reports the real error). Do not \"unify\" this with the\n * gateway budget.\n */\nexport async function probeDeploymentCandidates(candidates: string[], apiKey: string): Promise<string | null> {\n for (const candidate of candidates) {\n try {\n const probeRes = await fetch(`${candidate}/api/key-check`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${apiKey}`, \"Content-Type\": \"application/json\" },\n signal: AbortSignal.timeout(3000),\n });\n if (probeRes.ok) {\n const data = (await probeRes.json()) as { ok: boolean };\n if (data.ok) return candidate;\n }\n } catch {\n // Probe failed (timeout, network error, etc.) — try next candidate.\n }\n }\n return null;\n}\n","/**\n * TEN-2382: dev/prod isolation guard for URL resolution.\n *\n * The MCP server resolves its deployment URL from env vars only and falls back to\n * the hardcoded production gateway (DEFAULT_CLOUD_URL) when nothing is set. That\n * fall-through is silent today, so a misconfigured deployment quietly talks to\n * production. This makes it loud.\n *\n * Constraint (settled by review): the mcp-server has NO signal for \"a repo-local\n * binding was expected\" — it sees only env. So the warn fires on ANY fall-through\n * to the hardcoded default (Option A), not on an unimplementable \"binding expected\n * but absent\" condition.\n *\n * stderr ONLY — never stdout: stdio transport reserves stdout for MCP protocol bytes.\n */\nimport { DEFAULT_CLOUD_URL } from \"./client.js\";\n\n/**\n * Warn (on stderr) when `resolved` is the hardcoded production gateway because no\n * deployment URL was set explicitly. No-op when a URL was configured, or when the\n * resolved URL is anything other than the hardcoded default.\n */\nexport function warnOnProdFallthrough(\n resolved: string,\n opts: { explicit: boolean },\n): void {\n if (opts.explicit) return;\n if (resolved.replace(/\\/$/, \"\") !== DEFAULT_CLOUD_URL.replace(/\\/$/, \"\")) return;\n process.stderr.write(\n `[MCP] No deployment URL configured — defaulting to the production gateway ` +\n `${DEFAULT_CLOUD_URL}. Set CONVEX_SITE_URL or PRODUCTBRAIN_URL to target a ` +\n `different deployment.\\n`,\n );\n}\n"],"mappings":"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","names":["client","AsyncLocalStorage","createHash","AsyncLocalStorage"]}
@@ -42,7 +42,7 @@ import {
42
42
  trackSessionCaptureRate,
43
43
  trackWriteBackHintServed,
44
44
  trackZeroCaptureAuditFired
45
- } from "./chunk-SD33RK7E.js";
45
+ } from "./chunk-3M2TTUHD.js";
46
46
 
47
47
  // src/server.ts
48
48
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -17048,4 +17048,4 @@ export {
17048
17048
  createProductBrainServer,
17049
17049
  initFeatureFlags
17050
17050
  };
17051
- //# sourceMappingURL=chunk-SGXGMTOZ.js.map
17051
+ //# sourceMappingURL=chunk-JEF4VLJE.js.map
package/dist/cli/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/cli/index.ts
4
4
  var subcommand = process.argv[2];
5
5
  if (subcommand === "setup") {
6
- const { runSetup } = await import("../setup-DUKI2SYN.js");
6
+ const { runSetup } = await import("../setup-EQKU3EN5.js");
7
7
  await runSetup();
8
8
  } else {
9
9
  await import("../index.js");
package/dist/http.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SERVER_VERSION,
3
3
  createProductBrainServer,
4
4
  initFeatureFlags
5
- } from "./chunk-SGXGMTOZ.js";
5
+ } from "./chunk-JEF4VLJE.js";
6
6
  import {
7
7
  DEFAULT_CLOUD_URL,
8
8
  bootstrapHttp,
@@ -12,7 +12,7 @@ import {
12
12
  initAnalytics,
13
13
  runWithAuth,
14
14
  shutdownAnalytics
15
- } from "./chunk-SD33RK7E.js";
15
+ } from "./chunk-3M2TTUHD.js";
16
16
 
17
17
  // src/http.ts
18
18
  import { createHash, randomUUID as randomUUID2 } from "crypto";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  SERVER_VERSION,
4
4
  createProductBrainServer,
5
5
  initFeatureFlags
6
- } from "./chunk-SGXGMTOZ.js";
6
+ } from "./chunk-JEF4VLJE.js";
7
7
  import {
8
8
  bootstrap,
9
9
  getAgentSessionId,
@@ -14,7 +14,7 @@ import {
14
14
  recoverSessionState,
15
15
  shutdownAnalytics,
16
16
  trackSessionStarted
17
- } from "./chunk-SD33RK7E.js";
17
+ } from "./chunk-3M2TTUHD.js";
18
18
 
19
19
  // src/index.ts
20
20
  import { readFileSync } from "fs";
@@ -9,7 +9,7 @@ import {
9
9
  trackSetupStarted,
10
10
  warnOnProdFallthrough,
11
11
  writeClientConfig
12
- } from "./chunk-SD33RK7E.js";
12
+ } from "./chunk-3M2TTUHD.js";
13
13
 
14
14
  // src/cli/setup.ts
15
15
  import { execSync } from "child_process";
@@ -302,4 +302,4 @@ export {
302
302
  resolveSetupSiteUrl,
303
303
  runSetup
304
304
  };
305
- //# sourceMappingURL=setup-DUKI2SYN.js.map
305
+ //# sourceMappingURL=setup-EQKU3EN5.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productbrain/mcp",
3
- "version": "0.0.1-beta.4513",
3
+ "version": "0.0.1-beta.4530",
4
4
  "description": "Product Brain — MCP server for AI-assisted product knowledge management",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/analytics.ts","../src/auth.ts","../src/cli/config-writer.ts","../src/generated/routeLatencyBudget.generated.ts","../src/lib/gatewaySeamStore.ts","../src/gatewaySeam.ts","../src/client.ts","../src/lib/conversation.ts","../src/lib/toolActionCounts.ts","../src/lib/deploymentUrlResolver.ts","../src/prod-fallthrough.ts"],"sourcesContent":["/**\n * PostHog analytics for SynergyOS maintainers — tracks MCP usage (sessions, tool calls).\n * Not user-facing. Key is injected at build time via SYNERGYOS_POSTHOG_KEY.\n * Override with POSTHOG_MCP_KEY for self-hosted deployments.\n */\n\nimport { userInfo } from \"node:os\";\nimport { PostHog } from \"posthog-node\";\n\nlet client: PostHog | null = null;\nlet distinctId = \"anonymous\";\n\nconst POSTHOG_HOST = \"https://eu.i.posthog.com\";\n\n/** Injected at build time: SYNERGYOS_POSTHOG_KEY env when running `npm run build`/publish. */\ndeclare const __SYNERGYOS_POSTHOG_KEY__: string;\n\n/** Only write to stderr when MCP_DEBUG=1 for quieter default DX. */\nfunction log(msg: string): void {\n if (process.env.MCP_DEBUG === \"1\") {\n process.stderr.write(msg);\n }\n}\n\nfunction getBuildTimeKey(): string {\n try {\n return __SYNERGYOS_POSTHOG_KEY__;\n } catch {\n // Not replaced by bundler (e.g. running via tsx in tests) — treat as absent.\n return \"\";\n }\n}\n\nexport function initAnalytics(): void {\n const apiKey = process.env.POSTHOG_MCP_KEY || getBuildTimeKey();\n if (!apiKey) {\n log(\"[MCP-ANALYTICS] No PostHog key — tracking disabled (set SYNERGYOS_POSTHOG_KEY at build time for publish)\\n\");\n return;\n }\n\n client = new PostHog(apiKey, {\n host: POSTHOG_HOST,\n flushAt: 1,\n flushInterval: 5000,\n featureFlagsPollingInterval: 30_000,\n });\n distinctId = process.env.MCP_USER_ID || fallbackDistinctId();\n\n log(`[MCP-ANALYTICS] Initialized — host=${POSTHOG_HOST} distinctId=${distinctId}\\n`);\n}\n\nfunction fallbackDistinctId(): string {\n try {\n return userInfo().username;\n } catch {\n return `os-${process.pid}`;\n }\n}\n\nexport function trackSessionStarted(\n workspaceId: string,\n serverVersion: string,\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_session_started\",\n properties: {\n workspace_id: workspaceId,\n server_version: serverVersion,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackToolCall(\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n workspaceId: string,\n errorMsg?: string,\n): void {\n const properties: Record<string, unknown> = {\n tool: fn,\n status,\n duration_ms: durationMs,\n workspace_id: workspaceId,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n };\n if (errorMsg) properties.error = errorMsg;\n\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_tool_called\",\n properties,\n });\n}\n\n/**\n * Per-tool+action call telemetry (WP-484 S1, Q3). Fired from\n * `runWithToolContext` in client.ts — the one chokepoint every compound tool\n * already wraps its handler body in. Feeds the next consolidation decision\n * (§2 of the design: no per-MCP-tool call-frequency signal existed before this).\n */\nexport function trackCompoundToolAction(\n tool: string,\n action: string | undefined,\n workspaceId: string,\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_compound_tool_action\",\n properties: {\n tool,\n action: action ?? null,\n workspace_id: workspaceId,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackSetupStarted(): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_started\",\n properties: {\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackSetupCompleted(\n chosenClient: string,\n outcome: \"config_written\" | \"config_existed\" | \"snippet_shown\" | \"write_error\",\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_completed\",\n properties: {\n client: chosenClient,\n outcome,\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackQualityVerdict(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n context: string;\n passed: boolean;\n source: string;\n criteria_total: number;\n criteria_failed: number;\n llm_scheduled: boolean;\n /** WP-475 E3: rubric methodology that produced the verdict (v1-vs-v2 seam). */\n methodology_version?: string;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_generated\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackQualityCheck(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n passed: boolean;\n source: string;\n llm_status?: string;\n llm_duration_ms?: number;\n llm_error?: string;\n has_roger_martin: boolean;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_checked\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport type ClassifierReasonCategory =\n | \"auto-routed\"\n | \"low-confidence\"\n | \"ambiguous\"\n | \"non-provisioned\";\n\ntype CaptureClassifierTelemetryProps = {\n predicted_collection: string;\n confidence: number;\n auto_routed: boolean;\n reason_category: ClassifierReasonCategory;\n explicit_collection_provided: boolean;\n};\n\nfunction trackCaptureClassifierEvent(\n event: \"mcp_capture_classifier_evaluated\" | \"mcp_capture_classifier_auto_routed\" | \"mcp_capture_classifier_fallback\",\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event,\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics are advisory and must never break capture flow.\n }\n}\n\nexport function trackCaptureClassifierEvaluated(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_evaluated\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierAutoRouted(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_auto_routed\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierFallback(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_fallback\", workspaceId, props);\n}\n\n/** GLO-26 / TEN-156: every SSOT commit for PostHog funnels (split auto vs manual). */\nexport function trackChainEntryCommitted(\n workspaceId: string,\n props: {\n entry_id: string;\n collection?: string;\n commit_method: \"auto\" | \"manual\";\n surface:\n | \"mcp_commit_tool\"\n | \"mcp_capture\"\n | \"mcp_wrapup\";\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"chain_entry_committed\",\n properties: {\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n ...props,\n },\n });\n } catch {\n // Analytics must never break the tool path.\n }\n}\n\nexport function trackKnowledgeGap(\n workspaceId: string,\n props: {\n query: string;\n tool: string;\n action: string;\n gap_type: \"search_zero\" | \"context_task_empty\" | \"context_entry_isolated\" | \"context_graph_empty\";\n collection_scope?: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"knowledge_gap_detected\",\n properties: {\n ...props,\n query: props.query.slice(0, 200),\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the tool response path.\n }\n}\n\n// ── BET-272 S6 / STD-155: Capture intelligence observability ────────────────\n\n/** Fires when formative quality hints are returned at capture time. */\nexport function trackCaptureQualityHints(\n workspaceId: string,\n props: {\n collection: string;\n hint_count: number;\n hint_fields: string[];\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"mcp_capture_quality_hints\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break capture flow.\n }\n}\n\n/** Fires when relation suggestions are returned at capture time. */\nexport function trackCaptureRelationSuggestions(\n workspaceId: string,\n props: {\n collection: string;\n suggestion_count: number;\n relation_types: string[];\n avg_confidence: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"mcp_capture_relation_suggestions\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break capture flow.\n }\n}\n\n// ── BET-288 S0: Collection classification confusion matrix telemetry ────────\n\n/**\n * Fires on every collection classification (auto-routed, fallback, explicit-provided).\n * Captures the full confusion signal: predicted collection, runner-up, thinkingLayer,\n * and confidence scores. This is the baseline measurement for BET-288 algorithm changes.\n */\nexport function trackCollectionClassified(\n workspaceId: string,\n props: {\n collection_slug: string;\n thinking_layer: string | null;\n confidence: number;\n classified_by: \"llm\" | \"heuristic\";\n confidence_tier: \"high\" | \"medium\" | \"low\";\n alternative_slug: string | null;\n alternative_confidence: number | null;\n explicit_collection_provided: boolean;\n auto_routed: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"collection_classified\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\n// ── BET-289 S5: Field-level writing guidance telemetry ────────────────────────\n\n/** Fires when field guidance is injected into a capture prompt or response. */\nexport function trackFieldGuidanceApplied(\n workspaceId: string,\n props: {\n collection: string;\n guided_field_count: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_guidance_applied\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\n/** Fires when commit-time heuristic detects field guidance violations. */\nexport function trackFieldQualityWarning(\n workspaceId: string,\n props: {\n warning_count: number;\n warning_types: string[];\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_quality_warning\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit flow.\n }\n}\n\n/** Fires when skipGuidanceCheck is used to bypass guidance validation. */\nexport function trackFieldQualityOverride(\n workspaceId: string,\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_quality_override\",\n properties: {\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit flow.\n }\n}\n\n// ── WP-306 S3: Write-Back Measurement (capture rate insights) ──────────────\n\n/** Fires on session close to track session capture rate. */\nexport function trackSessionCaptureRate(\n workspaceId: string,\n props: {\n entries_created: number;\n entries_modified: number;\n relations_created: number;\n had_captures: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"session_capture_rate\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break session closure.\n }\n}\n\n/** Fires when captureAudit is triggered (activity but no captures). */\nexport function trackZeroCaptureAuditFired(\n workspaceId: string,\n props: {\n suggestion_count: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"zero_capture_audit_fired\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break wrapup flow.\n }\n}\n\n// ── WP-316 S1b: Capture contract observability ──────────────────────────────\n\n/** Fires when a capture-contract resource request references an unknown collection slug. */\nexport function trackCaptureContractMiss(\n workspaceId: string,\n props: {\n slug: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"capture_contract_miss\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the resource response path.\n }\n}\n\n/** Fires when writeBackHints are included in an orient response. */\nexport function trackWriteBackHintServed(\n workspaceId: string,\n props: {\n hint_count: number;\n has_task: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"write_back_hint_served\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break orient flow.\n }\n}\n\n/**\n * WP-316 S1a: Fires when entries action=commit fails with a structured error code.\n * Enables observability on which validation errors block commits most often.\n */\nexport function trackCommitErrorByCode(\n workspaceId: string,\n props: {\n error_code: string;\n missing_field_count: number;\n field_error_count: number;\n entry_id: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"commit_error_by_code\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit error path.\n }\n}\n\n// ── WP-316 S2: Classifier divergence tracking ─────────────────────────────\n\n/**\n * Fires when an agent provides an explicit collection that differs from the\n * classifier's top suggestion. Measures how often agents override the classifier\n * and whether those overrides are to known alternatives or completely off-roster.\n */\nexport function trackClassifierDivergence(\n workspaceId: string,\n props: {\n classifier_collection: string;\n agent_collection: string;\n classifier_confidence: number;\n classifier_tier: string;\n agent_in_candidates: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"classifier_divergence\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\nexport function getPostHogClient(): PostHog | null {\n return client;\n}\n\nexport async function shutdownAnalytics(): Promise<void> {\n await client?.shutdown();\n}\n","/**\n * Request-scoped auth for HTTP transport mode.\n *\n * stdio: API key from PRODUCTBRAIN_API_KEY env, one user per process.\n * http: API key from Bearer header per request, many users per process.\n *\n * AsyncLocalStorage propagates the token through the async call chain\n * so client.ts resolves the correct API key and state per request.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { createHash } from \"node:crypto\";\n\n// ── Key Hashing (Fix 3 — session binding) ───────────────────────────────\n\n/**\n * Short one-way hash of an API key used to bind MCP sessions to a specific key.\n * Not a secret — stored in the session entry to detect session hijacking.\n */\nexport function hashKey(key: string): string {\n return createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 16);\n}\n\n// ── Request Context ─────────────────────────────────────────────────────\n\ninterface RequestAuth {\n apiKey: string;\n /**\n * WP-479 review fix (Codex re-review): the HTTP transport's `Mcp-Session-Id` for this request.\n * Session-lifecycle state (the active agentSessionId + oriented/closed flags) keys on this so two\n * concurrent HTTP streams SHARING an API key don't overwrite each other's active session. Absent\n * in STDIO mode (one process = one session) and on the pre-session initialize request.\n */\n mcpSessionId?: string;\n}\n\nconst requestStore = new AsyncLocalStorage<RequestAuth>();\n\nexport function runWithAuth<T>(auth: RequestAuth, fn: () => T | Promise<T>): T | Promise<T> {\n return requestStore.run(auth, fn);\n}\n\nexport function getRequestApiKey(): string | undefined {\n return requestStore.getStore()?.apiKey;\n}\n\n/** The current HTTP request's `Mcp-Session-Id`, when present (HTTP transport, post-initialize). */\nexport function getRequestMcpSessionId(): string | undefined {\n return requestStore.getStore()?.mcpSessionId;\n}\n\n// ── Per-Key State (HTTP mode) ───────────────────────────────────────────\n\nexport interface KeyState {\n workspaceId: string | null;\n workspaceSlug: string | null;\n workspaceName: string | null;\n workspaceCreatedAt: number | null;\n /** BET-76 FEAT-111: Cached at workspace resolution time. Defaults to 'open'. */\n workspaceGovernanceMode: \"open\" | \"consensus\" | \"role\" | null;\n agentSessionId: string | null;\n apiKeyId: string | null;\n apiKeyScope: \"read\" | \"readwrite\";\n sessionOriented: boolean;\n sessionClosed: boolean;\n lastAccess: number;\n /** DEC-789 S2: Convex deployment URL this key belongs to, resolved at key-check time. */\n deploymentUrl: string | null;\n}\n\nconst SESSION_TTL_MS = 30 * 60 * 1000;\nconst MAX_KEYS = 100;\nconst keyStateMap = new Map<string, KeyState>();\n\nfunction newKeyState(): KeyState {\n return {\n workspaceId: null,\n workspaceSlug: null,\n workspaceName: null,\n workspaceCreatedAt: null,\n workspaceGovernanceMode: null,\n agentSessionId: null,\n apiKeyId: null,\n apiKeyScope: \"readwrite\",\n sessionOriented: false,\n sessionClosed: false,\n lastAccess: Date.now(),\n deploymentUrl: null,\n };\n}\n\nexport function getKeyState(apiKey: string): KeyState {\n let s = keyStateMap.get(apiKey);\n if (!s) {\n s = newKeyState();\n keyStateMap.set(apiKey, s);\n evictStale();\n }\n s.lastAccess = Date.now();\n return s;\n}\n\nfunction evictStale(): void {\n if (keyStateMap.size <= MAX_KEYS) return;\n const now = Date.now();\n for (const [key, s] of keyStateMap) {\n if (now - s.lastAccess > SESSION_TTL_MS) keyStateMap.delete(key);\n }\n if (keyStateMap.size > MAX_KEYS) {\n const sorted = [...keyStateMap.entries()].sort((a, b) => a[1].lastAccess - b[1].lastAccess);\n for (let i = 0; i < sorted.length - MAX_KEYS; i++) {\n keyStateMap.delete(sorted[i][0]);\n }\n }\n}\n","/**\n * Multi-client MCP config detection and writer.\n *\n * Supports:\n * - Cursor: .cursor/mcp.json in cwd (project-level)\n * - Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)\n * %APPDATA%/Claude/claude_desktop_config.json (Windows)\n *\n * The writer reads existing config, merges the new server entry (never\n * overwrites existing entries), and writes back. Falls back to printing\n * a snippet for unsupported OS or unknown formats.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\n\nexport interface McpClientInfo {\n name: string;\n configPath: string;\n}\n\nconst SERVER_ENTRY_KEY = \"Product Brain\";\nconst LEGACY_ENTRY_KEY = \"productbrain\";\n\n/**\n * Canonical npx package specifier. Update here when exiting beta.\n * Frontend mirror: src/lib/constants/mcp.ts\n * Business rule: BR-84 (Chain)\n */\nexport const MCP_NPX_PACKAGE = \"@productbrain/mcp@beta\";\n\nfunction buildServerEntry(apiKey: string) {\n return {\n command: \"npx\",\n args: [\"-y\", MCP_NPX_PACKAGE],\n env: { PRODUCTBRAIN_API_KEY: apiKey },\n };\n}\n\n// ── Detection ───────────────────────────────────────────────────────────\n\nfunction getCursorConfigPath(): string {\n return join(process.cwd(), \".cursor\", \"mcp.json\");\n}\n\nfunction getClaudeDesktopConfigPath(): string | null {\n const os = platform();\n if (os === \"darwin\") {\n return join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n }\n if (os === \"win32\") {\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"Claude\", \"claude_desktop_config.json\");\n }\n // Linux: no official Claude Desktop location yet\n return null;\n}\n\nexport function resolveClient(name: \"Cursor\" | \"Claude Desktop\"): McpClientInfo | null {\n if (name === \"Cursor\") {\n return { name, configPath: getCursorConfigPath() };\n }\n const configPath = getClaudeDesktopConfigPath();\n return configPath ? { name, configPath } : null;\n}\n\n// ── Writing ─────────────────────────────────────────────────────────────\n\nfunction readJsonSafe(path: string): Record<string, any> {\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"));\n } catch {\n return {};\n }\n}\n\n/**\n * Write or merge the Product Brain server entry into a client config file.\n * Migrates legacy \"productbrain\" key to \"Product Brain\" when present.\n * Returns true if the config was written, false if already present.\n */\nexport async function writeClientConfig(\n client: McpClientInfo,\n apiKey: string,\n): Promise<boolean> {\n const config = readJsonSafe(client.configPath);\n\n const serversKey = \"mcpServers\";\n if (!config[serversKey]) config[serversKey] = {};\n\n // Migrate legacy \"productbrain\" key or update existing Product Brain with new API key\n if (config[serversKey][LEGACY_ENTRY_KEY]) {\n const legacy = config[serversKey][LEGACY_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = {\n ...buildServerEntry(apiKey),\n env: { ...legacy.env, PRODUCTBRAIN_API_KEY: legacy.env?.PRODUCTBRAIN_API_KEY ?? apiKey },\n };\n delete config[serversKey][LEGACY_ENTRY_KEY];\n } else {\n const existing = config[serversKey][SERVER_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = existing\n ? { ...existing, env: { ...existing.env, PRODUCTBRAIN_API_KEY: apiKey } }\n : buildServerEntry(apiKey);\n }\n\n const dir = dirname(client.configPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(client.configPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n return true;\n}\n","// GENERATED by scripts/generate-route-latency-budget.mjs from packages/kernel-client/src/routeLatencyBudget.ts\n// and packages/kernel-client/src/generated/routes.generated.ts.\n// DO NOT EDIT. Run `npm run route-budget:codegen` to regenerate.\n//\n// WP-575 (TEN-2917, TEN-1126), commits_to DEC-64. This file is emitted IDENTICALLY into\n// packages/mcp-server and packages/cli so the two connectors cannot disagree about how\n// long a gateway route may take — the defect this work package removes was exactly that\n// disagreement (MCP hardcoded a blanket 10s, the CLI set none at all).\n//\n// The rationale — why route TYPE is the structural predictor of LLM exposure, and how\n// each value is grounded in DEC-64 rather than invented — lives with the declaration in\n// packages/kernel-client/src/routeLatencyBudget.ts. Read that file before changing a\n// number here; changing it HERE does nothing, the next codegen run overwrites it.\n//\n// Why a copy and not an import: both connectors consume @productbrain/kernel-client\n// TYPE-ONLY (mcp-server via a tsconfig paths alias for Railway's isolated Docker build,\n// DEC-701; cli as a devDependency it must not emit into its published dist/). A budget\n// sets an AbortSignal, so it is runtime code and cannot be type-erased. See the\n// generator header for the full constraint.\n\nexport type GatewayRouteType = 'query' | 'mutation' | 'action';\n\n/** The budget table, copied verbatim from the SSOT. Milliseconds. */\nexport const ROUTE_LATENCY_BUDGET_MS = {\n\tquery: 10_000,\n\tmutation: 10_000,\n\taction: 30_000,\n} as const;\n\n/**\n * PER-ROUTE OVERRIDES — routes whose OWN server-side deadline exceeds their type's budget\n * (PR #533 review, Codex P1). Route type predicts LLM EXPOSURE, not LLM COUNT: a flat\n * action budget assumed every action is one inline LLM call, and shipped actions declare\n * far longer deadlines. Bounding those at the type budget aborts a call the server is\n * still legitimately working on. Each value is derived from a deadline the SERVER\n * declares — see the SSOT for the citation behind every number.\n */\nexport const ROUTE_LATENCY_BUDGET_OVERRIDE_MS = {\n\t// convex/intelligence/onboardingChat.ts:333 declares `timeoutMs: 45_000` for the extraction\n\t// LLM call — 15s BEYOND the flat action budget. 60s clears it with room for the surrounding\n\t// request/response work the 45s covers none of.\n\t'onboarding.chat': 60_000,\n\t// convex/intelligence/spineCheck.ts:210 sets SPINE_CHECK_WALL_CLOCK_BUDGET_MS = 4 * 60_000\n\t// for its SEQUENTIAL strategy probes (:225-239, deliberately not Promise.all), and :215\n\t// reserves a tail of one 25s probe + an optional 25s story pass + 10s. The server's own\n\t// design ceiling is scheduleSpineCheck's 5-min MAX_RUN_IN_FLIGHT_MS lease (:206-209).\n\t// 5.5 min sits above that ceiling and far under Convex's 10-min action cap.\n\t'quality.spineCheck': 330_000,\n} as const;\n\n/**\n * The widest declared budget — the fail-safe default for a route name this map does not\n * know. Derived from the tables rather than restated, so it cannot drift if a value moves.\n * Spans the overrides too, so the unknown-route default is never TIGHTER than a budget\n * already granted to a known route.\n */\nexport const WIDEST_ROUTE_LATENCY_BUDGET_MS: number = Math.max(\n\t...Object.values(ROUTE_LATENCY_BUDGET_MS),\n\t...Object.values(ROUTE_LATENCY_BUDGET_OVERRIDE_MS),\n);\n\n/** Every gateway route's Convex function type, from convex/http.ts's registry. */\nconst ROUTE_TYPE_ENTRIES: Readonly<Record<string, GatewayRouteType>> = {\n\t\"resolveWorkspace\": \"query\",\n\t\"feedback.submit\": \"mutation\",\n\t\"feedback.listOwn\": \"query\",\n\t\"feedback.list\": \"action\",\n\t\"feedback.note\": \"action\",\n\t\"feedback.group\": \"action\",\n\t\"feedback.status\": \"action\",\n\t\"organisation.status\": \"query\",\n\t\"organisation.assignMembership\": \"mutation\",\n\t\"organisation.removeMembership\": \"mutation\",\n\t\"organisation.setOwnership\": \"mutation\",\n\t\"organisation.clearOwnership\": \"mutation\",\n\t\"organisation.activate\": \"mutation\",\n\t\"organisation.upgrade\": \"mutation\",\n\t\"organisation.upgradeRung2\": \"mutation\",\n\t\"organisation.upgradeRung3\": \"mutation\",\n\t\"organisation.upgradeRung4\": \"mutation\",\n\t\"organisation.updateGovernanceMode\": \"mutation\",\n\t\"organisation.hierarchy.show\": \"query\",\n\t\"organisation.hierarchy.setParent\": \"mutation\",\n\t\"organisation.hierarchy.clearParent\": \"mutation\",\n\t\"chain.seed\": \"mutation\",\n\t\"chain.listCollections\": \"query\",\n\t\"chain.getCollection\": \"query\",\n\t\"chain.getCollectionFields\": \"query\",\n\t\"chain.auditCollections\": \"query\",\n\t\"chain.exportDefinitions\": \"query\",\n\t\"chain.createCollection\": \"mutation\",\n\t\"chain.updateCollection\": \"mutation\",\n\t\"chain.listEntries\": \"query\",\n\t\"chain.getEntry\": \"query\",\n\t\"chain.batchGetEntries\": \"query\",\n\t\"chain.createEntry\": \"action\",\n\t\"chain.updateEntry\": \"mutation\",\n\t\"chain.restoreArchivedEntry\": \"mutation\",\n\t\"chain.moveToCollection\": \"mutation\",\n\t\"chain.shapeAdvisories\": \"query\",\n\t\"chain.shapeAdvisorySummary\": \"query\",\n\t\"chain.showShapeAdvisory\": \"query\",\n\t\"chain.dispositionShapeAdvisory\": \"mutation\",\n\t\"conflicts.list\": \"query\",\n\t\"conflicts.resolve\": \"mutation\",\n\t\"conflicts.summary\": \"query\",\n\t\"conflicts.snooze\": \"mutation\",\n\t\"conflicts.reverdict\": \"mutation\",\n\t\"direction.list\": \"query\",\n\t\"direction.refresh\": \"action\",\n\t\"direction.defer\": \"mutation\",\n\t\"question.create\": \"mutation\",\n\t\"question.adopt\": \"mutation\",\n\t\"question.assign\": \"mutation\",\n\t\"question.snooze\": \"mutation\",\n\t\"question.decline\": \"mutation\",\n\t\"question.answer\": \"mutation\",\n\t\"question.forceClose\": \"mutation\",\n\t\"question.list\": \"query\",\n\t\"chain.classifyCollection\": \"action\",\n\t\"chain.classifyStrategyCategory\": \"query\",\n\t\"chain.batchClassifyHeuristic\": \"query\",\n\t\"chain.resolveCollection\": \"action\",\n\t\"chain.searchEntries\": \"query\",\n\t\"chain.searchByCanonicalName\": \"query\",\n\t\"chain.commitEntry\": \"mutation\",\n\t\"chain.verifyEntry\": \"mutation\",\n\t\"chain.batchCommitConstellation\": \"mutation\",\n\t\"chain.listEntryHistory\": \"query\",\n\t\"chain.listEntryVersions\": \"query\",\n\t\"chain.createEntryRelation\": \"mutation\",\n\t\"chain.createEntryRelations\": \"mutation\",\n\t\"chain.removeEntryRelation\": \"mutation\",\n\t\"chain.normalizeEntryDataLLM\": \"action\",\n\t\"chain.decomposeContent\": \"action\",\n\t\"chain.normalizeEntryDataPreview\": \"action\",\n\t\"chain.listEntryRelations\": \"query\",\n\t\"chain.scoreLinkCandidates\": \"query\",\n\t\"chain.evaluateCoherence\": \"query\",\n\t\"chain.listAutoLinkSuggestions\": \"query\",\n\t\"chain.acceptAutoLinkSuggestion\": \"mutation\",\n\t\"chain.dismissAutoLinkSuggestion\": \"mutation\",\n\t\"chain.quarantineAutoLinkSuggestion\": \"mutation\",\n\t\"chain.resurrectAutoLinkSuggestion\": \"mutation\",\n\t\"chain.expireAutoLinkSuggestion\": \"mutation\",\n\t\"chain.clusterAutoLinkSuggestions\": \"query\",\n\t\"chain.batchApplyAutoLinkSuggestions\": \"action\",\n\t\"chain.validateCommitConstellation\": \"query\",\n\t\"chain.getCaptureContract\": \"query\",\n\t\"chain.gatherContext\": \"query\",\n\t\"chain.getConstellation\": \"query\",\n\t\"chain.auditBet\": \"query\",\n\t\"agentKnowledge.facilitateEnvelope\": \"action\",\n\t\"agentKnowledge.wrapupEnvelope\": \"action\",\n\t\"agentKnowledge.captureEnvelope\": \"action\",\n\t\"chain.graphSuggestLinks\": \"query\",\n\t\"chain.graphGatherContext\": \"query\",\n\t\"chain.assembleBuildContext\": \"query\",\n\t\"chain.qualityCheck\": \"query\",\n\t\"chain.changeDetection\": \"query\",\n\t\"chain.structuralAggregation\": \"query\",\n\t\"chain.detectSemanticConflicts\": \"action\",\n\t\"chain.taskAwareGatherContext\": \"query\",\n\t\"chain.journeyAwareGatherContext\": \"query\",\n\t\"chain.resolveTaskStartup\": \"query\",\n\t\"chain.getBindingGovernanceView\": \"query\",\n\t\"chain.resolveTaskStartupHybrid\": \"action\",\n\t\"chain.taskAwareHybridGatherContext\": \"action\",\n\t\"chain.gatherFromSeeds\": \"query\",\n\t\"chain.getEntryNeighborhood\": \"query\",\n\t\"chain.deepChainWalk\": \"action\",\n\t\"chain.recordBriefRun\": \"mutation\",\n\t\"chain.getLastBriefRun\": \"query\",\n\t\"chain.incrementalChanges\": \"query\",\n\t\"chain.compoundQuery\": \"action\",\n\t\"chain.dismissSuggestion\": \"mutation\",\n\t\"chain.recordSessionSignal\": \"mutation\",\n\t\"chain.workspaceReadiness\": \"query\",\n\t\"chain.getCaptureHealth\": \"query\",\n\t\"chain.getGroundingHealth\": \"query\",\n\t\"chain.recordGroundingOutcome\": \"mutation\",\n\t\"chain.suggestLinksForCapture\": \"query\",\n\t\"chain.setOnboardingCompleted\": \"mutation\",\n\t\"chain.checkCardinalityWarning\": \"query\",\n\t\"chain.ingestDocument\": \"action\",\n\t\"chain.getOrientEntries\": \"query\",\n\t\"chain.getOrientView\": \"action\",\n\t\"chain.getRitualsSurface\": \"query\",\n\t\"chain.getGovernanceWithRelations\": \"action\",\n\t\"chain.classifyGovernance\": \"query\",\n\t\"chain.getVocabulary\": \"query\",\n\t\"scoreboard.get\": \"action\",\n\t\"rework.report\": \"mutation\",\n\t\"chain.listLabels\": \"query\",\n\t\"chain.createLabel\": \"mutation\",\n\t\"chain.updateLabel\": \"mutation\",\n\t\"chain.deleteLabel\": \"mutation\",\n\t\"chain.applyLabel\": \"mutation\",\n\t\"chain.removeLabel\": \"mutation\",\n\t\"chain.listEntriesByLabel\": \"query\",\n\t\"setup.getActiveSurface\": \"query\",\n\t\"setup.materializeSetup\": \"action\",\n\t\"setup.recordTamperRefusal\": \"mutation\",\n\t\"setup.recordTransition\": \"mutation\",\n\t\"setup.getCurrentSetupState\": \"query\",\n\t\"setup.listAssetsForUser\": \"query\",\n\t\"setup.ingestSetupAsset\": \"mutation\",\n\t\"setup.ingestSetupAssetWithBody\": \"action\",\n\t\"setup.fetchAssetBody\": \"action\",\n\t\"setup.auditAssetBodies\": \"action\",\n\t\"setup.repairAssetBody\": \"action\",\n\t\"setup.listFailedAuditReceipts\": \"action\",\n\t\"setup.markPersonalSetupAssetDormantFromSync\": \"mutation\",\n\t\"setup.resolveSemanticRefs\": \"query\",\n\t\"setup.updateLastProjectedHash\": \"mutation\",\n\t\"setup.getSkillSystemHealth\": \"query\",\n\t\"setup.recordActivationReceipt\": \"mutation\",\n\t\"setup.recordSetupInvocation\": \"mutation\",\n\t\"setup.getUserActivationState\": \"query\",\n\t\"setup.getPbSetupState\": \"query\",\n\t\"setup.getSkillBody\": \"action\",\n\t\"setup.stampMcpOnlySurface\": \"mutation\",\n\t\"setup.stampDetectedSurfaces\": \"mutation\",\n\t\"setup.getKey32Snapshot\": \"query\",\n\t\"setup.getKey33Snapshot\": \"query\",\n\t\"gaps.record\": \"mutation\",\n\t\"gaps.resolve\": \"mutation\",\n\t\"gaps.top\": \"query\",\n\t\"gaps.stats\": \"query\",\n\t\"agent.startSession\": \"mutation\",\n\t\"agent.resumeSession\": \"mutation\",\n\t\"agent.closeSession\": \"mutation\",\n\t\"agent.markOriented\": \"mutation\",\n\t\"agent.touchSession\": \"mutation\",\n\t\"agent.recordActivity\": \"mutation\",\n\t\"agent.getSession\": \"query\",\n\t\"agent.getActiveSession\": \"query\",\n\t\"agent.recentSessions\": \"query\",\n\t\"agent.activityStats\": \"query\",\n\t\"agent.getActivityByDay\": \"query\",\n\t\"agent.validateSession\": \"query\",\n\t\"agent.getSessionWrapup\": \"query\",\n\t\"agent.recordWrapup\": \"mutation\",\n\t\"agent.reportOrientMetric\": \"mutation\",\n\t\"agent.listSessions\": \"query\",\n\t\"agent.showConversation\": \"query\",\n\t\"usage.getWorkspaceSummary\": \"query\",\n\t\"quality.evaluateHeuristicAndSchedule\": \"mutation\",\n\t\"quality.reEvaluateEntry\": \"mutation\",\n\t\"quality.evaluateAtCapture\": \"action\",\n\t\"quality.evaluateAtCommit\": \"action\",\n\t\"quality.evaluateForReview\": \"action\",\n\t\"quality.getCachedVerdict\": \"query\",\n\t\"quality.getLatestVerdictForEntry\": \"query\",\n\t\"quality.spineCheck\": \"action\",\n\t\"quality.scheduleSpineCheck\": \"mutation\",\n\t\"quality.getLatestSpineVerdict\": \"query\",\n\t\"onboarding.chat\": \"action\",\n\t\"workspace.health\": \"query\",\n\t\"workspace.healthAll\": \"query\",\n\t\"workspace.backfill\": \"mutation\",\n\t\"workspace.backfillAll\": \"mutation\",\n\t\"authorityDomains.readiness\": \"query\",\n\t\"authorityDomains.add\": \"mutation\",\n\t\"authorityDomains.propose\": \"action\",\n\t\"authorityDomains.review\": \"query\",\n\t\"authorityDomains.queueKnownTag\": \"mutation\",\n\t\"authorityDomains.ratify\": \"mutation\",\n\t\"authorityDomains.reject\": \"mutation\",\n\t\"authorityDomains.discardPending\": \"mutation\",\n\t\"authorityDomains.recordSample\": \"mutation\",\n\t\"authorityDomains.benchmark\": \"action\",\n\t\"authorityDomains.activateCutover\": \"mutation\",\n\t\"authorityDomains.principleDistribution\": \"query\",\n\t\"performance.getVitalsSummary\": \"query\",\n\t\"performance.getApiOverview\": \"query\",\n\t\"performance.getRouteBreakdown\": \"query\",\n\t\"performance.getSlowSamples\": \"query\",\n\t\"performance.getSampleCount\": \"query\",\n\t\"chainwork.listTypes\": \"query\",\n\t\"chainwork.getChainType\": \"query\",\n\t\"chainwork.scoreRun\": \"query\",\n\t\"chainwork.getArtifact\": \"query\",\n\t\"chainwork.getWorkflowRun\": \"query\",\n\t\"chainwork.getLatestWorkflowRun\": \"query\",\n\t\"chainwork.recordWorkflowCheckpoint\": \"mutation\",\n\t\"chainwork.finalizeWorkflowRun\": \"action\",\n\t\"chainwork.submitToKG\": \"action\",\n\t\"chainwork.generate\": \"action\",\n\t\"chainwork.getLastVerifiedBrief\": \"query\",\n\t\"gitchain.createChain\": \"mutation\",\n\t\"gitchain.editLink\": \"mutation\",\n\t\"gitchain.updateChain\": \"mutation\",\n\t\"gitchain.getChain\": \"query\",\n\t\"gitchain.listChains\": \"query\",\n\t\"gitchain.getHistory\": \"query\",\n\t\"gitchain.listCommits\": \"query\",\n\t\"gitchain.commitChain\": \"mutation\",\n\t\"gitchain.diffVersions\": \"mutation\",\n\t\"gitchain.runGate\": \"query\",\n\t\"gitchain.createBranch\": \"mutation\",\n\t\"gitchain.listBranches\": \"mutation\",\n\t\"gitchain.checkConflicts\": \"mutation\",\n\t\"gitchain.mergeBranch\": \"mutation\",\n\t\"gitchain.addComment\": \"mutation\",\n\t\"gitchain.resolveComment\": \"mutation\",\n\t\"gitchain.listComments\": \"mutation\",\n\t\"gitchain.revertChain\": \"mutation\",\n\t\"staging.getCommittedSourceRefs\": \"query\",\n\t\"staging.commitStagingEntryWithClassification\": \"action\",\n\t\"governance.listProposals\": \"query\",\n\t\"governance.countOpenProposals\": \"query\",\n\t\"governance.respondToProposal\": \"mutation\",\n\t\"maps.createMap\": \"mutation\",\n\t\"maps.createAudienceMapSet\": \"mutation\",\n\t\"maps.addToSlot\": \"mutation\",\n\t\"maps.removeFromSlot\": \"mutation\",\n\t\"maps.replaceInSlot\": \"mutation\",\n\t\"maps.commitMap\": \"mutation\",\n\t\"maps.getMap\": \"query\",\n\t\"maps.listMaps\": \"query\",\n\t\"maps.getJourneyMapWithEnrichment\": \"query\",\n\t\"maps.listJourneyMaps\": \"query\",\n\t\"maps.listMapCommits\": \"query\",\n};\n\n/**\n * NULL-PROTOTYPE, deliberately (PR #533 review, Codex P2).\n *\n * A plain object literal inherits Object.prototype, so an unknown route name that\n * collides with an inherited member — `toString`, `constructor`, `valueOf` — resolves to a\n * TRUTHY function instead of falling through to the widest-budget default below. That\n * yielded `AbortSignal.timeout(undefined)`, which THROWS before the call ever reaches the\n * gateway — turning the fail-safe default into a hard failure.\n *\n * The SSOT scans an array (`GATEWAY_ROUTES.find`) and has no such hole, so a plain-object\n * copy DISAGREED with the source it is generated from — precisely the connector drift this\n * file exists to end. A null prototype restores parity for every consumer of the map at\n * once (`latencyBudgetMsForRoute` here, `routeMayMutate` in both connectors' seams) rather\n * than guarding each lookup and leaving the next one to rediscover it.\n */\nexport const ROUTE_TYPE_BY_NAME: Readonly<Record<string, GatewayRouteType>> = Object.assign(\n\tObject.create(null) as Record<string, GatewayRouteType>,\n\tROUTE_TYPE_ENTRIES,\n);\n\n/** Budget for a route TYPE. Total over the three kinds the registry can produce. */\nexport function latencyBudgetMsForRouteType(type: GatewayRouteType): number {\n\treturn ROUTE_LATENCY_BUDGET_MS[type];\n}\n\n/**\n * Budget for a route by NAME.\n *\n * FAILS SAFE, DELIBERATELY: an unknown name gets the WIDEST budget, not the tightest and\n * not a throw. Guessing low on a route we cannot classify would abort a call still\n * legitimately in flight and re-create the exact false-failure this work package removes.\n * Guessing high only delays surfacing a genuinely stuck call — and an unknown name is\n * almost always a caller bug the gateway rejects in milliseconds anyway.\n */\nexport function latencyBudgetMsForRoute(routeName: string): number {\n\t// `Object.hasOwn`, not truthiness: the override table is a plain literal, so a bare\n\t// lookup would resolve INHERITED members to a truthy value. Same hole the null\n\t// prototype closes above, guarded here because this table stays a plain literal.\n\tif (Object.hasOwn(ROUTE_LATENCY_BUDGET_OVERRIDE_MS, routeName)) {\n\t\treturn (ROUTE_LATENCY_BUDGET_OVERRIDE_MS as Record<string, number>)[routeName];\n\t}\n\tconst type = ROUTE_TYPE_BY_NAME[routeName];\n\treturn type ? latencyBudgetMsForRouteType(type) : WIDEST_ROUTE_LATENCY_BUDGET_MS;\n}\n","/**\n * The gateway seam's MEMORY — what each tenant's calls have added up to, kept per workspace.\n *\n * Split out of ./gatewaySeam.ts during the PR #533 review round at the 500-LOC ratchet's\n * insistence (STD-2 / DEC-1504), and the ratchet was reading the design correctly: classifying\n * how one call failed and remembering what a tenant's calls have cost are different jobs with\n * different reasons to change. gatewaySeam.ts keeps the error taxonomy and the recording\n * orchestration; this module owns the stores and the invariant that makes them safe.\n *\n * THAT INVARIANT IS TENANT PARTITIONING. In HTTP transport one MCP process multiplexes many\n * tenants (../http.ts's per-request `runWithAuth`), so every structure here is keyed by\n * workspace, bounded per workspace, and readable only by naming the scopes you own. Both\n * stores share one LRU (`touchBoundedWorkspaceMap`) precisely so they can never disagree about\n * which tenants are retained — a buffer that evicted a workspace the counters kept would\n * report one tenant's rates beside another tenant's call list.\n *\n * ../gatewaySeam.ts re-exports this module's public surface, so ../client.ts and the test mocks\n * that enumerate client.js's exports keep working unchanged.\n */\n// ─── Audit buffer ─────────────────────────────────────────────────────\n\nexport interface AuditEntry {\n ts: string;\n /**\n * Monotonic per-process recording order — the key `getAuditLog` merges a caller's buckets by.\n *\n * `ts` cannot do this job: it is ISO-8601 with MILLISECOND resolution, and several gateway\n * calls routinely land inside one millisecond, so sorting by it leaves same-ms entries in\n * whatever order the buckets happened to be visited. The old single global buffer got true\n * insertion order for free; partitioning it per workspace is what makes an explicit ordering\n * key necessary. Internal to the seam — the audit view renders `ts`, never this.\n */\n seq: number;\n fn: string;\n workspace: string;\n status: \"ok\" | \"error\";\n durationMs: number;\n error?: string;\n /** For compound tools: tool name and action for audit display */\n toolContext?: { tool: string; action?: string };\n /**\n * WP-575: the declared latency budget this call was given, in ms. Recorded on every entry\n * (not just failures) so an observed duration can be read against the budget it was\n * actually judged by, rather than against whatever the budget happens to be today.\n */\n budgetMs?: number;\n /** WP-575: true when this call failed by hitting `budgetMs`, not by a server rejection. */\n timedOut?: boolean;\n}\n\n/**\n * PER-WORKSPACE, not one global ring (PR #533 review, Codex P2).\n *\n * This buffer pre-dates the work package (it lived in client.ts) and was a single process-wide\n * array evicted with `shift()`. In HTTP transport one process multiplexes many tenants, and\n * `lib/auditView.ts` filters to the caller AFTER eviction has already happened — so any tenant\n * making 50 calls silently erased every other tenant's entries. The quiet tenant's\n * `health action=audit` then reported nothing at all while its own lifetime timeout counters\n * were nonzero: a noisy neighbour deleting exactly the KEY-79 evidence this work package\n * exists to make readable.\n *\n * The cap is now PER WORKSPACE, so 50 is a floor on what each tenant can see rather than a\n * pool they compete for, and the map of workspaces is bounded by the same `MAX_SEAM_WORKSPACES`\n * LRU as the counters — same `touchBoundedWorkspaceMap` helper, so the two can never disagree\n * about which tenants are retained.\n */\nconst AUDIT_BUFFER_SIZE = 50;\nconst auditBufferByWorkspace = new Map<string, AuditEntry[]>();\n/** Deliberately NOT reset by `__resetGatewaySeamCountersForTest` — order must stay strictly increasing for the life of the process, and nothing reads it as a count. */\nlet nextAuditSeq = 0;\n\n/**\n * This caller's slice of the audit buffer, oldest first, merged across the scopes it owns.\n *\n * Takes scopes for the same reason `getMergedGatewaySeamCounters` does — a session's first\n * call is filed under `cacheScope()` before `resolveWorkspace` returns — and the two MUST\n * merge over the same scope set or the log and the counters would describe different callers.\n * There is deliberately no \"give me everything\" shape: that is what leaked across tenants.\n *\n * Merged by `seq`, the recording order — within one workspace that is exactly what the old\n * global buffer returned; across a caller's two scopes it interleaves them, which is what\n * \"this caller's recent calls\" means. See `AuditEntry.seq` for why not `ts`.\n */\nexport function getAuditLog(scopes: readonly string[]): readonly AuditEntry[] {\n const merged: AuditEntry[] = [];\n for (const scope of new Set(scopes)) {\n const bucket = auditBufferByWorkspace.get(scope);\n if (bucket) merged.push(...bucket);\n }\n return merged.sort((a, b) => a.seq - b.seq);\n}\n\n// ─── Rolling latency + error counters (WP-575 element 5) ──────────────\n\n/**\n * WHY COUNTERS AND NOT JUST THE BUFFER: `auditBuffer` holds the last 50 entries, so it\n * answers \"what happened recently\" but cannot answer \"what is the failure rate\" — the older\n * entries are gone. KEY-79 measures exactly that (batch success rate, chain.createEntry\n * false-failure rate), and until now the only way to report it was to self-grade. These\n * counters are cumulative for the process lifetime and never evict, so the rate is real.\n *\n * `timeouts` is tracked SEPARATELY from `errors` on purpose, and it is the number this work\n * package exists to drive to zero: a timeout is the class of failure where the server may\n * actually have succeeded, so lumping it in with genuine server rejections is what made the\n * false-failure rate unmeasurable in the first place. Note `timeouts` is a SUBSET of\n * `errors` — every timeout is also counted there, so `errors` remains \"all failures.\"\n */\nexport interface GatewaySeamCounters {\n calls: number;\n errors: number;\n timeouts: number;\n /** Summed wall-clock ms across all calls — divide by `calls` for the mean. */\n totalDurationMs: number;\n /** Slowest single call observed, in ms. */\n maxDurationMs: number;\n /** Per-route breakdown, same fields, for attributing a rate to the route that caused it. */\n byRoute: Record<string, { calls: number; errors: number; timeouts: number; totalDurationMs: number; maxDurationMs: number }>;\n}\n\n/** A null-prototype `byRoute` — see `emptySeamCounters` for why it must never be a plain `{}`. */\nfunction emptyByRoute(): GatewaySeamCounters[\"byRoute\"] {\n return Object.create(null) as GatewaySeamCounters[\"byRoute\"];\n}\n\n/**\n * `byRoute` is NULL-PROTOTYPE, deliberately (PR #533 review round 3, Copilot).\n *\n * It is indexed by an arbitrary route NAME at three sites — `recordSeamCounters`, the snapshot\n * below, and `getMergedGatewaySeamCounters` — each via `byRoute[fn] ??= {...}`. On a plain `{}`\n * a route named `toString`/`constructor`/`valueOf` resolves to the INHERITED function, which is\n * neither null nor undefined, so `??=` does not assign and the following `route.calls += 1`\n * writes onto a function and yields **NaN** — silently corrupting the very counters KEY-79 is\n * measured from, with no error to notice. This is the same Object.prototype collision this PR\n * already closed for the route→type map; `byRoute` was the sibling left unswept.\n */\nfunction emptySeamCounters(): GatewaySeamCounters {\n return { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0, byRoute: emptyByRoute() };\n}\n\n/**\n * PR #533 review, Codex P2: in HTTP transport mode one process multiplexes MANY tenants\n * (see http.ts's per-request `runWithAuth`), so a single module-scope counters object — what\n * this used to be — let any authenticated caller read every OTHER tenant's process-lifetime\n * call/error/timeout history through `workspace action=audit`. Keying by workspace makes the\n * isolation structural: `getGatewaySeamCounters` can only ever return the slice for the\n * workspace it's asked for, so a caller who doesn't know another tenant's workspaceId cannot\n * reach that tenant's counters. In stdio mode there is exactly one workspace, so this degrades\n * to the old single-bucket behavior with no observable change.\n *\n * BOUNDED MAP: an HTTP process can be handed at most `MAX_SESSIONS` (200, http.ts) concurrent\n * sessions, and each session belongs to one workspace — so 200 is also the ceiling on distinct\n * workspaces a single process can plausibly be serving at once. Cap at that same number so a\n * long-lived multi-tenant process can't grow this map without limit.\n *\n * EVICT LEAST-RECENTLY-RECORDED, not oldest-inserted. This started as insertion-order FIFO\n * borrowed from `_sessionLifecycleByStream`, and that precedent does not transfer: its keys are\n * write-once per stream, so FIFO and LRU coincide there. A `workspaceId` is a STABLE key reused\n * for the life of the process, so under FIFO the workspace that has been active LONGEST is the\n * first evicted — the single most active tenant loses its counters the moment workspace #201\n * appears, and its next `workspace action=audit` reports zeros as if it had never made a call.\n * That silently defeats the KEY-79 observability this fix exists to provide. Recording a call\n * re-inserts the key so it moves to the end of the Map's insertion order, making eviction\n * genuinely least-recently-used — the same intent as http.ts's `lastAccess` sort, without\n * carrying a timestamp per bucket.\n */\nconst MAX_SEAM_WORKSPACES = 200;\nconst seamCountersByWorkspace = new Map<string, GatewaySeamCounters>();\n\n/**\n * Fetch-or-create a workspace's bucket, refreshing its LRU position and evicting the\n * least-recently-recorded workspace once the map is full.\n *\n * Shared by the counters and the audit buffer rather than written twice: both are keyed by\n * workspace, both are bounded by the same ceiling, and both must retain the same tenants — a\n * buffer that evicted a workspace the counters kept (or vice versa) would report a tenant's\n * rates against another tenant's call list. The eviction reasoning is in the comment above.\n */\nfunction touchBoundedWorkspaceMap<V>(map: Map<string, V>, workspace: string, create: () => V): V {\n const existing = map.get(workspace);\n if (existing !== undefined) {\n // Move to the end so eviction below is least-recently-RECORDED, not oldest-inserted.\n // A Map re-`set` of an existing key keeps the value and refreshes its insertion position.\n map.delete(workspace);\n map.set(workspace, existing);\n return existing;\n }\n if (map.size >= MAX_SEAM_WORKSPACES) {\n const leastRecentlyRecorded = map.keys().next().value;\n if (leastRecentlyRecorded !== undefined) map.delete(leastRecentlyRecorded);\n }\n const created = create();\n map.set(workspace, created);\n return created;\n}\n\nfunction getOrCreateWorkspaceCounters(workspace: string): GatewaySeamCounters {\n return touchBoundedWorkspaceMap(seamCountersByWorkspace, workspace, emptySeamCounters);\n}\n\nfunction getOrCreateWorkspaceAuditBuffer(workspace: string): AuditEntry[] {\n return touchBoundedWorkspaceMap(auditBufferByWorkspace, workspace, () => []);\n}\n\n/**\n * Snapshot of ONE workspace's gateway seam counters — deep-copied so a caller can hold it\n * across further calls without it mutating underneath them.\n *\n * PR #533 review, Codex P2: `workspace` is required, not optional — there is no \"give me\n * everything\" call shape, because that shape is exactly what let one tenant read another's\n * history. A workspace with no recorded calls yet gets a fresh empty snapshot, never another\n * workspace's data and never the old global aggregate.\n */\nexport function getGatewaySeamCounters(workspace: string): GatewaySeamCounters {\n const counters = seamCountersByWorkspace.get(workspace);\n if (!counters) return emptySeamCounters();\n return {\n calls: counters.calls,\n errors: counters.errors,\n timeouts: counters.timeouts,\n totalDurationMs: counters.totalDurationMs,\n maxDurationMs: counters.maxDurationMs,\n // Copied onto a null prototype, not left as `Object.fromEntries`' plain object — the\n // snapshot is indexed by route name downstream (`getMergedGatewaySeamCounters`) and would\n // reintroduce the inherited-member collision `emptySeamCounters` documents.\n byRoute: Object.assign(emptyByRoute(), Object.fromEntries(Object.entries(counters.byRoute).map(([k, v]) => [k, { ...v }]))),\n };\n}\n\n/**\n * Snapshot merged across SEVERAL buckets belonging to the SAME caller.\n *\n * Exists because a session's calls are not all recorded under one key. `audit()` attributes a\n * call to `state().workspaceId ?? cacheScope()` (client.ts), and `workspaceId` is only set once\n * `resolveWorkspace` RETURNS — so every session's first call, `resolveWorkspace` itself, is\n * recorded under `cacheScope()`. Filtering the audit view to the resolved workspaceId alone\n * therefore hid that call forever, under-counting every tenant's own view of itself (including\n * in stdio mode, where the pre-fix global aggregate had shown it).\n *\n * Merging is safe precisely because `cacheScope()` is derived from the CALLER'S OWN API key —\n * both buckets are the same tenant, so this widens a caller's view of itself without ever\n * reaching another tenant's. Callers must only pass scopes they have proven belong to them.\n */\nexport function getMergedGatewaySeamCounters(scopes: readonly string[]): GatewaySeamCounters {\n const merged = emptySeamCounters();\n for (const scope of new Set(scopes)) {\n const part = getGatewaySeamCounters(scope);\n merged.calls += part.calls;\n merged.errors += part.errors;\n merged.timeouts += part.timeouts;\n merged.totalDurationMs += part.totalDurationMs;\n merged.maxDurationMs = Math.max(merged.maxDurationMs, part.maxDurationMs);\n for (const [route, r] of Object.entries(part.byRoute)) {\n const into = (merged.byRoute[route] ??= { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0 });\n into.calls += r.calls;\n into.errors += r.errors;\n into.timeouts += r.timeouts;\n into.totalDurationMs += r.totalDurationMs;\n into.maxDurationMs = Math.max(into.maxDurationMs, r.maxDurationMs);\n }\n }\n return merged;\n}\n\n/**\n * Test-only reset — the counters are process-lifetime cumulative by design.\n *\n * Clears the audit buffer too: both are per-workspace seam state written by the same\n * `recordGatewayCall`, so resetting one and leaving the other would let a test's assertions\n * about \"this tenant's calls\" read entries from the previous test.\n */\nexport function __resetGatewaySeamCountersForTest(): void {\n seamCountersByWorkspace.clear();\n auditBufferByWorkspace.clear();\n}\n\n/**\n * Append one recorded call to its workspace's buffer, stamping the ordering `seq`.\n *\n * The seq stamp lives here, with the store, rather than at the caller: it is the store's\n * ordering key and nothing outside should be able to mint one out of order.\n */\nexport function appendAuditEntry(entry: Omit<AuditEntry, \"seq\">): void {\n const stored: AuditEntry = { ...entry, seq: nextAuditSeq++ };\n const bucket = getOrCreateWorkspaceAuditBuffer(stored.workspace);\n bucket.push(stored);\n if (bucket.length > AUDIT_BUFFER_SIZE) bucket.shift();\n}\n\nexport function recordSeamCounters(\n workspace: string,\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n timedOut: boolean,\n): void {\n const seamCounters = getOrCreateWorkspaceCounters(workspace);\n const route = (seamCounters.byRoute[fn] ??= {\n calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0,\n });\n\n seamCounters.calls += 1;\n seamCounters.totalDurationMs += durationMs;\n if (durationMs > seamCounters.maxDurationMs) seamCounters.maxDurationMs = durationMs;\n route.calls += 1;\n route.totalDurationMs += durationMs;\n if (durationMs > route.maxDurationMs) route.maxDurationMs = durationMs;\n\n if (status === \"error\") {\n seamCounters.errors += 1;\n route.errors += 1;\n if (timedOut) {\n seamCounters.timeouts += 1;\n route.timeouts += 1;\n }\n }\n}\n/**\n * Human-readable seam summary for `health action=audit` (WP-575 element 5).\n *\n * Lives here, with the counters, rather than in the tool: the tool renders an audit view,\n * this module decides what the numbers MEAN — notably that a timeout gets its own line\n * instead of being folded into the error rate, because it is the failure class where the\n * server may actually have SUCCEEDED.\n */\nexport function formatGatewaySeamSummary(seam: GatewaySeamCounters): string {\n const lines = [\n \"\\n\\n---\\n\\n# Gateway seam (process lifetime)\\n\",\n `Calls: ${seam.calls} \\u2014 errors: ${seam.errors}, of which timeouts: ${seam.timeouts}`,\n ];\n if (seam.calls > 0) {\n lines.push(`Mean: ${Math.round(seam.totalDurationMs / seam.calls)}ms \\u2014 slowest: ${seam.maxDurationMs}ms`);\n }\n if (seam.timeouts > 0) {\n lines.push(`\\u26a0 ${seam.timeouts} call(s) hit their latency budget \\u2014 those outcomes are unknown, not failed.`);\n }\n return lines.join(\"\\n\");\n}\n","/**\n * The gateway seam — what happened on a `/api/aki` call, as opposed to how it was made.\n *\n * Split out of `./client.ts` during WP-575 (TEN-2917) at the 500-LOC ratchet's insistence\n * (STD-2 / DEC-1504), and the ratchet was reading the design correctly: client.ts's job is\n * to MAKE the call — resolve the deployment, attach auth, parse the envelope, cache reads.\n * Recording what the call cost, classifying how it failed, and deciding whether the caller\n * may safely retry are a different job with a different reason to change. Everything here\n * is downstream-of-the-response; nothing here knows how to send one.\n *\n * Deliberately free of any dependency on client.ts, so there is no import cycle: the two\n * pieces of per-call context this module cannot derive (the resolved workspace and the\n * active tool context) are PASSED IN by the caller rather than reached for.\n *\n * `./client.ts` re-exports this module's public surface, so existing importers (and the\n * test mocks that enumerate client.js's exports) keep working unchanged.\n *\n * What a tenant's calls ADD UP TO — the per-workspace audit buffer and the rolling counters —\n * moved to ./lib/gatewaySeamStore.ts in the PR #533 review round (STD-2 / DEC-1504 again:\n * classifying one failure and remembering a tenant's history are different jobs). Its surface\n * is re-exported below so this module stays the single import point for the seam.\n */\nimport { trackToolCall } from \"./analytics.js\";\nimport { ROUTE_TYPE_BY_NAME } from \"./generated/routeLatencyBudget.generated.js\";\nimport { appendAuditEntry, recordSeamCounters, type AuditEntry } from \"./lib/gatewaySeamStore.js\";\n\nexport {\n formatGatewaySeamSummary,\n getAuditLog,\n getGatewaySeamCounters,\n getMergedGatewaySeamCounters,\n __resetGatewaySeamCountersForTest,\n type AuditEntry,\n type GatewaySeamCounters,\n} from \"./lib/gatewaySeamStore.js\";\n\n/** Convex `/api/aki` error body — 4xx/5xx include `error`; structured codes include `code`. */\nexport class KernelCallError extends Error {\n readonly status: number;\n readonly code?: string;\n /** WP-316 S1a: Structured commit validation — required field keys missing from entry.data. */\n readonly missingRequiredFields?: string[];\n /** WP-316 S1a: Structured commit validation — field-level data errors. */\n readonly fieldErrors?: string[];\n /**\n * WP-465 slice ⑤: structured diagnostics carried by an `ok:false` kernel envelope\n * (e.g. `coherencyRefusals`, `blockers`). The gateway forwards these verbatim at\n * HTTP 200; without preserving them here a refused/blocked envelope would collapse\n * into a bare code+message and the caller could not surface the per-offender routes.\n */\n readonly diagnostics?: Record<string, unknown>;\n constructor(\n message: string,\n status: number,\n code?: string,\n missingRequiredFields?: string[],\n fieldErrors?: string[],\n diagnostics?: Record<string, unknown>,\n ) {\n super(message);\n this.name = \"KernelCallError\";\n this.status = status;\n this.code = code;\n this.missingRequiredFields = missingRequiredFields;\n this.fieldErrors = fieldErrors;\n this.diagnostics = diagnostics;\n }\n}\n\n/**\n * A gateway call that hit its declared latency budget (WP-575, TEN-2917).\n *\n * WHY THIS IS ITS OWN TYPE and not folded into the generic network error client.ts used to\n * throw for every `fetch` rejection: a budget abort and a connection refusal are opposite\n * facts about the server, and the caller has to be able to tell them apart. A refusal proves\n * nothing ran. A budget abort proves only that WE stopped waiting — the server may well have\n * completed the write. Collapsing both into \"network error\" is what let MCP report a hard\n * failure on `chain.createEntry` calls that had SUCCEEDED, and left callers with no way to\n * say so. `mayHaveLanded` carries that distinction structurally so a caller can surface a\n * partial-success signal instead of a bare failure (see tools/smart-capture.ts).\n *\n * The `name` below is a LOAD-BEARING contract, pinned by a test: smart-capture discriminates\n * on it rather than `instanceof`, because class identity survives neither a mocked module\n * nor module duplication by a bundler.\n */\nexport class GatewayTimeoutError extends Error {\n /** Gateway route name that was aborted. */\n readonly fn: string;\n /** The declared budget, in ms, that this route was given (see routeLatencyBudget). */\n readonly budgetMs: number;\n /** Wall-clock ms actually spent before the abort. */\n readonly elapsedMs: number;\n /**\n * True when the aborted call could have landed a write server-side despite this client\n * giving up. Derived by `mayHaveLandedOnTimeout` from the route contract's own function\n * type (`mutation`/`action` mutate; `query` does not) AND the call's own arguments (a\n * `preview: true` dry run writes nothing) — never guessed from the name.\n */\n readonly mayHaveLanded: boolean;\n constructor(fn: string, budgetMs: number, elapsedMs: number, mayHaveLanded: boolean) {\n super(\n `MCP call \"${fn}\" exceeded its ${budgetMs}ms latency budget (waited ${elapsedMs}ms).` +\n (mayHaveLanded\n ? \" This route writes, so the server may have completed it — verify before retrying.\"\n : \"\"),\n );\n this.name = \"GatewayTimeoutError\";\n this.fn = fn;\n this.budgetMs = budgetMs;\n this.elapsedMs = elapsedMs;\n this.mayHaveLanded = mayHaveLanded;\n }\n}\n\n/**\n * Does this route mutate state? (WP-575)\n *\n * Read off the route contract's own Convex function type — `mutation` and `action` can\n * write, `query` cannot — never inferred from the route's NAME. Name-based guessing is how\n * client.ts's read-cache `isWrite` has to work (it predates the typed contract and covers\n * cache invalidation, where over-invalidating is merely wasteful), but the stakes here are\n * different: this decides whether the agent is told a timed-out write may have landed.\n * Telling it \"nothing landed\" when something did is the worse error, so an UNKNOWN route\n * fails safe to `true` — same direction as the budget's own unknown-name fallback.\n */\nexport function routeMayMutate(fn: string): boolean {\n const type = ROUTE_TYPE_BY_NAME[fn];\n return type === undefined || type !== \"query\";\n}\n\n/**\n * Was this call a DRY RUN? (PR #533 review, Codex P2)\n *\n * `preview: true` is a gateway-wide contract meaning \"run every validation, write nothing\" —\n * not a per-route convenience. Every registered route that accepts the argument returns before\n * its first write, verified one by one: `chain.createEntry`\n * (convex/agentKnowledge/entries.ts:498, returning at :583-599 with the first write at :601 —\n * and its action wrapper skips the contradiction detector entirely at :915),\n * `chain.commitEntry` (:2537, before createPublishedVersion/createProposalForEntry/\n * recordSessionActivity), `chain.createEntryRelation`\n * (convex/agentKnowledge/relations.ts:152 and :186, before any insert or scheduler), and\n * `quality.evaluateHeuristicAndSchedule` (convex/intelligence/qualityCoaching.ts:1101,\n * documented \"NEVER persist, schedule, or stamp\", before the insert at :1110).\n */\nexport function isDryRunCall(args: unknown): boolean {\n return (args as { preview?: unknown } | null | undefined)?.preview === true;\n}\n\n/**\n * Could a timed-out call have LANDED A WRITE? (PR #533 review, Codex P2)\n *\n * The route's type answers \"can this route write at all\"; the call's own arguments answer\n * \"was this particular call asking it to\". Both are needed, and deciding it here — once,\n * where both are in scope — is what keeps the four surfaces that report a timeout consistent:\n * `GatewayTimeoutError`'s message, lib/captureTimeoutOutcome.ts, lib/batchTimeoutCohort.ts,\n * and the batch-preview markdown in lib/batchCaptureOutput.ts. Deriving it from the type\n * alone told a dry run its write \"may have completed\" and warned against retrying — beside\n * that same response's own \"no DB writes\" line.\n *\n * The fail-safe direction is unchanged for everything else: an unknown route still mutates\n * as far as we know (`routeMayMutate`), because claiming \"nothing landed\" when something did\n * is the worse error. A dry run is the one case where \"nothing landed\" is a CONTRACT, not a\n * guess — see `isDryRunCall` for the route-by-route verification.\n */\nexport function mayHaveLandedOnTimeout(fn: string, args: unknown): boolean {\n return routeMayMutate(fn) && !isDryRunCall(args);\n}\n\n/**\n * Classify a failed gateway call and throw. Shared by BOTH phases a call can fail in — the\n * headers phase (`fetch` rejects) and the body phase (`res.json()` rejects while the abort\n * signal is still live, PR #533 review Codex P2) — because the phase changes what happened,\n * not what it MEANS.\n *\n * WP-575: a budget abort and a connection failure are opposite facts, and only the former\n * leaves the server's own outcome unknown. `AbortSignal.timeout` rejects with a DOMException\n * named \"TimeoutError\"; a caller-driven abort surfaces as \"AbortError\". Treat both as \"we\n * stopped waiting\", never as \"the server failed.\"\n *\n * `record` is injected rather than called directly so this module keeps owing nothing to\n * client.ts (see the file header) — the caller supplies the workspace/tool context.\n *\n * `args` is taken rather than a pre-computed `mayHaveLanded` so the derivation stays inside\n * this module (`mayHaveLandedOnTimeout`): two call sites computing it themselves is how the\n * two connectors drifted apart in the first place.\n */\nexport function throwClassifiedGatewayFailure(\n err: any,\n fn: string,\n args: unknown,\n budgetMs: number,\n elapsedMs: number,\n phase: \"network\" | \"response body\",\n record: (auditMsg: string, timedOut: boolean) => void,\n): never {\n if (err?.name === \"TimeoutError\" || err?.name === \"AbortError\") {\n const timeoutErr = new GatewayTimeoutError(fn, budgetMs, elapsedMs, mayHaveLandedOnTimeout(fn, args));\n record(timeoutErr.message, true);\n throw timeoutErr;\n }\n const detail = err?.message ?? String(err);\n record(phase === \"network\" ? detail : `${phase}: ${detail}`, false);\n throw new Error(`MCP call \"${fn}\" ${phase} error: ${detail}`);\n}\n\n\n/**\n * What an unreadable response BODY means — and it depends on the status.\n *\n * `fetch` resolves at headers while the budget signal is still live, so a stalled body aborts\n * after the call already succeeded at the transport level. Two different situations land here\n * and they deserve opposite answers (PR #533 review round 6):\n *\n * - **Status was OK** — a 2xx whose body never arrived. The server's outcome is genuinely\n * unknown, which is the timeout this work package exists to name. Classify and throw.\n * - **Status was NOT OK** — the server reached a verdict and reported it; only its error body\n * stalled. Calling that a budget timeout trades a real status and code (a 429's\n * `retryAfterSeconds`, a 4xx's validation code) for \"the server may have completed it\n * anyway, verify before retrying\" — about a call the server explicitly failed. Yield an\n * empty body instead and let the caller's own status branch report it, which is what\n * non-OK responses have always done.\n *\n * Lives here rather than in the caller's catch because this is the taxonomy's question, not\n * the call-maker's: client.ts sends requests, this module decides what a failure MEANT.\n */\nexport function emptyBodyOrThrowClassified<T>(\n err: any,\n fn: string,\n args: unknown,\n budgetMs: number,\n elapsedMs: number,\n statusOk: boolean,\n record: (auditMsg: string, timedOut: boolean) => void,\n): T {\n if (statusOk) throwClassifiedGatewayFailure(err, fn, args, budgetMs, elapsedMs, \"response body\", record);\n return {} as T;\n}\n\n// ─── Recording one call ───────────────────────────────────────────────\n\nfunction shouldLogAudit(status: \"ok\" | \"error\"): boolean {\n return status === \"error\" || process.env.MCP_DEBUG === \"1\";\n}\n\n/**\n * Record one completed gateway call: buffer entry, cumulative counters, PostHog, stderr.\n *\n * `workspace` and `toolContext` are parameters rather than module-level lookups precisely so\n * this module owes nothing to client.ts — see the file header.\n */\nexport function recordGatewayCall(params: {\n fn: string;\n status: \"ok\" | \"error\";\n durationMs: number;\n workspace: string;\n errorMsg?: string;\n toolContext?: { tool: string; action?: string } | null;\n /** WP-575: the budget this call was judged by, and whether it was what killed it. */\n budgetMs?: number;\n timedOut?: boolean;\n}): void {\n const { fn, status, durationMs, workspace, errorMsg, toolContext, budgetMs, timedOut } = params;\n const ts = new Date().toISOString();\n\n const entry: Omit<AuditEntry, \"seq\"> = { ts, fn, workspace, status, durationMs };\n if (errorMsg) entry.error = errorMsg;\n if (toolContext) entry.toolContext = toolContext;\n if (budgetMs !== undefined) entry.budgetMs = budgetMs;\n if (timedOut) entry.timedOut = true;\n appendAuditEntry(entry);\n\n recordSeamCounters(workspace, fn, status, durationMs, timedOut === true);\n trackToolCall(fn, status, durationMs, workspace, errorMsg);\n\n if (!shouldLogAudit(status)) return;\n\n const base =\n `[MCP-AUDIT] ${ts} fn=${fn} workspace=${workspace} status=${status} duration=${durationMs}ms` +\n `${budgetMs !== undefined ? ` budget=${budgetMs}ms` : \"\"}${timedOut ? \" timedOut=true\" : \"\"}`;\n process.stderr.write(\n status === \"error\" && errorMsg ? `${base} error=${JSON.stringify(errorMsg)}\\n` : `${base}\\n`,\n );\n}\n\n","/**\n * MCP client — communicates with the Convex HTTP Action gateway.\n *\n * Dual mode:\n * stdio — single user, API key from env, module-level state\n * http — multi-user, API key from AsyncLocalStorage, per-key state\n *\n * Configuration:\n * PRODUCTBRAIN_API_KEY — pb_sk_* key (stdio mode; http mode gets it per-request)\n * CONVEX_SITE_URL — (optional) Convex deployment URL, defaults to cloud\n */\n\nimport type { GatewayRouteReturnByName } from \"@productbrain/kernel-client\";\n// WP-575 (TEN-2917): budget DECLARED at the route contract, delivered as a drift-checked copy.\nimport { latencyBudgetMsForRoute } from \"./generated/routeLatencyBudget.generated.js\";\n// WP-575 / STD-2: what a call COST and how it FAILED lives in ./gatewaySeam.ts — this file\n// makes the call, that one records it. Re-exported below so existing importers still resolve.\nimport { KernelCallError, emptyBodyOrThrowClassified, recordGatewayCall, throwClassifiedGatewayFailure } from \"./gatewaySeam.js\"; // GatewayTimeoutError/routeMayMutate deliberately absent: re-exported below (needs no import) and matched by `name`, respectively (review round 3).\nexport {\n GatewayTimeoutError,\n KernelCallError,\n getAuditLog,\n getGatewaySeamCounters,\n formatGatewaySeamSummary,\n __resetGatewaySeamCountersForTest,\n type AuditEntry,\n type GatewaySeamCounters,\n} from \"./gatewaySeam.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { trackCompoundToolAction } from \"./analytics.js\";\nimport { getRequestApiKey, getRequestMcpSessionId, getKeyState, hashKey, type KeyState } from \"./auth.js\";\nimport type { NextAction } from \"./envelope.js\";\nimport { MCP_NPX_PACKAGE } from \"./cli/config-writer.js\";\nimport { warnOnProdFallthrough } from \"./prod-fallthrough.js\";\nimport { resolveConversationId } from \"./lib/conversation.js\";\nimport type { AgentSessionStartNotice } from \"./lib/sessionNotices.js\"; // WP-584 offset (STD-2/DEC-1504)\nimport { recordToolAction } from \"./lib/toolActionCounts.js\"; // WP-584 offset (STD-2/DEC-1504)\nimport { parseFallbackUrls, probeDeploymentCandidates } from \"./lib/deploymentUrlResolver.js\"; // WP-575 offset (STD-2/DEC-1504)\n\n// ─── Conversation identity (WP-479 E2) ─────────────────────────────────\n\n// The MCP server is a subprocess of the harness (Claude Code / Cursor / Codex) — it resolves its\n// conversation identity ONCE at process startup from its own env and holds it for the process\n// lifetime (there is no per-request or per-key variation: one MCP server process is one\n// conversation). `undefined` means \"not yet resolved\"; `null` is a legitimate resolved value\n// (no identity signal present — legacy behavior).\nlet _conversationId: string | null | undefined;\n\nexport function getConversationId(): string | null {\n // In HTTP transport mode a single process multiplexes many `Mcp-Session-Id` streams. The\n // env-derived conversation id is process-global, so it can't identify a stream — and returning null\n // (an earlier fix) is worse: `startSession`'s null-conversation reuse then collapses every HTTP\n // stream sharing an API key onto ONE server `agentSessions` row (Codex P1). The `Mcp-Session-Id` IS\n // the stable per-stream identity, so use it as the conversation id for HTTP starts/recovery — each\n // stream becomes its own server-side conversation. STDIO (one process = one conversation) keeps the\n // env-derived id.\n const mcpSid = getRequestMcpSessionId();\n if (mcpSid) return `mcp:${mcpSid}`;\n if (getRequestApiKey()) return null; // HTTP request before a session id is assigned (initialize)\n if (_conversationId === undefined) _conversationId = resolveConversationId();\n return _conversationId;\n}\n\n// ─── Tool Context (for audit action logging) ─────────────────────────────\n\nconst toolContextStore = new AsyncLocalStorage<{ tool: string; action?: string }>();\n\n/**\n * Run a callback with tool context for audit logging.\n * Compound tools should wrap their handler with this so workspace action=audit can distinguish\n * e.g. entries action=get from entries action=search.\n */\nexport function runWithToolContext<T>(\n ctx: { tool: string; action?: string },\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n recordToolAction(ctx.tool, ctx.action);\n trackCompoundToolAction(ctx.tool, ctx.action, state().workspaceId ?? \"unresolved\");\n return toolContextStore.run(ctx, fn);\n}\n\nfunction getToolContext(): { tool: string; action?: string } | null {\n return toolContextStore.getStore() ?? null;\n}\n\nexport const DEFAULT_CLOUD_URL = \"https://gateway.productbrain.io\";\n\n// ─── Read Cache (Batch A: sub-200ms repeat calls) ─────────────────────\n\nconst CACHE_TTL_MS = 60_000; // 60s per plan\nconst CACHEABLE_FNS = [\n \"chain.getOrientEntries\",\n \"chain.gatherContext\",\n \"chain.graphGatherContext\",\n \"chain.taskAwareGatherContext\",\n \"chain.journeyAwareGatherContext\",\n \"chain.assembleBuildContext\",\n] as const;\n\nfunction isCacheable(fn: string): boolean {\n return (CACHEABLE_FNS as readonly string[]).includes(fn);\n}\n\n// PR #405 review (Codex P2): `chain.evaluateCoherence` (read-only) fell through this pattern\n// (no `evaluate` verb recognized), misclassifying every successful coherence check on the\n// orient/start/wrapup hot path as a write and discarding cacheable results for no reason.\n// PR #517 round 1 (Codex P2): same fix for `chain.shapeAdvisories`/`chain.showShapeAdvisory`.\n// PR #519 review round 3 (Codex P2): `chain.shapeAdvisorySummary` (orientShapeAdvisorySummary,\n// read-only) fell through this same gap — a successful read was misclassified as a write,\n// clearing the shared 60s orient/context cache for every workspace in the process.\nconst READ_PATTERN =\n /^(chain\\.(get|list|search|batchGet|gather|graph|task|journey|assemble|workspace|score|absence|evaluate|shapeAdvisories|showShapeAdvisory|shapeAdvisorySummary)|chainwork\\.(get|list|score)|maps\\.(get|list)|gitchain\\.(get|list|diff|history|runGate))/i;\n\nfunction isWrite(fn: string): boolean {\n if (fn.startsWith(\"agent.\")) return false;\n return !READ_PATTERN.test(fn);\n}\n\ninterface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nconst readCache = new Map<string, CacheEntry<unknown>>();\n\nfunction cacheKey(fn: string, args: Record<string, unknown>): string {\n return `${fn}:${JSON.stringify(args)}`;\n}\n\nfunction getCached<T>(fn: string, args: Record<string, unknown>): T | undefined {\n if (!isCacheable(fn)) return undefined;\n const key = cacheKey(fn, args);\n const entry = readCache.get(key) as CacheEntry<T> | undefined;\n if (!entry || Date.now() > entry.expiresAt) {\n if (entry) readCache.delete(key);\n return undefined;\n }\n return entry.data;\n}\n\nfunction setCached<T>(fn: string, args: Record<string, unknown>, data: T): void {\n if (!isCacheable(fn)) return;\n const key = cacheKey(fn, args);\n readCache.set(key, { data, expiresAt: Date.now() + CACHE_TTL_MS });\n}\n\nfunction invalidateReadCache(): void {\n readCache.clear();\n}\n\n// ─── State Management ─────────────────────────────────────────────────\n\nconst _stdioState: KeyState = {\n workspaceId: null,\n workspaceSlug: null,\n workspaceName: null,\n workspaceCreatedAt: null,\n workspaceGovernanceMode: null,\n agentSessionId: null,\n apiKeyId: null,\n apiKeyScope: \"readwrite\",\n sessionOriented: false,\n sessionClosed: false,\n lastAccess: 0,\n deploymentUrl: null,\n};\n\n/**\n * Returns the active client state.\n * stdio: module-level singleton. http: per-API-key state from AsyncLocalStorage.\n */\nfunction state(): KeyState {\n const reqKey = getRequestApiKey();\n if (reqKey) return getKeyState(reqKey);\n return _stdioState;\n}\n\n/**\n * Cache partition key for the current request.\n * http — hashed API key (per-workspace isolation; hashKey is already used for\n * session binding in auth.ts, so the raw secret is never used as a map key).\n * stdio — fixed \"stdio\" sentinel (single user per process).\n *\n * The OAuth access_token IS the permanent pb_sk_ API key (stable per workspace\n * across refreshes — TEN-1143), so this key never churns mid-session.\n * Every per-workspace cache (collectionCache, smart-capture profile/hub) keys on this.\n */\nexport function cacheScope(): string {\n const key = getRequestApiKey();\n return key ? hashKey(key) : \"stdio\";\n}\n\n/**\n * Returns the active API key (request-scoped in HTTP mode, env in stdio mode).\n */\nfunction getActiveApiKey(): string {\n const fromRequest = getRequestApiKey();\n if (fromRequest) return fromRequest;\n const fromEnv = process.env.PRODUCTBRAIN_API_KEY;\n if (!fromEnv) throw new Error(\"No API key available — set PRODUCTBRAIN_API_KEY or provide Bearer token\");\n return fromEnv;\n}\n\n// ─── Agent Session State ──────────────────────────────────────────────\n\n/**\n * WP-479 review fix (Codex re-review): the SESSION-LIFECYCLE fields (active agentSessionId +\n * oriented/closed flags) must be isolated per HTTP `Mcp-Session-Id`, not shared across a key's\n * concurrent streams — otherwise two streams sharing a key overwrite each other's active session and\n * later orient/write calls use the sibling's session. Workspace + cache state legitimately stays\n * per-API-key on `state()`; only these three fields move to a per-stream store. In STDIO mode (or the\n * pre-session initialize request) there is no Mcp-Session-Id, so they stay on the singleton/per-key\n * `state()` — one process/one key is one session there.\n */\ninterface AgentSessionLifecycle {\n agentSessionId: string | null;\n sessionOriented: boolean;\n sessionClosed: boolean;\n}\nconst _sessionLifecycleByStream = new Map<string, AgentSessionLifecycle>();\nconst MAX_SESSION_STREAMS = 500;\n\nfunction sessionLifecycle(): AgentSessionLifecycle {\n const mcpSid = getRequestMcpSessionId();\n if (!mcpSid) return state(); // STDIO / pre-session — the per-key/singleton state carries them\n const key = `${cacheScope()}:${mcpSid}`;\n let lc = _sessionLifecycleByStream.get(key);\n if (!lc) {\n // Bound memory: a terminated stream's entry is stale; FIFO-evict the oldest when full.\n if (_sessionLifecycleByStream.size >= MAX_SESSION_STREAMS) {\n const oldest = _sessionLifecycleByStream.keys().next().value;\n if (oldest !== undefined) _sessionLifecycleByStream.delete(oldest);\n }\n lc = { agentSessionId: null, sessionOriented: false, sessionClosed: false };\n _sessionLifecycleByStream.set(key, lc);\n }\n return lc;\n}\n\nexport function getAgentSessionId(): string | null {\n return sessionLifecycle().agentSessionId;\n}\n\nexport function isSessionOriented(): boolean {\n return sessionLifecycle().sessionOriented;\n}\n\nexport function setSessionOriented(value: boolean): void {\n sessionLifecycle().sessionOriented = value;\n}\n\nexport function getApiKeyScope(): \"read\" | \"readwrite\" {\n return state().apiKeyScope;\n}\n\nexport function isSessionClosed(): boolean {\n return sessionLifecycle().sessionClosed;\n}\n\nexport interface AgentSessionStartResult {\n sessionId: string;\n initiatedBy: string;\n toolsScope: \"read\" | \"readwrite\";\n workspaceName: string;\n feedbackQueueNew?: number; feedbackOldestNewAt?: number;\n notices?: AgentSessionStartNotice[];\n adoptDiscoveryHint?: string; // WP-638 S4 §10.3 — \"3\" or, at the scan cap, \"20+\".\n}\n\n/**\n * Start an agent session. Creates a session record in Convex.\n * toolsScope is derived server-side from the API key — not passed as a parameter.\n * WP-479 E2: concurrent sessions on one key are expected, not collapsed — an existing active\n * session is never superseded; per-key quota is enforced instead (TEN-2570).\n */\nexport async function startAgentSession(): Promise<AgentSessionStartResult> {\n const workspaceId = await getWorkspaceId();\n const s = state();\n if (!s.apiKeyId) {\n throw new Error(\"Cannot start session: API key ID not resolved. Ensure workspace resolution completed.\");\n }\n\n const result = await kernelCall<GatewayRouteReturnByName<\"agent.startSession\">>(\"agent.startSession\", {\n workspaceId,\n apiKeyId: s.apiKeyId,\n clientKind: \"mcp\",\n // WP-479 E2: resolved once at process startup and held — see getConversationId() above.\n conversationId: getConversationId() ?? undefined,\n });\n\n // Session-lifecycle fields are per-Mcp-Session-Id (Codex re-review); apiKeyScope stays per-key.\n const lc = sessionLifecycle();\n if (lc.agentSessionId) {\n resetTouchThrottle(lc.agentSessionId);\n }\n lc.agentSessionId = result.sessionId;\n s.apiKeyScope = result.toolsScope;\n lc.sessionOriented = false;\n lc.sessionClosed = false;\n resetTouchThrottle(result.sessionId);\n\n return result;\n}\n\n/**\n * Close the current agent session. After this, write tools are blocked\n * even if the MCP connection stays open.\n */\nexport async function closeAgentSession(): Promise<void> {\n const lc = sessionLifecycle();\n if (!lc.agentSessionId) return;\n const sessionId = lc.agentSessionId;\n try {\n await kernelCall<GatewayRouteReturnByName<\"agent.closeSession\">>(\"agent.closeSession\", {\n sessionId,\n status: \"closed\",\n });\n } finally {\n resetTouchThrottle(sessionId);\n lc.sessionClosed = true;\n lc.agentSessionId = null;\n lc.sessionOriented = false;\n }\n}\n\n/**\n * Mark current session as orphaned (used on disconnect/crash).\n */\nexport async function orphanAgentSession(): Promise<void> {\n const lc = sessionLifecycle();\n if (!lc.agentSessionId) return;\n const sessionId = lc.agentSessionId;\n try {\n await kernelCall<GatewayRouteReturnByName<\"agent.closeSession\">>(\"agent.closeSession\", {\n sessionId,\n status: \"orphaned\",\n });\n } catch {\n // Best-effort on disconnect\n } finally {\n resetTouchThrottle(sessionId);\n lc.agentSessionId = null;\n lc.sessionOriented = false;\n }\n}\n\n/**\n * Touch the session to update lastToolCallAt. Fire-and-forget.\n * Throttled to at most once per 5s to prevent OCC conflicts when\n * multiple tool calls complete in parallel.\n */\nconst _lastTouchAtBySession = new Map<string, number>();\nconst TOUCH_THROTTLE_MS = 5_000;\n\nexport function touchSessionActivity(): void {\n const sessionId = sessionLifecycle().agentSessionId;\n if (!sessionId) return;\n\n const now = Date.now();\n const lastTouchAt = _lastTouchAtBySession.get(sessionId) ?? 0;\n if (now - lastTouchAt < TOUCH_THROTTLE_MS) return;\n _lastTouchAtBySession.set(sessionId, now);\n\n kernelCall<GatewayRouteReturnByName<\"agent.touchSession\">>(\"agent.touchSession\", {\n sessionId,\n }).catch(() => {});\n}\n\nexport function resetTouchThrottle(sessionId?: string | null): void {\n if (sessionId) {\n _lastTouchAtBySession.delete(sessionId);\n return;\n }\n _lastTouchAtBySession.clear();\n}\n\n/**\n * Record structured activity on the current session.\n */\nexport async function recordSessionActivity(activity: {\n entryCreated?: string;\n entryModified?: string;\n relationCreated?: boolean;\n gateFailure?: boolean;\n contradictionWarning?: boolean;\n strategyLinkWarnedForEntryId?: string;\n}): Promise<void> {\n const sessionId = sessionLifecycle().agentSessionId;\n if (!sessionId) return;\n try {\n await kernelCall<GatewayRouteReturnByName<\"agent.recordActivity\">>(\"agent.recordActivity\", {\n sessionId,\n ...activity,\n });\n } catch {\n // Non-critical — don't fail the tool call over activity tracking\n }\n}\n\n// ─── Audit ────────────────────────────────────────────────────────────\n\n/**\n * Bootstrap for stdio mode: set CONVEX_SITE_URL default and warn on missing key.\n * API key is validated lazily on first kernelCall so the server can start and handle\n * signals (SIGTERM) even before credentials are provided (e.g. in tests).\n */\nexport function bootstrap(): void {\n const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;\n process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;\n warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });\n const pbKey = process.env.PRODUCTBRAIN_API_KEY;\n if (!pbKey?.startsWith(\"pb_sk_\")) {\n process.stderr.write(\n \"[MCP] Warning: PRODUCTBRAIN_API_KEY is not set or invalid. \" +\n \"Tool calls will fail until a valid key is provided.\\n\"\n );\n }\n}\n\n/**\n * Bootstrap for HTTP mode: only set CONVEX_SITE_URL.\n * API key validation happens per-request via Bearer token.\n */\nexport function bootstrapHttp(): void {\n const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;\n process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;\n warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });\n}\n\n/** @deprecated Use bootstrap() instead. Alias kept for callers in transition. */\nexport const bootstrapCloudMode = bootstrap;\n\nfunction getEnv(key: string): string {\n const value = process.env[key];\n if (!value) throw new Error(`${key} environment variable is required`);\n return value;\n}\n\n/**\n * DEC-789 S2: Resolve the Convex deployment URL for the active API key.\n *\n * Warm path (normal): key-check already ran during OAuth authorize, so\n * state().deploymentUrl is set — return it immediately.\n *\n * No-fallback path (default / single-deployment): when CONVEX_FALLBACK_URLS\n * is not set, return CONVEX_SITE_URL directly without probing. This preserves\n * the original single-URL behavior and avoids extra fetch calls in tests and\n * stdio mode.\n *\n * Cold-start path (after Railway restart with CONVEX_FALLBACK_URLS configured):\n * keyStateMap was wiped; probe candidate URLs (lib/deploymentUrlResolver.ts) in order until\n * one responds ok:true to /api/key-check, store the result so subsequent calls are instant.\n *\n * Must be called from within a runWithAuth context (state() and\n * getActiveApiKey() both require it).\n */\nasync function resolveDeploymentUrl(): Promise<string> {\n const s = state();\n if (s.deploymentUrl) return s.deploymentUrl;\n\n const primaryUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_CLOUD_URL).replace(/\\/$/, \"\");\n const fallbacks = parseFallbackUrls(process.env.CONVEX_FALLBACK_URLS);\n\n // No fallbacks configured — single-deployment setup, use primary directly.\n // This is the common case: preserves the original behavior, no extra fetch calls.\n if (fallbacks.length === 0) {\n return primaryUrl;\n }\n\n // Multi-deployment cold-start: probe candidates in order.\n const candidates = [primaryUrl, ...fallbacks.map((u) => u.replace(/\\/$/, \"\"))];\n\n let apiKey: string;\n try {\n apiKey = getActiveApiKey();\n } catch {\n // No API key available — return primary and let the tool call fail normally.\n return primaryUrl;\n }\n\n const found = await probeDeploymentCandidates(candidates, apiKey);\n if (found) {\n s.deploymentUrl = found;\n return found;\n }\n\n // All probes failed — return the first candidate and let the tool call fail with its normal error.\n return candidates[0];\n}\n\n/**\n * Thin adapter over ./gatewaySeam.ts's `recordGatewayCall` — supplies the two pieces of\n * per-call context that module deliberately does not reach for (see its header).\n *\n * PR #533 review, Codex P2: the workspace fallback used to be the literal string\n * \"unresolved\" — a single bucket EVERY caller whose workspace hadn't resolved yet shared. In\n * HTTP mode that's still a cross-tenant collision (two different not-yet-resolved API keys\n * would land in the same \"unresolved\" audit/counters bucket), just a narrower one than the\n * original unfiltered leak. `cacheScope()` already exists for exactly this shape of problem\n * (hashed per-API-key in HTTP mode, a fixed \"stdio\" sentinel in stdio mode — see its own doc\n * comment) and is what collectionCache/smart-capture already partition on, so reuse it here\n * instead of inventing a second per-caller identity scheme.\n */\nfunction audit(\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n errorMsg?: string,\n meta?: { budgetMs?: number; timedOut?: boolean },\n): void {\n recordGatewayCall({\n fn, status, durationMs, errorMsg,\n workspace: state().workspaceId ?? cacheScope(),\n toolContext: getToolContext(),\n budgetMs: meta?.budgetMs,\n timedOut: meta?.timedOut,\n });\n}\n\n// ─── HTTP Client ──────────────────────────────────────────────────────\n\n// NextAction imported from ./envelope.js — local MCP-layer definition (mirrors convex/lib/envelopeContract.ts;\n// BR-113 prevents direct convex/ imports from MCP package, so shape is kept in sync manually)\n\ninterface GatewaySuccessResponse<T> {\n ok: true;\n summary: string;\n data: T;\n next?: NextAction[];\n _meta?: { durationMs?: number };\n}\n\ninterface GatewayErrorResponse {\n ok: false;\n code?: string;\n message?: string;\n error?: string;\n missingRequiredFields?: string[];\n fieldErrors?: unknown[];\n /** WP-465 slice ⑤: structured per-offender diagnostics (coherencyRefusals, blockers, …). */\n diagnostics?: Record<string, unknown>;\n}\n\ntype GatewayResponse<T> = GatewaySuccessResponse<T> | GatewayErrorResponse;\n\n// STD-101: Single SSOT for TOUCH_EXCLUDED — shared by kernelCall (callGateway) and kernelCallEnvelope.\n// Exported for testability — tests assert membership to prevent OCC cascade regressions.\nexport const TOUCH_EXCLUDED = new Set([\n \"agent.touchSession\",\n \"agent.startSession\",\n \"agent.markOriented\",\n \"agent.recordActivity\",\n \"agent.recordWrapup\",\n \"agent.closeSession\",\n // WP-376 α.3: orient byte report is itself a heartbeat-equivalent observability\n // write that already patches the same agentSessions row. A follow-up touchSession\n // would create a redundant second write per orient call (DEC-50 OCC anti-pattern).\n \"agent.reportOrientMetric\",\n]);\n\n/**\n * Private: shared HTTP fetch + error handling for kernelCall and kernelCallEnvelope.\n * Returns the parsed envelope fields without touching the read cache or session.\n */\n/**\n * Connector identity for the gateway's context.served shared seam (DEC-1207). Sent as the\n * `x-pb-source` header so the server-side emit attributes MCP — the dominant agent surface\n * (INS-1706) that emitted nothing under the prior CLI-only seam.\n */\nconst MCP_TELEMETRY_SOURCE = \"mcp\";\n\nasync function callGateway<T>(fn: string, args: Record<string, unknown>): Promise<{\n data: T;\n summary: string;\n next?: NextAction[];\n _meta?: { durationMs?: number };\n}> {\n const siteUrl = await resolveDeploymentUrl();\n const apiKey = getActiveApiKey();\n\n // WP-575 (TEN-2917): the budget is DECLARED at the route contract\n // (packages/kernel-client/src/routeLatencyBudget.ts) and reaches this connector as a\n // drift-checked generated copy. It replaces the blanket `AbortSignal.timeout(10_000)`\n // that used to apply to every route alike — including `chain.createEntry`, whose real\n // 14-20s round-trip meant this client reported hard failures on writes that had SUCCEEDED.\n const budgetMs = latencyBudgetMsForRoute(fn);\n const start = Date.now();\n\n let res: Response;\n try {\n res = await fetch(`${siteUrl}/api/aki`, {\n method: \"POST\",\n signal: AbortSignal.timeout(budgetMs),\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n // DEC-1207: attribute this connector for the gateway's context.served seam. MCP is\n // the dominant agent surface (INS-1706); without this header it was emitting nothing.\n \"x-pb-source\": MCP_TELEMETRY_SOURCE,\n },\n body: JSON.stringify({ fn, args }),\n });\n } catch (err: any) {\n throwClassifiedGatewayFailure(err, fn, args, budgetMs, Date.now() - start, \"network\",\n (m, t) => audit(fn, \"error\", Date.now() - start, m, { budgetMs, timedOut: t }));\n }\n\n // WP-575 / PR #533 review (Codex P2): the abort signal is STILL LIVE while the body streams,\n // and `fetch` resolves at headers — so a slow body can blow the budget HERE, outside the try\n // above, and escape as a raw DOMException nothing classifies. Same treatment, later phase.\n let json: GatewayResponse<T>;\n try {\n json = (await res.json()) as GatewayResponse<T>;\n } catch (err: any) {\n json = emptyBodyOrThrowClassified<GatewayResponse<T>>(err, fn, args, budgetMs, Date.now() - start, res.ok,\n (m, t) => audit(fn, \"error\", Date.now() - start, m, { budgetMs, timedOut: t }));\n }\n\n if (!res.ok || json.ok === false) {\n const errJson = json as GatewayErrorResponse;\n const msg = errJson.error ?? errJson.message ?? \"unknown error\";\n audit(fn, \"error\", Date.now() - start, errJson.code ? `${msg} [${errJson.code}]` : msg, { budgetMs });\n throw new KernelCallError(\n `MCP call \"${fn}\" failed (${res.status}): ${msg}`,\n res.status,\n errJson.code,\n Array.isArray(errJson.missingRequiredFields) ? errJson.missingRequiredFields : undefined,\n Array.isArray(errJson.fieldErrors) ? (errJson.fieldErrors as string[]) : undefined,\n errJson.diagnostics && typeof errJson.diagnostics === \"object\" ? errJson.diagnostics : undefined,\n );\n }\n\n audit(fn, \"ok\", Date.now() - start, undefined, { budgetMs });\n\n const { data, summary, next, _meta } = json as GatewaySuccessResponse<T>;\n return {\n data: data as T,\n summary: summary || fn,\n next,\n _meta,\n };\n}\n\n/**\n * Low-level call to the HTTP Action gateway.\n * Workspace scoping is enforced server-side from the API key — callers\n * don't need to (and can't) override the workspace.\n *\n * Read cache: orient and context-gather responses are cached for 60s.\n * Cache is invalidated on any write (safe-by-default: everything not\n * matching a known read pattern is treated as a write).\n */\nexport async function kernelCall<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const cached = getCached<T>(fn, args);\n if (cached !== undefined) {\n return cached;\n }\n\n const { data } = await callGateway<T>(fn, args);\n\n if (isWrite(fn)) {\n invalidateReadCache();\n } else {\n setCached(fn, args, data);\n }\n\n // Exclude session bookkeeping calls from the heartbeat touch. These mutations\n // already target the active session document, so immediately heartbeating after\n // them only adds avoidable OCC pressure on the same row.\n if (getAgentSessionId() && !TOUCH_EXCLUDED.has(fn)) {\n touchSessionActivity();\n }\n\n return data;\n}\n\n/**\n * Calls the gateway and returns the kernel success envelope\n * ({ ok: true, summary, data, next?, _meta? }) instead of stripping to just data.\n *\n * Use for MCP tools that need thin passthrough — forwarding summary, next actions,\n * or timing metadata to the MCP client.\n *\n * Note: Throws KernelCallError on gateway errors (non-200 responses). Full\n * envelope error passthrough (ok:false at HTTP 200) deferred to WP-321 S6+\n * when DEC-571 HTTP semantics are activated on error paths.\n *\n * Does NOT use the read cache (kernel _meta.durationMs would be stale from cache;\n * thin passthrough tools need fresh timing). Does NOT call invalidateReadCache\n * (read-only by contract).\n */\nexport async function kernelCallEnvelope<T>(\n fn: string,\n args: Record<string, unknown> = {},\n): Promise<{\n ok: true;\n summary: string;\n data: T;\n next?: NextAction[];\n _meta?: { durationMs?: number };\n}> {\n const { data, summary, next, _meta } = await callGateway<T>(fn, args);\n\n if (getAgentSessionId() && !TOUCH_EXCLUDED.has(fn)) {\n touchSessionActivity();\n }\n\n return { ok: true, summary, data, next, _meta };\n}\n\n// ─── Workspace Resolution ─────────────────────────────────────────────\n\nconst resolveInFlightMap = new Map<string, Promise<string>>();\n\nexport async function getWorkspaceId(): Promise<string> {\n const s = state();\n if (s.workspaceId) return s.workspaceId;\n\n const apiKey = getActiveApiKey();\n const existing = resolveInFlightMap.get(apiKey);\n if (existing) return existing;\n\n const promise = resolveWorkspaceWithRetry().finally(() => resolveInFlightMap.delete(apiKey));\n resolveInFlightMap.set(apiKey, promise);\n return promise;\n}\n\nasync function resolveWorkspaceWithRetry(maxRetries = 2): Promise<string> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const workspace = await kernelCall<{\n _id: string;\n name: string;\n slug: string;\n createdAt?: number;\n keyScope?: string;\n keyId?: string;\n governanceMode?: \"open\" | \"consensus\" | \"role\";\n } | null>(\"resolveWorkspace\", {});\n\n if (!workspace) {\n throw new Error(\n \"API key is valid but no workspace is associated. \" +\n `Run \\`npx ${MCP_NPX_PACKAGE} setup\\` or regenerate your key.`\n );\n }\n\n const s = state();\n s.workspaceId = workspace._id;\n s.workspaceSlug = workspace.slug;\n s.workspaceName = workspace.name;\n s.workspaceCreatedAt = workspace.createdAt ?? null;\n s.workspaceGovernanceMode = workspace.governanceMode ?? \"open\";\n if (workspace.keyScope) s.apiKeyScope = workspace.keyScope as \"read\" | \"readwrite\";\n if (workspace.keyId) s.apiKeyId = workspace.keyId;\n return s.workspaceId;\n } catch (err: any) {\n lastError = err;\n // WP-575 / PR #533 review (Codex P2): a budget abort must stay RETRYABLE. This regex\n // matched the OLD \"network error\" text, so the typed error silently dropped the retry\n // on the first call every session makes. It is a query — nothing to duplicate.\n const isTransient =\n err?.name === \"GatewayTimeoutError\" ||\n /network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(err.message);\n if (!isTransient || attempt === maxRetries) break;\n const delay = 1000 * (attempt + 1);\n process.stderr.write(\n `[MCP] Workspace resolution failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms...\\n`\n );\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n throw lastError!;\n}\n\nexport interface WorkspaceContext {\n workspaceId: string;\n workspaceSlug: string;\n workspaceName: string;\n createdAt: number | null;\n /** BET-76 FEAT-111: Cached from workspace resolution. Defaults to 'open'. */\n governanceMode: \"open\" | \"consensus\" | \"role\";\n}\n\nexport async function getWorkspaceContext(): Promise<WorkspaceContext> {\n const workspaceId = await getWorkspaceId();\n const s = state();\n return {\n workspaceId,\n workspaceSlug: s.workspaceSlug ?? \"unknown\",\n workspaceName: s.workspaceName ?? \"unknown\",\n createdAt: s.workspaceCreatedAt,\n governanceMode: s.workspaceGovernanceMode ?? \"open\",\n };\n}\n\n/**\n * TEN-1810: Re-fetches only governanceMode from the workspace without invalidating\n * stable identifiers (workspaceId, slug, name). Safe to call before every capture.\n * Single round-trip, no retry loop — workspace is already known at this point.\n */\nexport async function refreshWorkspaceGovernanceMode(): Promise<\"open\" | \"consensus\" | \"role\"> {\n const workspace = await kernelCall<{\n governanceMode?: \"open\" | \"consensus\" | \"role\";\n } | null>(\"resolveWorkspace\", {});\n const mode: \"open\" | \"consensus\" | \"role\" = workspace?.governanceMode ?? \"open\";\n const s = state();\n s.workspaceGovernanceMode = mode;\n return mode;\n}\n\nexport async function kernelQuery<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const workspaceId = await getWorkspaceId();\n return kernelCall<T>(fn, { ...args, workspaceId });\n}\n\nexport async function kernelMutation<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const workspaceId = await getWorkspaceId();\n return kernelCall<T>(fn, { ...args, workspaceId });\n}\n\n/**\n * @deprecated Use kernelQuery (reads) or kernelMutation (writes) instead.\n * Kept temporarily for backward compatibility — identical to both.\n */\nexport async function mcpAction<T>(fn: string, args: Record<string, unknown> = {}): Promise<T> {\n const workspaceId = await getWorkspaceId();\n return kernelCall<T>(fn, { ...args, workspaceId });\n}\n\n/**\n * Gate check: throws if no active, oriented session exists.\n *\n * Used for read tools that require session context per SOS-iszqu7:\n * structured Chain data for agent consumption requires an active session.\n * Lighter than requireWriteAccess — does not check key scope.\n */\nexport function requireActiveSession(): void {\n const lc = sessionLifecycle();\n\n if (!lc.agentSessionId) {\n throw new Error(\n \"Active session required (SOS-iszqu7). Call `session action=start` then `orient` first.\"\n );\n }\n\n if (lc.sessionClosed) {\n throw new Error(\n \"Session has been closed (SOS-iszqu7). Start a new session with `session action=start`.\"\n );\n }\n\n if (!lc.sessionOriented) {\n throw new Error(\n \"Orientation required before accessing build context (SOS-iszqu7). Call `orient` first.\"\n );\n }\n}\n\n/**\n * Gate check: throws if the agent is not allowed to write.\n *\n * Enforces:\n * 1. Session must exist (always required — no REQUIRE_AGENT_SESSION flag)\n * 2. Session must not be closed\n * 3. Session must be oriented\n * 4. Key scope must be readwrite\n */\nexport function requireWriteAccess(): void {\n const lc = sessionLifecycle();\n const s = state();\n\n if (!lc.agentSessionId) {\n throw new Error(\n \"Agent session required for write operations. Call `session action=start` first.\"\n );\n }\n\n if (lc.sessionClosed) {\n throw new Error(\n \"Agent session has been closed. Write tools are no longer available.\"\n );\n }\n\n if (!lc.sessionOriented) {\n throw new Error(\n \"Orientation required before writing to the Chain. Call 'orient' first.\"\n );\n }\n\n if (s.apiKeyScope === \"read\") {\n throw new Error(\n \"This API key has read-only scope. Write tools are not available.\"\n );\n }\n}\n\n/**\n * Gate for vendor feedback-triage actions (queue/note/group/status); list is member read-back.\n *\n * Mirrors the server contract exactly (convex/productFeedback.ts\n * `validateVendorTriageSession` → `intelligence/agentSessions:validateSessionForCaller`\n * with `requireActive: true`, `requireScope: 'readwrite'`, and deliberately NO\n * `requireOriented`): feedback rows are vendor telemetry, not Chain entries, and the\n * session-start queue hint points straight at `feedback action=queue` — gating triage\n * on orientation client-side would reject a call the server accepts.\n *\n * Enforces:\n * 1. Session must exist\n * 2. Session must not be closed\n * 3. Key scope must be readwrite\n *\n * Unlike `requireWriteAccess`, orientation is NOT required.\n */\nexport function requireVendorTriageAccess(): void {\n const lc = sessionLifecycle();\n const s = state();\n\n if (!lc.agentSessionId) {\n throw new Error(\n \"Agent session required for feedback triage. Call `session action=start` first.\"\n );\n }\n\n if (lc.sessionClosed) {\n throw new Error(\n \"Agent session has been closed. Feedback triage is no longer available.\"\n );\n }\n\n if (s.apiKeyScope === \"read\") {\n throw new Error(\n \"This API key has read-only scope. Feedback triage is not available.\"\n );\n }\n}\n\n/**\n * Recover session orientation state from Convex on restart.\n * If the session is active and oriented in Convex, restore local state.\n */\nexport async function recoverSessionState(): Promise<void> {\n const s = state();\n if (!s.workspaceId) return;\n try {\n const session = await kernelCall<{\n _id: string;\n status: string;\n oriented: boolean;\n toolsScope: string;\n } | null>(\"agent.getActiveSession\", {\n workspaceId: s.workspaceId,\n // WP-479 E2: required disambiguation arg for the per-apiKey-quota re-scoped\n // getActiveSession — absent identity (null) still resolves to the keyless/legacy path\n // server-side.\n conversationId: getConversationId() ?? undefined,\n });\n\n if (session && session.status === \"active\") {\n const lc = sessionLifecycle();\n lc.agentSessionId = session._id;\n lc.sessionOriented = session.oriented;\n s.apiKeyScope = session.toolsScope as \"read\" | \"readwrite\";\n lc.sessionClosed = false;\n }\n } catch {\n // Recovery is best-effort\n }\n}\n","/**\n * Conversation identity resolution — WP-479 E2 (client half), MCP server side.\n *\n * Deliberate duplicate of packages/cli/src/lib/conversation.ts — the two packages are published\n * independently (no shared workspace dependency between them), so this is a minimal, intentional\n * fork rather than a cross-package import. Keep the two files' resolution logic in sync by hand\n * if the priority order or normalization rule changes.\n *\n * The MCP server is a subprocess of the harness (Claude Code / Cursor / Codex), so the same env\n * channels that a Bash-tool child process would see are visible here too. Resolved once at\n * startup (see startAgentSession / recoverSessionState in ../client.ts) and held for the process\n * lifetime — there is no `--conversation` flag equivalent on this surface, so there is no\n * explicit-override parameter here (unlike the CLI twin).\n *\n * Priority order (harness findings: INS-2122, founder-run tests 2026-07-10):\n * 1. `PB_CONVERSATION_ID` — the universal seam any harness adapter can set.\n * 2. `CLAUDE_CODE_SESSION_ID` — present in Claude Code 2.1.206+ Bash shells (version-dependent,\n * treat as detected, never asserted).\n * 3. `CURSOR_CONVERSATION_ID` — confirmed live in cursor-agent 2026.07.01 shell env.\n * 4. `CODEX_TUI_SESSION_LOG_PATH` — Codex has no native conversation id; this Superset-injected\n * log path is unique per session run and stands in as the discriminator. It always contains\n * `/`, so normalization (below) always hashes it — no special-casing needed here.\n * 5. none of the above → `null` (legacy behavior — no conversation disambiguation).\n */\n\nimport { createHash } from \"node:crypto\";\n\n/** Above this raw length, or containing any char outside the join-key-safe set, hash instead. */\nconst MAX_RAW_LEN = 128;\n\n/** Join-key-safe charset. */\nconst SAFE_CHARS_RE = /^[A-Za-z0-9._-]+$/;\n\n/**\n * Normalize a raw candidate conversation id into a value that is always safe to use as a Convex\n * join key. Never truncates (a `slice()` would collide two distinct long ids into one lineage) —\n * oversized or unsafe-charset ids are replaced whole with their sha256 hex digest.\n */\nexport function normalizeConversationId(raw: string): string {\n if (raw.length > MAX_RAW_LEN || !SAFE_CHARS_RE.test(raw)) {\n return createHash(\"sha256\").update(raw).digest(\"hex\");\n }\n return raw;\n}\n\nfunction pickRawConversationId(): string | null {\n const candidates = [\n process.env.PB_CONVERSATION_ID,\n process.env.CLAUDE_CODE_SESSION_ID,\n process.env.CURSOR_CONVERSATION_ID,\n process.env.CODEX_TUI_SESSION_LOG_PATH,\n ];\n for (const candidate of candidates) {\n if (candidate && candidate.trim().length > 0) return candidate.trim();\n }\n return null;\n}\n\n/**\n * Resolve the effective conversation id for this MCP server process. Pure env-based feature\n * detection — with none of the channel env vars set, returns `null` and callers fall back to\n * legacy (no-conversation) behavior.\n */\nexport function resolveConversationId(): string | null {\n const raw = pickRawConversationId();\n if (!raw) return null;\n return normalizeConversationId(raw);\n}\n","/**\n * Compound-tool action telemetry (WP-484 S1, Q3: decided in). Per-tool+action call counter.\n * `runWithToolContext` (client.ts) is already the one chokepoint every compound tool wraps its\n * handler body in (Code Integrity: derive from what the system already tracks, don't add a\n * second call site per tool) — §2 found NO existing per-MCP-tool call-frequency telemetry, so\n * this is what makes the *next* consolidation decision data-driven. In-memory for local/test\n * visibility; mirrored to PostHog (`mcp_compound_tool_action`) separately via `trackCompoundToolAction`.\n *\n * Extracted out of client.ts (not inlined) — STD-2/DEC-1504: client.ts is grandfathered\n * shrink-only; this is the WP-584 offset for that PR's session-start notice type addition.\n */\nconst toolActionCounts = new Map<string, number>();\n\nfunction actionCountKey(tool: string, action?: string): string {\n return action ? `${tool}:${action}` : tool;\n}\n\nexport function recordToolAction(tool: string, action?: string): void {\n const key = actionCountKey(tool, action);\n toolActionCounts.set(key, (toolActionCounts.get(key) ?? 0) + 1);\n}\n\n/** Snapshot of per-tool+action call counts for this process (diagnostics/tests). */\nexport function getToolActionCounts(): Record<string, number> {\n return Object.fromEntries(toolActionCounts);\n}\n\n/** Test-only: reset the counter between test cases. */\nexport function resetToolActionCounts(): void {\n toolActionCounts.clear();\n}\n","/**\n * Cold-start deployment probing (DEC-789 S2) — pulled out of client.ts (module-health ratchet,\n * STD-2/DEC-1504: client.ts is grandfathered shrink-only, so a new fix recovers the room it\n * needs by splitting out a genuine responsibility rather than widening the baseline).\n *\n * \"Parse the fallback URL list, then probe each candidate's /api/key-check until one answers\n * ok:true\" is self-contained — it takes a candidate list and an API key and returns the first\n * live deployment (or null), with no dependency on client.ts's own state. client.ts's\n * `resolveDeploymentUrl` still owns WHEN to probe (warm-path short-circuit, no-fallback\n * short-circuit, caching the winner on `state().deploymentUrl`) — this module owns HOW.\n */\n\n/** DEC-789 S2: parse comma-separated fallback deployment URLs from env. */\nexport function parseFallbackUrls(raw: string | undefined): string[] {\n if (!raw) return [];\n return raw.split(\",\").map((u) => u.trim()).filter(Boolean);\n}\n\n/**\n * Probe each candidate in order; return the first that answers ok:true to `/api/key-check`, or\n * null if every candidate failed.\n *\n * WP-575: the 3s timeout here is DELIBERATELY not route-derived, and is not the class of\n * timeout that work package's budget replaced. `/api/key-check` is a different endpoint from\n * the `/api/aki` gateway — it is not a registered route, has no entry in the route contract, and\n * no budget to derive from. It is also a REACHABILITY PROBE whose whole job is to pick the live\n * deployment out of several candidates by failing fast: a longer wait here would multiply across\n * candidates and delay every call, and a probe giving up costs nothing (the caller falls through\n * to the primary URL and the real call reports the real error). Do not \"unify\" this with the\n * gateway budget.\n */\nexport async function probeDeploymentCandidates(candidates: string[], apiKey: string): Promise<string | null> {\n for (const candidate of candidates) {\n try {\n const probeRes = await fetch(`${candidate}/api/key-check`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${apiKey}`, \"Content-Type\": \"application/json\" },\n signal: AbortSignal.timeout(3000),\n });\n if (probeRes.ok) {\n const data = (await probeRes.json()) as { ok: boolean };\n if (data.ok) return candidate;\n }\n } catch {\n // Probe failed (timeout, network error, etc.) — try next candidate.\n }\n }\n return null;\n}\n","/**\n * TEN-2382: dev/prod isolation guard for URL resolution.\n *\n * The MCP server resolves its deployment URL from env vars only and falls back to\n * the hardcoded production gateway (DEFAULT_CLOUD_URL) when nothing is set. That\n * fall-through is silent today, so a misconfigured deployment quietly talks to\n * production. This makes it loud.\n *\n * Constraint (settled by review): the mcp-server has NO signal for \"a repo-local\n * binding was expected\" — it sees only env. So the warn fires on ANY fall-through\n * to the hardcoded default (Option A), not on an unimplementable \"binding expected\n * but absent\" condition.\n *\n * stderr ONLY — never stdout: stdio transport reserves stdout for MCP protocol bytes.\n */\nimport { DEFAULT_CLOUD_URL } from \"./client.js\";\n\n/**\n * Warn (on stderr) when `resolved` is the hardcoded production gateway because no\n * deployment URL was set explicitly. No-op when a URL was configured, or when the\n * resolved URL is anything other than the hardcoded default.\n */\nexport function warnOnProdFallthrough(\n resolved: string,\n opts: { explicit: boolean },\n): void {\n if (opts.explicit) return;\n if (resolved.replace(/\\/$/, \"\") !== DEFAULT_CLOUD_URL.replace(/\\/$/, \"\")) return;\n process.stderr.write(\n `[MCP] No deployment URL configured — defaulting to the production gateway ` +\n `${DEFAULT_CLOUD_URL}. Set CONVEX_SITE_URL or PRODUCTBRAIN_URL to target a ` +\n `different deployment.\\n`,\n );\n}\n"],"mappings":"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","names":["client","AsyncLocalStorage","createHash","AsyncLocalStorage"]}