@fml-inc/panopticon 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +59 -29
  2. package/bin/hook-handler +0 -0
  3. package/bin/mcp-server +0 -0
  4. package/bin/proxy +0 -0
  5. package/bin/server +0 -0
  6. package/dist/api/client.d.ts +2 -2
  7. package/dist/api/client.js +1 -1
  8. package/dist/{chunk-3BUJ7URA.js → chunk-3ILOOWUF.js} +66 -2
  9. package/dist/chunk-3ILOOWUF.js.map +1 -0
  10. package/dist/{chunk-HQCY722C.js → chunk-3ZT3V7FP.js} +5 -5
  11. package/dist/{chunk-WLBNFVIG.js → chunk-BKGQJ76N.js} +47 -19
  12. package/dist/chunk-BKGQJ76N.js.map +1 -0
  13. package/dist/{chunk-3TZAKV3M.js → chunk-FMAHQRIU.js} +2 -2
  14. package/dist/{chunk-LWXF7YRG.js → chunk-GPTBERQD.js} +2 -2
  15. package/dist/{chunk-4SM2H22C.js → chunk-HO443ZQM.js} +1 -1
  16. package/dist/{chunk-4SM2H22C.js.map → chunk-HO443ZQM.js.map} +1 -1
  17. package/dist/{chunk-L7G27XWF.js → chunk-HRNZUHTA.js} +3 -3
  18. package/dist/{chunk-CF4GPWLI.js → chunk-J3HVD4VI.js} +2 -2
  19. package/dist/{chunk-SEXU2WYG.js → chunk-MEVW27U4.js} +5 -4
  20. package/dist/chunk-MEVW27U4.js.map +1 -0
  21. package/dist/{chunk-SUGSQ4YI.js → chunk-N7NCNJZU.js} +4 -4
  22. package/dist/{chunk-RX2RXHBH.js → chunk-NE7VBLQD.js} +6 -5
  23. package/dist/{chunk-RX2RXHBH.js.map → chunk-NE7VBLQD.js.map} +1 -1
  24. package/dist/{chunk-NXH7AONS.js → chunk-OROLSIWZ.js} +8 -6
  25. package/dist/chunk-OROLSIWZ.js.map +1 -0
  26. package/dist/{chunk-XLTCUH5A.js → chunk-OW52TNVA.js} +4 -4
  27. package/dist/{chunk-DZ5HJFB4.js → chunk-SKZHAYNF.js} +53 -2
  28. package/dist/chunk-SKZHAYNF.js.map +1 -0
  29. package/dist/{chunk-BVOE7A2Z.js → chunk-V3XR2TAN.js} +8 -6
  30. package/dist/chunk-V3XR2TAN.js.map +1 -0
  31. package/dist/{chunk-HRCEIYKU.js → chunk-WXPT6KG7.js} +2 -2
  32. package/dist/cli.js +6 -6
  33. package/dist/cli.js.map +1 -1
  34. package/dist/db.js +1 -1
  35. package/dist/doctor.js +4 -4
  36. package/dist/hooks/handler.js +4 -4
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.js +15 -15
  39. package/dist/mcp/server.js +1 -1
  40. package/dist/otlp/server.js +5 -5
  41. package/dist/pricing.js +2 -2
  42. package/dist/proxy/server.js +5 -5
  43. package/dist/prune.js +2 -2
  44. package/dist/query.js +2 -2
  45. package/dist/{reparse-636YZCE3.js → reparse-VHUSGCPN.js} +5 -5
  46. package/dist/scanner.d.ts +7 -1
  47. package/dist/scanner.js +1 -1
  48. package/dist/server.js +13 -13
  49. package/dist/setup.js +3 -3
  50. package/dist/sync/index.d.ts +2 -2
  51. package/dist/sync/index.js +4 -4
  52. package/dist/{types-D-MYCBol.d.ts → types-DrhrWbWe.d.ts} +1 -0
  53. package/package.json +21 -13
  54. package/dist/chunk-3BUJ7URA.js.map +0 -1
  55. package/dist/chunk-BVOE7A2Z.js.map +0 -1
  56. package/dist/chunk-DZ5HJFB4.js.map +0 -1
  57. package/dist/chunk-NXH7AONS.js.map +0 -1
  58. package/dist/chunk-SEXU2WYG.js.map +0 -1
  59. package/dist/chunk-WLBNFVIG.js.map +0 -1
  60. /package/dist/{chunk-HQCY722C.js.map → chunk-3ZT3V7FP.js.map} +0 -0
  61. /package/dist/{chunk-3TZAKV3M.js.map → chunk-FMAHQRIU.js.map} +0 -0
  62. /package/dist/{chunk-LWXF7YRG.js.map → chunk-GPTBERQD.js.map} +0 -0
  63. /package/dist/{chunk-L7G27XWF.js.map → chunk-HRNZUHTA.js.map} +0 -0
  64. /package/dist/{chunk-CF4GPWLI.js.map → chunk-J3HVD4VI.js.map} +0 -0
  65. /package/dist/{chunk-SUGSQ4YI.js.map → chunk-N7NCNJZU.js.map} +0 -0
  66. /package/dist/{chunk-XLTCUH5A.js.map → chunk-OW52TNVA.js.map} +0 -0
  67. /package/dist/{chunk-HRCEIYKU.js.map → chunk-WXPT6KG7.js.map} +0 -0
  68. /package/dist/{reparse-636YZCE3.js.map → reparse-VHUSGCPN.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/proxy/server.ts","../src/hooks/ingest.ts","../src/eventConfig.ts","../src/hooks/permissions.ts","../src/proxy/emit.ts","../src/proxy/formats/anthropic.ts","../src/proxy/formats/openai.ts","../src/proxy/formats/openai-responses.ts","../src/proxy/sessions.ts","../src/proxy/streaming.ts","../src/proxy/ws-capture.ts"],"sourcesContent":["import http from \"node:http\";\nimport https from \"node:https\";\nimport { config } from \"../config.js\";\nimport { log } from \"../log.js\";\nimport { addBreadcrumb, captureException } from \"../sentry.js\";\nimport { allTargets, getTarget } from \"../targets/index.js\";\nimport { emitHookEventAsync, emitOtelLogs, emitOtelMetrics } from \"./emit.js\";\nimport { anthropicParser } from \"./formats/anthropic.js\";\nimport { openaiParser } from \"./formats/openai.js\";\nimport { openaiResponsesParser } from \"./formats/openai-responses.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./formats/types.js\";\nimport { SessionTracker } from \"./sessions.js\";\nimport {\n createAnthropicAccumulator,\n createOpenaiAccumulator,\n isStreamingRequest,\n} from \"./streaming.js\";\nimport { WebSocketMessageExtractor } from \"./ws-capture.js\";\n\n// Build upstream route table from target adapters that have proxy specs,\n// plus static entries for targets without full adapters (e.g. openai, google)\nfunction buildUpstreamRoutes(): Record<string, string> {\n const routes: Record<string, string> = {};\n for (const v of allTargets()) {\n if (v.proxy && typeof v.proxy.upstreamHost === \"string\") {\n routes[v.id] = v.proxy.upstreamHost;\n }\n }\n // Static routes for API-only targets (not CLI tools with full adapters)\n if (!routes.openai) routes.openai = \"api.openai.com\";\n if (!routes.google) routes.google = \"generativelanguage.googleapis.com\";\n // Alias: Claude adapter registers as \"claude\" but ANTHROPIC_BASE_URL uses /proxy/anthropic\n if (!routes.anthropic) routes.anthropic = \"api.anthropic.com\";\n return routes;\n}\n\nconst UPSTREAM_ROUTES = buildUpstreamRoutes();\n\n// Pre-compute known route prefixes for error messages\nconst KNOWN_ROUTES_MSG = [\n ...Object.keys(UPSTREAM_ROUTES),\n ...allTargets()\n .filter((v) => v.proxy && typeof v.proxy.upstreamHost === \"function\")\n .map((v) => v.id),\n]\n .filter((v, i, a) => a.indexOf(v) === i)\n .map((v) => `/${v}/*`)\n .join(\", \");\n\nconst FORMAT_PARSERS: ApiFormatParser[] = [\n anthropicParser,\n openaiParser,\n openaiResponsesParser,\n];\n\nconst sessions = new SessionTracker();\n\ninterface Route {\n target: string;\n upstream: string;\n path: string;\n}\n\nfunction parseRoute(\n url: string,\n headers?: http.IncomingHttpHeaders,\n): Route | null {\n // Match /target/rest-of-path\n const match = url.match(/^\\/([^/]+)(\\/.*)?$/);\n if (!match) return null;\n\n const targetId = match[1];\n const requestPath = match[2] ?? \"/\";\n\n // Check target adapter for dynamic routing (e.g. Codex JWT auto-detect)\n const targetAdapter = getTarget(targetId);\n if (targetAdapter?.proxy) {\n const { proxy } = targetAdapter;\n const flatHeaders = flattenHeaders(headers ?? {});\n\n const upstream =\n typeof proxy.upstreamHost === \"function\"\n ? proxy.upstreamHost(flatHeaders)\n : proxy.upstreamHost;\n\n const finalPath = proxy.rewritePath\n ? proxy.rewritePath(requestPath, flatHeaders)\n : requestPath;\n\n return { target: targetId, upstream, path: finalPath };\n }\n\n // Fall back to static route table\n const upstream = UPSTREAM_ROUTES[targetId];\n if (!upstream) return null;\n\n return { target: targetId, upstream, path: requestPath };\n}\n\nfunction collectBody(req: http.IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nfunction processCapture(capture: CapturedExchange): void {\n for (const parser of FORMAT_PARSERS) {\n if (!parser.matches(capture.request.path)) continue;\n\n const hookEvents = parser.extractEvents(capture);\n for (const event of hookEvents) {\n event.source = \"proxy\";\n event.target = capture.target;\n emitHookEventAsync(event);\n }\n\n const metrics = parser.extractMetrics(capture);\n for (const metric of metrics) {\n metric.attributes = { ...metric.attributes, source: \"proxy\" };\n }\n if (metrics.length > 0) {\n emitOtelMetrics(metrics);\n }\n\n const logs = parser.extractLogs(capture);\n for (const log of logs) {\n log.attributes = { ...log.attributes, source: \"proxy\" };\n }\n if (logs.length > 0) {\n emitOtelLogs(logs);\n }\n\n return; // Only use first matching parser\n }\n}\n\nfunction forwardNonStreaming(\n route: Route,\n clientReq: http.IncomingMessage,\n clientRes: http.ServerResponse,\n requestBody: Buffer,\n parsedReqBody: unknown,\n): void {\n const startMs = Date.now();\n const { sessionId, isNew } = sessions.getOrCreateSession(\n route.target,\n parsedReqBody,\n );\n\n if (isNew) {\n emitHookEventAsync({\n session_id: sessionId,\n hook_event_name: \"SessionStart\",\n source: \"proxy\",\n target: route.target,\n });\n }\n\n const headers: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(clientReq.headers)) {\n if (value !== undefined && key !== \"host\") {\n headers[key] = value;\n }\n }\n\n const upstreamReq = https.request(\n {\n hostname: route.upstream,\n port: 443,\n path: route.path,\n method: clientReq.method,\n headers,\n },\n (upstreamRes) => {\n const chunks: Buffer[] = [];\n upstreamRes.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n upstreamRes.on(\"end\", () => {\n const responseBody = Buffer.concat(chunks);\n const duration_ms = Date.now() - startMs;\n\n // Forward response to client\n clientRes.writeHead(upstreamRes.statusCode ?? 200, upstreamRes.headers);\n clientRes.end(responseBody);\n\n // Capture and process\n let parsedResBody: unknown;\n try {\n parsedResBody = JSON.parse(responseBody.toString(\"utf-8\"));\n } catch {\n parsedResBody = {};\n }\n\n const capture: CapturedExchange = {\n target: route.target,\n sessionId,\n timestamp_ms: startMs,\n request: {\n path: route.path,\n headers: flattenHeaders(clientReq.headers),\n body: parsedReqBody,\n },\n response: {\n status: upstreamRes.statusCode ?? 0,\n body: parsedResBody,\n },\n duration_ms,\n };\n\n processCapture(capture);\n });\n },\n );\n\n upstreamReq.on(\"error\", (err) => {\n log.proxy.error(`Upstream error (${route.target}):`, err.message);\n addBreadcrumb(\n \"proxy\",\n `Upstream error: ${route.target}`,\n { target: route.target, upstream: route.upstream, error: err.message },\n \"error\",\n );\n if (!clientRes.headersSent) {\n clientRes.writeHead(502);\n clientRes.end(\n JSON.stringify({ error: \"upstream_error\", message: err.message }),\n );\n }\n });\n\n upstreamReq.write(requestBody);\n upstreamReq.end();\n}\n\nfunction forwardStreaming(\n route: Route,\n clientReq: http.IncomingMessage,\n clientRes: http.ServerResponse,\n requestBody: Buffer,\n parsedReqBody: unknown,\n): void {\n const startMs = Date.now();\n const { sessionId, isNew } = sessions.getOrCreateSession(\n route.target,\n parsedReqBody,\n );\n\n if (isNew) {\n emitHookEventAsync({\n session_id: sessionId,\n hook_event_name: \"SessionStart\",\n source: \"proxy\",\n target: route.target,\n });\n }\n\n const targetSpec = getTarget(route.target);\n const accumulator =\n targetSpec?.proxy?.accumulatorType === \"anthropic\"\n ? createAnthropicAccumulator()\n : createOpenaiAccumulator();\n\n const headers: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(clientReq.headers)) {\n if (value !== undefined && key !== \"host\") {\n headers[key] = value;\n }\n }\n\n const upstreamReq = https.request(\n {\n hostname: route.upstream,\n port: 443,\n path: route.path,\n method: clientReq.method,\n headers,\n },\n (upstreamRes) => {\n // Forward headers immediately\n clientRes.writeHead(upstreamRes.statusCode ?? 200, upstreamRes.headers);\n\n // Stream chunks through: forward to client + accumulate for capture\n upstreamRes.on(\"data\", (chunk: Buffer) => {\n accumulator.push(chunk);\n clientRes.write(chunk);\n });\n\n upstreamRes.on(\"end\", () => {\n clientRes.end();\n\n const duration_ms = Date.now() - startMs;\n const reconstructed = accumulator.finish();\n\n const capture: CapturedExchange = {\n target: route.target,\n sessionId,\n timestamp_ms: startMs,\n request: {\n path: route.path,\n headers: flattenHeaders(clientReq.headers),\n body: parsedReqBody,\n },\n response: {\n status: upstreamRes.statusCode ?? 0,\n body: reconstructed,\n },\n duration_ms,\n };\n\n processCapture(capture);\n });\n },\n );\n\n upstreamReq.on(\"error\", (err) => {\n log.proxy.error(`Upstream error (${route.target}):`, err.message);\n addBreadcrumb(\n \"proxy\",\n `Upstream error: ${route.target}`,\n { target: route.target, upstream: route.upstream, error: err.message },\n \"error\",\n );\n if (!clientRes.headersSent) {\n clientRes.writeHead(502);\n clientRes.end(\n JSON.stringify({ error: \"upstream_error\", message: err.message }),\n );\n }\n });\n\n upstreamReq.write(requestBody);\n upstreamReq.end();\n}\n\nfunction flattenHeaders(\n headers: http.IncomingHttpHeaders,\n): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nfunction tunnelWebSocket(\n req: http.IncomingMessage,\n clientSocket: import(\"node:stream\").Duplex,\n head: Buffer,\n): void {\n const url = req.url ?? \"\";\n const route = parseRoute(url, req.headers);\n\n if (!route) {\n clientSocket.end(\"HTTP/1.1 404 Not Found\\r\\n\\r\\n\");\n return;\n }\n\n // Track session for this target\n const { sessionId, isNew } = sessions.getOrCreateSession(route.target, {});\n if (isNew) {\n emitHookEventAsync({\n session_id: sessionId,\n hook_event_name: \"SessionStart\",\n source: \"proxy\",\n target: route.target,\n });\n }\n\n // Catch client socket errors early (before upstream upgrade completes)\n clientSocket.on(\"error\", (err) => {\n log.proxy.error(`WebSocket client error (${route.target}):`, err.message);\n });\n\n // Forward headers, replacing host with upstream.\n // Strip Sec-WebSocket-Extensions to disable permessage-deflate so the\n // frame capture can read uncompressed text payloads.\n const proxyHeaders: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (\n key !== \"host\" &&\n key !== \"sec-websocket-extensions\" &&\n value !== undefined\n ) {\n proxyHeaders[key] = value;\n }\n }\n proxyHeaders.host = route.upstream;\n\n // Use https.request to perform the upstream WebSocket upgrade.\n // Node's HTTP parser handles the 101 response; we get the raw socket back.\n const proxyReq = https.request({\n hostname: route.upstream,\n port: 443,\n path: route.path,\n method: req.method,\n headers: proxyHeaders,\n });\n\n proxyReq.on(\"upgrade\", (proxyRes, proxySocket, proxyHead) => {\n // Reconstruct the 101 response for the client\n let response = `HTTP/${proxyRes.httpVersion} ${proxyRes.statusCode} ${proxyRes.statusMessage}\\r\\n`;\n for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {\n response += `${proxyRes.rawHeaders[i]}: ${proxyRes.rawHeaders[i + 1]}\\r\\n`;\n }\n response += \"\\r\\n\";\n\n clientSocket.write(response);\n if (proxyHead.length > 0) clientSocket.write(proxyHead);\n if (head.length > 0) proxySocket.write(head);\n\n // Tap into WebSocket messages for capture while forwarding bytes unchanged.\n // Client messages contain the request; server \"response.completed\" has the\n // full response with usage data.\n const clientExtractor = new WebSocketMessageExtractor();\n const serverExtractor = new WebSocketMessageExtractor();\n let pendingRequest: unknown;\n let requestTimestamp = Date.now();\n\n clientExtractor.onMessage = (msg) => {\n try {\n pendingRequest = JSON.parse(msg);\n requestTimestamp = Date.now();\n } catch (err) {\n log.proxy.error(\"Failed to parse client WebSocket message:\", err);\n captureException(err, { component: \"proxy\", phase: \"ws-client-parse\" });\n }\n };\n\n serverExtractor.onMessage = (msg) => {\n try {\n const event = JSON.parse(msg) as Record<string, unknown>;\n if (event.type === \"response.completed\" && pendingRequest) {\n const capture: CapturedExchange = {\n target: route.target,\n sessionId,\n timestamp_ms: requestTimestamp,\n request: {\n path: route.path,\n headers: flattenHeaders(req.headers),\n body: pendingRequest,\n },\n response: {\n status: 200,\n body: event.response,\n },\n duration_ms: Date.now() - requestTimestamp,\n };\n processCapture(capture);\n pendingRequest = undefined;\n }\n } catch (err) {\n log.proxy.error(\"Failed to parse server WebSocket message:\", err);\n captureException(err, { component: \"proxy\", phase: \"ws-server-parse\" });\n }\n };\n\n proxySocket.on(\"data\", (chunk: Buffer) => {\n serverExtractor.push(chunk);\n clientSocket.write(chunk);\n });\n clientSocket.on(\"data\", (chunk: Buffer) => {\n clientExtractor.push(chunk);\n proxySocket.write(chunk);\n });\n\n proxySocket.on(\"error\", () => clientSocket.destroy());\n proxySocket.on(\"close\", () => clientSocket.destroy());\n clientSocket.on(\"close\", () => proxySocket.destroy());\n });\n\n proxyReq.on(\"error\", (err) => {\n log.proxy.error(`WebSocket upstream error (${route.target}):`, err.message);\n clientSocket.end(\"HTTP/1.1 502 Bad Gateway\\r\\n\\r\\n\");\n });\n\n proxyReq.on(\"response\", (res) => {\n // Upstream rejected the upgrade (non-101 response) — forward as-is\n let response = `HTTP/${res.httpVersion} ${res.statusCode} ${res.statusMessage}\\r\\n`;\n for (let i = 0; i < res.rawHeaders.length; i += 2) {\n response += `${res.rawHeaders[i]}: ${res.rawHeaders[i + 1]}\\r\\n`;\n }\n response += \"\\r\\n\";\n clientSocket.write(response);\n res.pipe(clientSocket);\n });\n\n proxyReq.end();\n}\n\n/** Handle a proxy API request. Expects the URL to have a target prefix. */\nexport async function handleProxyRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n): Promise<void> {\n const url = req.url ?? \"\";\n\n const route = parseRoute(url, req.headers);\n if (!route) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"unknown_route\",\n message: `Unknown target prefix. Known: ${KNOWN_ROUTES_MSG}`,\n }),\n );\n return;\n }\n\n try {\n const requestBody = await collectBody(req);\n\n // Parse request body once — used for streaming detection and session tracking\n let parsedReqBody: unknown;\n let streaming = false;\n try {\n parsedReqBody = JSON.parse(requestBody.toString(\"utf-8\"));\n streaming = isStreamingRequest(parsedReqBody);\n } catch {\n parsedReqBody = {};\n }\n\n if (streaming) {\n forwardStreaming(route, req, res, requestBody, parsedReqBody);\n } else {\n forwardNonStreaming(route, req, res, requestBody, parsedReqBody);\n }\n } catch (err) {\n log.proxy.error(\"Proxy error:\", err);\n captureException(err, { component: \"proxy\", path: url });\n if (!res.headersSent) {\n res.writeHead(500);\n res.end();\n }\n }\n}\n\n/** Handle a WebSocket upgrade for proxy tunneling. */\nexport { tunnelWebSocket };\n\nexport function createProxyServer(): http.Server {\n const server = http.createServer(async (req, res) => {\n const url = req.url ?? \"\";\n const method = req.method ?? \"\";\n\n if (url === \"/health\" && method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", port: config.proxyPort }));\n return;\n }\n\n if (method !== \"POST\") {\n res.writeHead(405);\n res.end();\n return;\n }\n\n await handleProxyRequest(req, res);\n });\n\n server.on(\"upgrade\", (req, socket, head) => {\n tunnelWebSocket(req, socket, head);\n });\n\n return server;\n}\n\n// When run directly, start the server\nconst entryScript = process.argv[1]?.replaceAll(\"\\\\\", \"/\") ?? \"\";\nif (\n entryScript.endsWith(\"/proxy/server.js\") ||\n entryScript.endsWith(\"/proxy/server.ts\")\n) {\n const server = createProxyServer();\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n log.proxy.warn(\n `Already running on ${config.proxyHost}:${config.proxyPort}`,\n );\n process.exit(0);\n }\n throw err;\n });\n server.listen(config.proxyPort, config.proxyHost, () => {\n log.proxy.info(`Listening on ${config.proxyHost}:${config.proxyPort}`);\n log.proxy.info(\"Routes:\");\n for (const [prefix, host] of Object.entries(UPSTREAM_ROUTES)) {\n log.proxy.info(` /${prefix}/* → https://${host}/*`);\n }\n // Show dynamic-routed targets not in the static table\n for (const v of allTargets()) {\n if (\n v.proxy &&\n typeof v.proxy.upstreamHost === \"function\" &&\n !UPSTREAM_ROUTES[v.id]\n ) {\n log.proxy.info(` /${v.id}/* → (dynamic)`);\n }\n }\n });\n\n const shutdown = () => {\n server.close();\n process.exit(0);\n };\n process.on(\"SIGTERM\", shutdown);\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGHUP\", shutdown);\n}\n","import { execFileSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { config } from \"../config.js\";\nimport {\n incrementEventTypeCount,\n incrementToolCount,\n insertHookEvent,\n insertRepoConfigSnapshot,\n insertUserConfigSnapshot,\n upsertSession,\n upsertSessionCwd,\n upsertSessionRepository,\n} from \"../db/store.js\";\nimport { isEventEnabled } from \"../eventConfig.js\";\nimport { log } from \"../log.js\";\nimport { type RepoInfo, resolveRepoFromCwd } from \"../repo.js\";\nimport { isGitignored, readConfig, resolveGitRoot } from \"../scanner.js\";\nimport { allTargets } from \"../targets/index.js\";\nimport type { TargetAdapter } from \"../targets/types.js\";\nimport { checkBashPermission } from \"./permissions.js\";\n\n// Cache: cwd → { name, email }\nconst gitIdentityCache = new Map<\n string,\n { name: string | null; email: string | null }\n>();\n\nfunction resolveGitIdentity(cwd: string): {\n name: string | null;\n email: string | null;\n} {\n const cached = gitIdentityCache.get(cwd);\n if (cached) return cached;\n\n const result = { name: null as string | null, email: null as string | null };\n try {\n result.name =\n execFileSync(\"git\", [\"-C\", cwd, \"config\", \"user.name\"], {\n encoding: \"utf-8\",\n timeout: 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim() || null;\n } catch {\n // no user.name configured\n }\n try {\n result.email =\n execFileSync(\"git\", [\"-C\", cwd, \"config\", \"user.email\"], {\n encoding: \"utf-8\",\n timeout: 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim() || null;\n } catch {\n // no user.email configured\n }\n gitIdentityCache.set(cwd, result);\n return result;\n}\n\n// Last resolved repo per session — used as fallback for events without paths\n// (e.g. Stop, UserPromptSubmit). Long-lived in the server process.\nconst lastSessionRepo = new Map<string, string>();\n\n// Track sessions where we've already captured user config\nconst userConfigCaptured = new Set<string>();\n\n// Track session:repo pairs where we've already captured repo config\nconst seenSessionRepos = new Set<string>();\n\nexport interface HookInput {\n session_id: string;\n hook_event_name: string;\n cwd?: string;\n repository?: string;\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n source?: string;\n target?: string;\n prompt?: string;\n [key: string]: unknown;\n}\n\nconst ALLOWED_PATH = path.join(config.dataDir, \"permissions\", \"allowed.json\");\n\ninterface AllowedList {\n bash_commands: string[];\n tools: string[];\n}\n\nfunction loadAllowed(): AllowedList | null {\n try {\n return JSON.parse(fs.readFileSync(ALLOWED_PATH, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nexport function isPanopticonMcpTool(toolName: string): boolean {\n return (\n toolName.startsWith(\"mcp__plugin_panopticon_panopticon__\") ||\n toolName.startsWith(\"mcp__panopticon__\")\n );\n}\n\n/**\n * Extract the shell_pwd from hook event data.\n * Claude Code may send it at the top level or nested in tool_input.\n */\nexport function extractShellPwd(data: HookInput): string | null {\n if (typeof data.shell_pwd === \"string\") return data.shell_pwd;\n if (typeof data.tool_input?.shell_pwd === \"string\")\n return data.tool_input.shell_pwd;\n return null;\n}\n\nexport type PathSource =\n | \"shell_pwd\"\n | \"tool_input.file_path\"\n | \"tool_input.path\"\n | \"cwd\";\n\nexport interface EventPath {\n dir: string;\n source: PathSource;\n}\n\n/**\n * Extract every directory path we can find from a hook event, in priority\n * order. Consumers can iterate the list to greedily resolve repos, capture\n * config snapshots, etc. without duplicating extraction logic.\n */\nexport function extractEventPaths(data: HookInput): EventPath[] {\n const paths: EventPath[] = [];\n const seen = new Set<string>();\n const add = (dir: string, source: PathSource) => {\n if (!seen.has(dir)) {\n seen.add(dir);\n paths.push({ dir, source });\n }\n };\n\n const shellPwd = extractShellPwd(data);\n if (shellPwd) add(shellPwd, \"shell_pwd\");\n\n const toolInput = data.tool_input;\n if (toolInput && typeof toolInput === \"object\") {\n const fp = (toolInput as Record<string, unknown>).file_path;\n if (typeof fp === \"string\" && path.isAbsolute(fp)) {\n add(path.dirname(fp), \"tool_input.file_path\");\n }\n const p = (toolInput as Record<string, unknown>).path;\n if (typeof p === \"string\" && path.isAbsolute(p)) {\n add(path.dirname(p), \"tool_input.path\");\n }\n }\n\n if (typeof data.cwd === \"string\") add(data.cwd, \"cwd\");\n\n return paths;\n}\n\nexport type ResolveFn = (dir: string) => RepoInfo | string | null;\n\nfunction normalizeResolveFn(\n resolveFn: ResolveFn,\n): (dir: string) => RepoInfo | null {\n return (dir: string) => {\n const result = resolveFn(dir);\n if (!result) return null;\n if (typeof result === \"string\") return { repo: result };\n return result;\n };\n}\n\n/**\n * Resolve the repository for an event, trying multiple sources in priority order:\n * 1. Explicit repository field\n * 2. shell_pwd (actual cwd at event time)\n * 3. tool_input.file_path / path\n * 4. Session cwd\n * 5. Last resolved repo for this session (cumulative cache)\n */\nexport function resolveEventRepo(\n data: HookInput,\n resolveFn: ResolveFn = resolveRepoFromCwd,\n): string | null {\n const sessionId = data.session_id ?? \"unknown\";\n\n let repo = data.repository ?? null;\n\n if (!repo) {\n const resolve = normalizeResolveFn(resolveFn);\n for (const { dir } of extractEventPaths(data)) {\n const info = resolve(dir);\n if (info) {\n repo = info.repo;\n break;\n }\n }\n }\n\n // Fallback: use the last resolved repo for this session\n if (!repo) {\n repo = lastSessionRepo.get(sessionId) ?? null;\n }\n\n // Cache for future events in this session\n if (repo) {\n lastSessionRepo.set(sessionId, repo);\n }\n\n return repo;\n}\n\n/**\n * Resolve ALL repos touched by this event — the primary repo plus any\n * additional repos referenced via tool_input paths.\n * Returns deduplicated { repo, dir, branch } tuples.\n */\nexport function resolveAllEventRepos(\n data: HookInput,\n resolveFn: ResolveFn = resolveRepoFromCwd,\n): Array<{ repo: string; dir: string; branch?: string | null }> {\n const results: Array<{\n repo: string;\n dir: string;\n branch?: string | null;\n }> = [];\n const seen = new Set<string>();\n const resolve = normalizeResolveFn(resolveFn);\n\n // Explicit repository field first\n if (data.repository) {\n seen.add(data.repository);\n const shellPwd = extractShellPwd(data);\n results.push({\n repo: data.repository,\n dir: shellPwd ?? (data.cwd as string) ?? \".\",\n });\n }\n\n for (const { dir } of extractEventPaths(data)) {\n const info = resolve(dir);\n if (info && !seen.has(info.repo)) {\n seen.add(info.repo);\n results.push({ repo: info.repo, dir, branch: info.branch });\n }\n }\n\n return results;\n}\n\n/** Clear the session repo cache (for testing). */\nexport function _resetSessionRepoCache(): void {\n lastSessionRepo.clear();\n}\n\n/**\n * Process a hook event: normalize, store, and optionally enforce permissions.\n * Returns a JSON-serializable response body (permission decision or {}).\n */\n/** Cache of resolved target per session — once identified, reuse for all events. */\nconst sessionTargetCache = new Map<string, string>();\n\n/** Clear the session target cache (for testing). */\nexport function _resetSessionTargetCache(): void {\n sessionTargetCache.clear();\n}\n\n/**\n * Resolve which target adapter sent this event, using the source/target\n * field, session cache, eventMap matching, or payload heuristics.\n */\nfunction resolveTarget(data: HookInput): TargetAdapter | undefined {\n const targets = allTargets();\n const sessionId = data.session_id;\n\n // 1. Explicit source/target field\n const source = data.source ?? data.target;\n if (source) {\n for (const v of targets) {\n if (v.id === source) {\n if (sessionId) sessionTargetCache.set(sessionId, v.id);\n return v;\n }\n }\n }\n\n // 2. Session cache — reuse previously identified target\n if (sessionId) {\n const cached = sessionTargetCache.get(sessionId);\n if (cached) {\n return targets.find((v) => v.id === cached);\n }\n }\n\n // 3. eventMap match (catches Gemini's BeforeTool, AfterTool, BeforeModel)\n const rawEvent = data.hook_event_name;\n if (rawEvent) {\n let matched: TargetAdapter | undefined;\n for (const v of targets) {\n if (rawEvent in v.events.eventMap) {\n if (matched) {\n log.hooks.warn(\n `Event \"${rawEvent}\" claimed by both \"${matched.id}\" and \"${v.id}\" — using \"${matched.id}\"`,\n );\n break;\n }\n matched = v;\n }\n }\n if (matched) {\n if (sessionId) sessionTargetCache.set(sessionId, matched.id);\n return matched;\n }\n }\n\n // 4. Model-based detection (last resort) — iterate adapter ident specs.\n // Logs a warning so operators know detection was ambiguous (see #73).\n const model = typeof data.model === \"string\" ? data.model : null;\n if (model) {\n let matched: TargetAdapter | undefined;\n for (const v of targets) {\n if (v.ident?.modelPatterns?.some((re) => re.test(model))) {\n matched = v;\n break;\n }\n }\n if (matched) {\n log.hooks.warn(\n `Target resolved via model-name heuristic: model=\"${model}\" → \"${matched.id}\". ` +\n `Set an explicit source/target field to avoid ambiguous detection.`,\n );\n if (sessionId) sessionTargetCache.set(sessionId, matched.id);\n return matched;\n }\n }\n\n return undefined;\n}\n\n/**\n * Process a hook event: normalize, store, and optionally enforce permissions.\n *\n * Called by the server's POST /hooks handler for every hook event from any\n * target (Claude Code, Gemini, Codex). The flow:\n * 1. Resolve which target adapter sent this event\n * 2. Map the event name to canonical form (e.g. Gemini's \"BeforeTool\" → \"PreToolUse\")\n * 3. Resolve the git repository from cwd/file paths\n * 4. Store the event in hook_events table (full payload as gzipped blob)\n * 5. Upsert session metadata (started_at, first_prompt, ended_at, etc.)\n * 6. For PreToolUse: check allowed.json and return permission decision\n *\n * Returns {} for most events. For PreToolUse, may return a permission\n * response that Claude Code uses to auto-approve/deny the tool call.\n */\nexport function processHookEvent(data: HookInput): Record<string, unknown> {\n const sessionId = data.session_id ?? \"unknown\";\n const rawEventType = data.hook_event_name ?? \"Unknown\";\n let eventType = rawEventType;\n const toolName = data.tool_name ?? null;\n const timestampMs = Date.now();\n\n // Resolve target and normalize event type + payload via adapter\n const target = resolveTarget(data);\n if (target) {\n const mapped = target.events.eventMap[eventType];\n if (mapped) eventType = mapped;\n if (target.events.normalizePayload) {\n data = target.events.normalizePayload(data);\n }\n }\n\n const repo = resolveEventRepo(data);\n\n const targetId = target?.id ?? \"unknown\";\n\n // Check if this event type is enabled in the logging config.\n // Permission enforcement still runs even for disabled events so that\n // PreToolUse responses are not silently dropped.\n if (!isEventEnabled(eventType)) {\n // Skip storage but still handle permission enforcement below\n if (eventType === \"PreToolUse\" && toolName) {\n return buildPermissionResponse(toolName, data, target);\n }\n return {};\n }\n\n insertHookEvent({\n session_id: sessionId,\n event_type: eventType,\n timestamp_ms: timestampMs,\n cwd: data.cwd,\n repository: repo ?? undefined,\n tool_name: toolName ?? undefined,\n target: targetId,\n payload: data,\n });\n\n // Upsert session — each event type contributes different fields.\n // SessionStart seeds the row; subsequent events enrich it. The session\n // row is the primary join target for queries across hook_events, otel,\n // and scanner tables.\n const sessionFields: Parameters<typeof upsertSession>[0] = {\n session_id: sessionId,\n target: targetId,\n has_hooks: 1,\n };\n if (eventType === \"SessionStart\") {\n // First event in a session — capture initial state. cwd and\n // permission_mode are snapshot values from launch time.\n sessionFields.started_at_ms = timestampMs;\n sessionFields.created_at = timestampMs;\n sessionFields.permission_mode =\n typeof data.permission_mode === \"string\"\n ? data.permission_mode\n : undefined;\n sessionFields.agent_version =\n typeof data.agent_version === \"string\" ? data.agent_version : undefined;\n // Derive project from cwd\n const cwd = data.cwd as string | undefined;\n if (cwd) {\n const repoInfo = resolveRepoFromCwd(cwd);\n sessionFields.project = repoInfo?.repo ?? path.basename(cwd);\n }\n }\n if (eventType === \"UserPromptSubmit\") {\n // Capture the first user prompt for session search/display. Only the\n // first prompt is stored (upsertSession uses INSERT OR IGNORE semantics\n // for first_prompt).\n const prompt =\n typeof data.prompt === \"string\"\n ? data.prompt\n : typeof data.user_prompt === \"string\"\n ? data.user_prompt\n : undefined;\n if (prompt) sessionFields.first_prompt = prompt;\n }\n if (eventType === \"Stop\" || eventType === \"SessionEnd\") {\n // Mark session end time. Stop fires on every turn completion, so\n // ended_at_ms gets updated repeatedly — the last Stop or SessionEnd\n // wins, giving us the true end time.\n sessionFields.ended_at_ms = timestampMs;\n }\n upsertSession(sessionFields);\n\n // Link subagent sessions to their parents in real-time.\n // SubagentStart fires on the PARENT session with agent_id identifying\n // the child. The subagent session ID follows the scanner convention:\n // \"agent-{agent_id}\" (matches file naming agent-*.jsonl).\n if (eventType === \"SubagentStart\" || eventType === \"SubagentStop\") {\n const agentId = data.agent_id as string | undefined;\n if (agentId) {\n const subagentSessionId = `agent-${agentId}`;\n const subagentFields: Parameters<typeof upsertSession>[0] = {\n session_id: subagentSessionId,\n target: targetId,\n parent_session_id: sessionId,\n relationship_type: \"subagent\",\n is_automated: 1,\n };\n if (eventType === \"SubagentStart\") {\n subagentFields.started_at_ms = timestampMs;\n subagentFields.created_at = timestampMs;\n } else {\n subagentFields.ended_at_ms = timestampMs;\n }\n upsertSession(subagentFields);\n }\n }\n\n // Increment event type + tool counts on the session\n incrementEventTypeCount(sessionId, eventType);\n if (eventType === \"PreToolUse\" && toolName) {\n incrementToolCount(sessionId, toolName);\n }\n\n // Populate session junction tables — greedily resolve all repos touched\n // by this event (primary cwd + any paths in tool_input).\n const allRepos = resolveAllEventRepos(data);\n for (const { repo: r, dir, branch } of allRepos) {\n const gitId = resolveGitIdentity(dir);\n upsertSessionRepository(sessionId, r, timestampMs, gitId, branch);\n\n // Capture repo config on first encounter per session\n const repoKey = `${sessionId}:${r}`;\n if (!seenSessionRepos.has(repoKey)) {\n seenSessionRepos.add(repoKey);\n try {\n const cfg = readConfig(dir);\n const gitRoot = resolveGitRoot(dir);\n const localSettingsPath = path.join(\n gitRoot ?? dir,\n \".claude\",\n \"settings.local.json\",\n );\n insertRepoConfigSnapshot({\n repository: r,\n cwd: dir,\n sessionId,\n hooks: cfg.project?.hooks ?? [],\n mcpServers: cfg.project?.mcpServers ?? [],\n commands: cfg.project?.commands ?? [],\n agents: cfg.project?.agents ?? [],\n rules: cfg.project?.rules ?? [],\n localHooks: cfg.projectLocal?.hooks ?? [],\n localMcpServers: cfg.projectLocal?.mcpServers ?? [],\n localPermissions: cfg.projectLocal?.permissions ?? {\n allow: [],\n ask: [],\n deny: [],\n },\n localIsGitignored: isGitignored(localSettingsPath, gitRoot ?? dir),\n instructions: cfg.instructions,\n });\n } catch {\n // Non-fatal — config scan failure shouldn't break hook processing\n }\n }\n }\n if (data.cwd) {\n upsertSessionCwd(sessionId, data.cwd as string, timestampMs);\n }\n\n // Capture user config on SessionStart (once per session)\n if (eventType === \"SessionStart\" && !userConfigCaptured.has(sessionId)) {\n userConfigCaptured.add(sessionId);\n try {\n const config = readConfig(data.cwd as string | undefined);\n insertUserConfigSnapshot({\n deviceName: os.hostname(),\n permissions: config.user.permissions,\n enabledPlugins: config.enabledPlugins,\n hooks: config.user.hooks,\n commands: config.user.commands,\n rules: config.user.rules,\n skills: config.user.skills,\n });\n } catch {\n // Non-fatal\n }\n }\n\n // Permission enforcement via allowed.json\n if (eventType === \"PreToolUse\" && toolName) {\n return buildPermissionResponse(toolName, data, target);\n }\n\n return {};\n}\n\n/**\n * Evaluate PreToolUse permission for a tool call.\n * Returns a formatted permission response if auto-allowed, otherwise {}.\n */\nfunction buildPermissionResponse(\n toolName: string,\n data: HookInput,\n target: TargetAdapter | undefined,\n): Record<string, unknown> {\n let decision: { allow: true; reason: string } | null = null;\n\n // Always auto-allow panopticon's own MCP tools\n if (isPanopticonMcpTool(toolName)) {\n decision = { allow: true, reason: \"Panopticon tool (always allowed)\" };\n } else {\n const allowed = loadAllowed();\n if (allowed) {\n if (toolName === \"Bash\") {\n const command = data.tool_input?.command;\n if (typeof command === \"string\" && allowed.bash_commands?.length) {\n decision = checkBashPermission(command, allowed.bash_commands);\n }\n } else if (allowed.tools?.includes(toolName)) {\n decision = { allow: true, reason: `Tool \"${toolName}\" is allowed` };\n }\n }\n }\n\n if (decision) {\n if (target) {\n return target.events.formatPermissionResponse(decision);\n }\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"allow\",\n permissionDecisionReason: decision.reason,\n },\n };\n }\n\n return {};\n}\n","/**\n * Per-event logging configuration.\n *\n * Separated from config.ts to avoid circular imports:\n * config.ts → targets/types.ts → hooks/ingest.ts → config.ts\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { config, ensureDataDir } from \"./config.js\";\nimport { ALL_EVENTS, type CanonicalEvent } from \"./targets/types.js\";\n\nexport type EventConfig = Record<CanonicalEvent, boolean>;\n\nconst EVENT_CONFIG_PATH = path.join(config.dataDir, \"event-config.json\");\n\n/** Cached config — null means not yet loaded. */\nlet cachedEventConfig: EventConfig | null = null;\n\nfunction defaultEventConfig(): EventConfig {\n const cfg = {} as EventConfig;\n for (const e of ALL_EVENTS) cfg[e] = true;\n return cfg;\n}\n\n/**\n * Load event logging config from disk.\n * Missing keys default to true (enabled).\n */\nexport function loadEventConfig(): EventConfig {\n if (cachedEventConfig) return cachedEventConfig;\n\n const defaults = defaultEventConfig();\n try {\n const raw = JSON.parse(fs.readFileSync(EVENT_CONFIG_PATH, \"utf-8\"));\n if (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n for (const key of Object.keys(raw)) {\n if (key in defaults && typeof raw[key] === \"boolean\") {\n defaults[key as CanonicalEvent] = raw[key];\n }\n }\n }\n } catch {\n // File doesn't exist or is invalid — use all-enabled defaults.\n }\n cachedEventConfig = defaults;\n return cachedEventConfig;\n}\n\n/** Write event config to disk and refresh cache. */\nexport function saveEventConfig(cfg: EventConfig): void {\n ensureDataDir();\n fs.writeFileSync(EVENT_CONFIG_PATH, `${JSON.stringify(cfg, null, 2)}\\n`);\n cachedEventConfig = cfg;\n}\n\n/** Check whether a specific event type should be logged. */\nexport function isEventEnabled(eventType: string): boolean {\n const cfg = loadEventConfig();\n // Unknown event types (e.g. from other targets) are always logged.\n if (!(eventType in cfg)) return true;\n return cfg[eventType as CanonicalEvent];\n}\n\n/** Force re-read from disk on next access. */\nexport function _resetEventConfigCache(): void {\n cachedEventConfig = null;\n}\n","/** Split a command string on chain operators (&&, ||, ;, |). */\nexport function splitChainComponents(cmd: string): string[] {\n return cmd\n .split(/\\s*(?:&&|\\|\\||;|\\|)\\s*/)\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\n/**\n * Extract all base commands from a single (non-chain) command string.\n * Returns multiple commands when the primary command delegates to others\n * (e.g., `find -exec rm` returns [\"find\", \"rm\"]).\n */\nexport function extractBaseCommands(component: string): string[] {\n // Strip leading env var assignments (FOO=bar cmd → cmd)\n let cmd = component.replace(/^(?:[A-Z_][A-Z0-9_]*=[^\\s]*\\s+)+/, \"\");\n // Strip trailing redirections (2>&1, >/dev/null, etc.)\n cmd = cmd.replace(/\\s*\\d*>[>&]?\\s*\\S+/g, \" \").trim();\n cmd = cmd.replace(/\\s*\\d*<\\s*\\S+/g, \" \").trim();\n\n const tokens = cmd.split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return [];\n\n // Shell re-entry: bash -c / sh -c are arbitrary execution\n if ((tokens[0] === \"bash\" || tokens[0] === \"sh\") && tokens.includes(\"-c\")) {\n return [tokens[0]];\n }\n\n // For compound CLI tools, skip flags to find the real subcommand.\n // The result is \"{tool} {subcommand}\" (e.g., \"git status\", \"xargs grep\").\n const COMPOUND_TOOLS: Record<string, Set<string>> = {\n git: new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]),\n gh: new Set([\"-R\", \"--repo\"]),\n npx: new Set([\"-p\", \"--package\"]),\n pnpm: new Set([\"--filter\", \"-C\", \"--dir\"]),\n xargs: new Set([\n \"-I\",\n \"-L\",\n \"-n\",\n \"-P\",\n \"-s\",\n \"--max-args\",\n \"--max-procs\",\n \"--replace\",\n ]),\n env: new Set([]),\n nice: new Set([\"-n\", \"--adjustment\"]),\n timeout: new Set([\"-k\", \"--kill-after\", \"-s\", \"--signal\"]),\n watch: new Set([\"-n\", \"-d\", \"--interval\"]),\n };\n const flagsWithArg = COMPOUND_TOOLS[tokens[0]];\n if (flagsWithArg && tokens.length > 1) {\n for (let i = 1; i < tokens.length; i++) {\n const t = tokens[i];\n if (t.startsWith(\"-\")) {\n if (flagsWithArg.has(t) && !t.includes(\"=\")) i++;\n continue;\n }\n // env: skip VAR=val assignments\n if (t.includes(\"=\") && tokens[0] === \"env\") continue;\n // timeout: first positional arg is the duration, skip it\n if (tokens[0] === \"timeout\" && /^\\d/.test(t)) continue;\n return [`${tokens[0]} ${t}`];\n }\n return [tokens[0]];\n }\n\n const baseCmd = tokens[0];\n const results = [baseCmd];\n\n // find -exec / -execdir: extract the delegated command\n if (baseCmd === \"find\") {\n for (let i = 1; i < tokens.length; i++) {\n if (tokens[i] === \"-exec\" || tokens[i] === \"-execdir\") {\n const delegated = tokens[i + 1];\n if (delegated && delegated !== \"{}\" && delegated !== \";\") {\n // Extract just the binary name (strip any path prefix)\n const binName = delegated.split(\"/\").pop()!;\n results.push(binName);\n }\n }\n }\n }\n\n return results;\n}\n\n/** Extract the base command from a single (non-chain) command string. */\nexport function extractBaseCommand(component: string): string {\n return extractBaseCommands(component)[0] ?? \"\";\n}\n\n/**\n * Check if a Bash command should be auto-approved.\n * Returns an allow decision only when ALL chain components match.\n * Returns null to fall through to Claude Code's normal prompting.\n */\nexport function checkBashPermission(\n command: string,\n allowedCommands: string[],\n): { allow: true; reason: string } | null {\n if (!allowedCommands.length) return null;\n\n const components = splitChainComponents(command);\n if (components.length === 0) return null;\n\n const bases = components.flatMap(extractBaseCommands);\n const unapproved = bases.filter((b) => !allowedCommands.includes(b));\n\n if (unapproved.length === 0) {\n return {\n allow: true,\n reason: `All ${bases.length} component(s) approved: ${bases.join(\", \")}`,\n };\n }\n return null;\n}\n","import type { OtelLogRow, OtelMetricRow } from \"../db/store.js\";\nimport { insertOtelLogs, insertOtelMetrics } from \"../db/store.js\";\nimport { processHookEvent } from \"../hooks/ingest.js\";\nimport { log } from \"../log.js\";\n\nexport interface HookInput {\n session_id: string;\n hook_event_name: string;\n cwd?: string;\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n prompt?: string;\n source?: string;\n target?: string;\n [key: string]: unknown;\n}\n\n/** Process a hook event in-process (no subprocess spawn). */\nexport function emitHookEvent(event: HookInput): Record<string, unknown> {\n return processHookEvent(event);\n}\n\n/** Fire and forget — log errors but don't block. */\nexport function emitHookEventAsync(event: HookInput): void {\n try {\n processHookEvent(event);\n } catch (err) {\n if (process.env.PANOPTICON_DEBUG) {\n log.proxy.error(\"hook emit error:\", err);\n }\n }\n}\n\nexport interface OtelMetricPayload {\n name: string;\n value: number;\n unit?: string;\n attributes?: Record<string, unknown>;\n sessionId?: string;\n}\n\n/** Write OTel metrics directly to DB (no HTTP round-trip). */\nexport function emitOtelMetrics(metrics: OtelMetricPayload[]): void {\n if (metrics.length === 0) return;\n\n const now = Date.now() * 1_000_000; // ms → ns\n\n try {\n const rows: OtelMetricRow[] = metrics.map((m) => ({\n timestamp_ns: now,\n name: m.name,\n value: m.value,\n metric_type: \"gauge\",\n unit: m.unit,\n attributes: m.attributes,\n resource_attributes: m.sessionId\n ? { \"session.id\": m.sessionId }\n : undefined,\n session_id: m.sessionId,\n }));\n insertOtelMetrics(rows);\n } catch (err) {\n if (process.env.PANOPTICON_DEBUG) {\n log.proxy.error(\"OTel metric emit error:\", err);\n }\n }\n}\n\n/** Write OTel log events directly to DB (no HTTP round-trip). */\nexport function emitOtelLogs(\n logs: {\n body: string;\n attributes?: Record<string, unknown>;\n sessionId?: string;\n severityText?: string;\n }[],\n): void {\n if (logs.length === 0) return;\n\n const now = Date.now() * 1_000_000;\n\n try {\n const rows: OtelLogRow[] = logs.map((l) => ({\n timestamp_ns: now,\n severity_text: l.severityText ?? \"INFO\",\n body: l.body,\n attributes: l.attributes,\n resource_attributes: l.sessionId\n ? { \"session.id\": l.sessionId }\n : undefined,\n session_id: l.sessionId,\n }));\n insertOtelLogs(rows);\n } catch (err) {\n if (process.env.PANOPTICON_DEBUG) {\n log.proxy.error(\"OTel log emit error:\", err);\n }\n }\n}\n","import type { HookInput, OtelMetricPayload } from \"../emit.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./types.js\";\n\n/** Parse Anthropic Messages API (/v1/messages) request/response. */\nexport const anthropicParser: ApiFormatParser = {\n matches(path: string): boolean {\n return path.includes(\"/v1/messages\");\n },\n\n extractEvents(capture: CapturedExchange): HookInput[] {\n const events: HookInput[] = [];\n const { request, response, sessionId } = capture;\n const reqBody = request.body as Record<string, unknown> | undefined;\n const resBody = response.body as Record<string, unknown> | undefined;\n\n if (!reqBody) return events;\n\n // Extract user prompt from the last user message in the request\n const messages = reqBody.messages as\n | Array<{ role: string; content: unknown }>\n | undefined;\n if (messages) {\n const lastUser = [...messages].reverse().find((m) => m.role === \"user\");\n if (lastUser) {\n const prompt = extractTextContent(lastUser.content);\n if (prompt) {\n events.push({\n session_id: sessionId,\n hook_event_name: \"UserPromptSubmit\",\n prompt,\n });\n }\n }\n\n // Extract tool results from request (PostToolUse for prior tool calls)\n for (const msg of messages) {\n if (msg.role === \"tool\" || msg.role === \"tool_result\") {\n const toolMsg = msg as Record<string, unknown>;\n events.push({\n session_id: sessionId,\n hook_event_name: \"PostToolUse\",\n tool_name: (toolMsg.tool_use_id as string) ?? \"unknown\",\n tool_input: {\n tool_use_id: toolMsg.tool_use_id,\n content: extractTextContent(toolMsg.content),\n },\n });\n }\n }\n }\n\n // Extract tool_use blocks from response (PreToolUse)\n if (resBody && Array.isArray(resBody.content)) {\n for (const block of resBody.content) {\n if (block.type === \"tool_use\") {\n events.push({\n session_id: sessionId,\n hook_event_name: \"PreToolUse\",\n tool_name: block.name ?? \"unknown\",\n tool_input: block.input ?? {},\n });\n }\n }\n }\n\n return events;\n },\n\n extractMetrics(capture: CapturedExchange): OtelMetricPayload[] {\n const metrics: OtelMetricPayload[] = [];\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n if (!resBody) return metrics;\n\n const usage = resBody.usage as Record<string, number> | undefined;\n const model = (resBody.model as string) ?? \"unknown\";\n\n if (usage) {\n if (usage.input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.input_tokens,\n attributes: {\n model,\n token_type: \"input\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.output_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.output_tokens,\n attributes: {\n model,\n token_type: \"output\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.cache_read_input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.cache_read_input_tokens,\n attributes: {\n model,\n token_type: \"cacheRead\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.cache_creation_input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.cache_creation_input_tokens,\n attributes: {\n model,\n token_type: \"cacheWrite\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n }\n\n return metrics;\n },\n\n extractLogs(capture: CapturedExchange) {\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n const model =\n (resBody?.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n const usage = resBody?.usage as Record<string, number> | undefined;\n\n return [\n {\n body: \"api_request\",\n sessionId: capture.sessionId,\n attributes: {\n model,\n target: capture.target,\n duration_ms: capture.duration_ms,\n status: capture.response.status,\n stop_reason: resBody?.stop_reason,\n input_tokens: usage?.input_tokens,\n output_tokens: usage?.output_tokens,\n },\n },\n ];\n },\n};\n\nfunction extractTextContent(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return (\n content\n .filter((c: Record<string, unknown>) => c.type === \"text\")\n .map((c: Record<string, unknown>) => c.text)\n .join(\"\\n\") || undefined\n );\n }\n return undefined;\n}\n","import type { HookInput, OtelMetricPayload } from \"../emit.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./types.js\";\n\n/** Parse OpenAI Chat Completions API (/v1/chat/completions) request/response. */\nexport const openaiParser: ApiFormatParser = {\n matches(path: string): boolean {\n return path.includes(\"/v1/chat/completions\");\n },\n\n extractEvents(capture: CapturedExchange): HookInput[] {\n const events: HookInput[] = [];\n const { request, response, sessionId } = capture;\n const reqBody = request.body as Record<string, unknown> | undefined;\n const resBody = response.body as Record<string, unknown> | undefined;\n\n if (!reqBody) return events;\n\n // Extract user prompt from the last user message\n const messages = reqBody.messages as\n | Array<{ role: string; content: unknown; tool_call_id?: string }>\n | undefined;\n if (messages) {\n const lastUser = [...messages].reverse().find((m) => m.role === \"user\");\n if (lastUser) {\n const prompt = extractTextContent(lastUser.content);\n if (prompt) {\n events.push({\n session_id: sessionId,\n hook_event_name: \"UserPromptSubmit\",\n prompt,\n });\n }\n }\n\n // Extract tool results from request (PostToolUse for prior tool calls)\n for (const msg of messages) {\n if (msg.role === \"tool\") {\n events.push({\n session_id: sessionId,\n hook_event_name: \"PostToolUse\",\n tool_name: (msg.tool_call_id as string) ?? \"unknown\",\n tool_input: {\n tool_call_id: msg.tool_call_id,\n content: extractTextContent(msg.content),\n },\n });\n }\n }\n }\n\n // Extract tool_calls from response (PreToolUse)\n if (resBody) {\n const choices = resBody.choices as\n | Array<{ message?: { tool_calls?: Array<Record<string, unknown>> } }>\n | undefined;\n if (choices) {\n for (const choice of choices) {\n const toolCalls = choice.message?.tool_calls;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const fn = tc.function as\n | { name?: string; arguments?: string }\n | undefined;\n let parsedArgs: Record<string, unknown> = {};\n if (fn?.arguments) {\n try {\n parsedArgs = JSON.parse(fn.arguments);\n } catch {\n parsedArgs = { raw: fn.arguments };\n }\n }\n events.push({\n session_id: sessionId,\n hook_event_name: \"PreToolUse\",\n tool_name: fn?.name ?? \"unknown\",\n tool_input: parsedArgs,\n });\n }\n }\n }\n }\n }\n\n return events;\n },\n\n extractMetrics(capture: CapturedExchange): OtelMetricPayload[] {\n const metrics: OtelMetricPayload[] = [];\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n if (!resBody) return metrics;\n\n const usage = resBody.usage as Record<string, number> | undefined;\n const model =\n (resBody.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n\n if (usage) {\n if (usage.prompt_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.prompt_tokens,\n attributes: {\n model,\n token_type: \"input\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.completion_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.completion_tokens,\n attributes: {\n model,\n token_type: \"output\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n }\n\n return metrics;\n },\n\n extractLogs(capture: CapturedExchange) {\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n const model =\n (resBody?.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n const usage = resBody?.usage as Record<string, number> | undefined;\n const choices = resBody?.choices as\n | Array<{ finish_reason?: string }>\n | undefined;\n\n return [\n {\n body: \"api_request\",\n sessionId: capture.sessionId,\n attributes: {\n model,\n target: capture.target,\n duration_ms: capture.duration_ms,\n status: capture.response.status,\n stop_reason: choices?.[0]?.finish_reason,\n input_tokens: usage?.prompt_tokens,\n output_tokens: usage?.completion_tokens,\n },\n },\n ];\n },\n};\n\nfunction extractTextContent(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return (\n content\n .filter((c: Record<string, unknown>) => c.type === \"text\")\n .map((c: Record<string, unknown>) => c.text)\n .join(\"\\n\") || undefined\n );\n }\n return undefined;\n}\n","import type { HookInput, OtelMetricPayload } from \"../emit.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./types.js\";\n\n/** Parse OpenAI Responses API (/v1/responses, /backend-api/codex/responses). */\nexport const openaiResponsesParser: ApiFormatParser = {\n matches(path: string): boolean {\n return path.endsWith(\"/responses\") || path.includes(\"/responses?\");\n },\n\n extractEvents(capture: CapturedExchange): HookInput[] {\n const events: HookInput[] = [];\n const { request, response, sessionId } = capture;\n const reqBody = request.body as Record<string, unknown> | undefined;\n const resBody = response.body as Record<string, unknown> | undefined;\n\n if (!reqBody) return events;\n\n // Extract user prompt from input field\n const input = reqBody.input;\n const prompt = extractInputText(input);\n if (prompt) {\n events.push({\n session_id: sessionId,\n hook_event_name: \"UserPromptSubmit\",\n prompt,\n });\n }\n\n // Extract tool results from input (function_call_output items)\n if (Array.isArray(input)) {\n for (const item of input) {\n const it = item as Record<string, unknown>;\n if (it.type === \"function_call_output\") {\n events.push({\n session_id: sessionId,\n hook_event_name: \"PostToolUse\",\n tool_name: (it.call_id as string) ?? \"unknown\",\n tool_input: {\n call_id: it.call_id,\n content: it.output,\n },\n });\n }\n }\n }\n\n // Extract tool calls from response output (function_call items)\n if (resBody) {\n const output = resBody.output as\n | Array<Record<string, unknown>>\n | undefined;\n if (output) {\n for (const item of output) {\n if (item.type === \"function_call\") {\n let parsedArgs: Record<string, unknown> = {};\n if (typeof item.arguments === \"string\") {\n try {\n parsedArgs = JSON.parse(item.arguments);\n } catch {\n parsedArgs = { raw: item.arguments };\n }\n }\n events.push({\n session_id: sessionId,\n hook_event_name: \"PreToolUse\",\n tool_name: (item.name as string) ?? \"unknown\",\n tool_input: parsedArgs,\n });\n }\n }\n }\n }\n\n return events;\n },\n\n extractMetrics(capture: CapturedExchange): OtelMetricPayload[] {\n const metrics: OtelMetricPayload[] = [];\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n if (!resBody) return metrics;\n\n const usage = resBody.usage as Record<string, number> | undefined;\n const model =\n (resBody.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n\n if (usage) {\n if (usage.input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.input_tokens,\n attributes: {\n model,\n token_type: \"input\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.output_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.output_tokens,\n attributes: {\n model,\n token_type: \"output\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n }\n\n return metrics;\n },\n\n extractLogs(capture: CapturedExchange) {\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n const model =\n (resBody?.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n const usage = resBody?.usage as Record<string, number> | undefined;\n\n return [\n {\n body: \"api_request\",\n sessionId: capture.sessionId,\n attributes: {\n model,\n target: capture.target,\n duration_ms: capture.duration_ms,\n status: capture.response.status,\n stop_reason: resBody?.status,\n input_tokens: usage?.input_tokens,\n output_tokens: usage?.output_tokens,\n },\n },\n ];\n },\n};\n\n/** Extract text from the Responses API input field. */\nfunction extractInputText(input: unknown): string | undefined {\n // String input: \"hello\"\n if (typeof input === \"string\") return input;\n\n if (!Array.isArray(input)) return undefined;\n\n // Array of messages or items — find the last user content\n const texts: string[] = [];\n for (let i = input.length - 1; i >= 0; i--) {\n const item = input[i] as Record<string, unknown>;\n\n // Message format: { role: \"user\", content: \"hello\" }\n if (item.role === \"user\") {\n const text = extractContentField(item.content);\n if (text) return text;\n }\n\n // Item format: { type: \"message\", role: \"user\", content: [...] }\n if (item.type === \"message\" && item.role === \"user\") {\n const text = extractContentField(item.content);\n if (text) return text;\n }\n\n // Inline text item: { type: \"input_text\", text: \"hello\" }\n if (item.type === \"input_text\" && typeof item.text === \"string\") {\n texts.unshift(item.text);\n }\n }\n\n return texts.length > 0 ? texts.join(\"\\n\") : undefined;\n}\n\nfunction extractContentField(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return (\n content\n .filter(\n (c: Record<string, unknown>) =>\n c.type === \"input_text\" || c.type === \"text\",\n )\n .map((c: Record<string, unknown>) => c.text)\n .join(\"\\n\") || undefined\n );\n }\n return undefined;\n}\n","import { config } from \"../config.js\";\n\ninterface SessionState {\n id: string;\n lastRequestMs: number;\n lastMessageCount: number;\n seq: number;\n}\n\nexport class SessionTracker {\n private sessions = new Map<string, SessionState>();\n\n getOrCreateSession(\n target: string,\n requestBody?: unknown,\n ): { sessionId: string; isNew: boolean } {\n const now = Date.now();\n const existing = this.sessions.get(target);\n const messageCount = countMessages(requestBody);\n\n // Detect new session from conversation context\n if (existing) {\n const isReset = this.isConversationReset(existing, messageCount, now);\n if (!isReset) {\n existing.lastRequestMs = now;\n if (messageCount > 0) existing.lastMessageCount = messageCount;\n return { sessionId: existing.id, isNew: false };\n }\n }\n\n // New session\n const seq = existing ? existing.seq + 1 : 1;\n const date = new Date(now).toISOString().slice(0, 10).replace(/-/g, \"\");\n const sessionId = `${target}-${date}-${String(seq).padStart(3, \"0\")}`;\n\n this.sessions.set(target, {\n id: sessionId,\n lastRequestMs: now,\n lastMessageCount: messageCount,\n seq,\n });\n return { sessionId, isNew: true };\n }\n\n private isConversationReset(\n state: SessionState,\n messageCount: number,\n now: number,\n ): boolean {\n // Context-based detection: if message count drops significantly,\n // the conversation was cleared/reset. A request with <= 2 messages\n // (system + user) after a longer conversation is a strong reset signal.\n if (messageCount > 0 && state.lastMessageCount > 3 && messageCount <= 2) {\n return true;\n }\n\n // Significant drop in message count (more than halved and dropped by 3+)\n if (\n messageCount > 0 &&\n state.lastMessageCount > 0 &&\n messageCount < state.lastMessageCount / 2 &&\n state.lastMessageCount - messageCount >= 3\n ) {\n return true;\n }\n\n // Fallback: idle timeout\n if (now - state.lastRequestMs >= config.proxyIdleSessionMs) {\n return true;\n }\n\n return false;\n }\n}\n\n/** Count non-system messages in a chat request body. */\nfunction countMessages(body: unknown): number {\n if (typeof body !== \"object\" || body === null) return 0;\n const messages = (body as Record<string, unknown>).messages;\n if (!Array.isArray(messages)) return 0;\n\n // Count only non-system messages — system messages don't indicate\n // conversation depth and are present in every request\n return messages.filter((m: Record<string, unknown>) => m.role !== \"system\")\n .length;\n}\n","/**\n * Accumulate SSE streaming responses into a final reconstructed message.\n * Forwards chunks to the client in real-time while buffering for capture.\n */\n\nexport interface StreamAccumulator {\n /** Feed a raw SSE chunk. Returns the chunk unchanged (for forwarding). */\n push(chunk: Buffer): Buffer;\n /** Finalize and return the reconstructed response body. */\n finish(): Record<string, unknown>;\n}\n\n/** Create an accumulator for Anthropic streaming format. */\nexport function createAnthropicAccumulator(): StreamAccumulator {\n let message: Record<string, unknown> = {};\n let usage: Record<string, number> = {};\n const contentBlocks: Array<Record<string, unknown>> = [];\n let currentBlockIndex = -1;\n const textParts = new Map<number, string>();\n\n return {\n push(chunk: Buffer): Buffer {\n const text = chunk.toString(\"utf-8\");\n for (const line of text.split(\"\\n\")) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") continue;\n\n try {\n const event = JSON.parse(data);\n switch (event.type) {\n case \"message_start\":\n message = event.message ?? {};\n usage = (event.message?.usage as Record<string, number>) ?? {};\n break;\n case \"content_block_start\":\n currentBlockIndex = event.index ?? contentBlocks.length;\n contentBlocks[currentBlockIndex] = event.content_block ?? {};\n break;\n case \"content_block_delta\":\n if (event.delta?.type === \"text_delta\" && event.delta.text) {\n const idx = event.index ?? currentBlockIndex;\n textParts.set(\n idx,\n (textParts.get(idx) ?? \"\") + event.delta.text,\n );\n } else if (\n event.delta?.type === \"input_json_delta\" &&\n event.delta.partial_json\n ) {\n const idx = event.index ?? currentBlockIndex;\n textParts.set(\n idx,\n (textParts.get(idx) ?? \"\") + event.delta.partial_json,\n );\n }\n break;\n case \"message_delta\":\n if (event.delta) {\n Object.assign(message, event.delta);\n }\n if (event.usage) {\n Object.assign(usage, event.usage);\n }\n break;\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n return chunk;\n },\n\n finish(): Record<string, unknown> {\n // Reconstruct content blocks with accumulated text\n const content = contentBlocks.map((block, i) => {\n if (block.type === \"text\") {\n return { ...block, text: textParts.get(i) ?? block.text ?? \"\" };\n }\n if (block.type === \"tool_use\") {\n const raw = textParts.get(i);\n let input = block.input;\n if (raw) {\n try {\n input = JSON.parse(raw);\n } catch {\n input = { raw };\n }\n }\n return { ...block, input };\n }\n return block;\n });\n\n return {\n ...message,\n content,\n usage,\n };\n },\n };\n}\n\n/** Create an accumulator for OpenAI streaming format. */\nexport function createOpenaiAccumulator(): StreamAccumulator {\n let model = \"\";\n let finishReason: string | null = null;\n let role = \"\";\n let contentParts = \"\";\n const toolCalls = new Map<\n number,\n { id: string; type: string; function: { name: string; arguments: string } }\n >();\n let usage: Record<string, number> = {};\n\n return {\n push(chunk: Buffer): Buffer {\n const text = chunk.toString(\"utf-8\");\n for (const line of text.split(\"\\n\")) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") continue;\n\n try {\n const event = JSON.parse(data);\n if (event.model) model = event.model;\n if (event.usage) usage = event.usage;\n\n const choice = event.choices?.[0];\n if (!choice) continue;\n\n if (choice.finish_reason) finishReason = choice.finish_reason;\n\n const delta = choice.delta;\n if (!delta) continue;\n\n if (delta.role) role = delta.role;\n if (delta.content) contentParts += delta.content;\n\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n const existing = toolCalls.get(idx);\n if (existing) {\n if (tc.function?.arguments) {\n existing.function.arguments += tc.function.arguments;\n }\n } else {\n toolCalls.set(idx, {\n id: tc.id ?? \"\",\n type: tc.type ?? \"function\",\n function: {\n name: tc.function?.name ?? \"\",\n arguments: tc.function?.arguments ?? \"\",\n },\n });\n }\n }\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n return chunk;\n },\n\n finish(): Record<string, unknown> {\n const message: Record<string, unknown> = {\n role: role || \"assistant\",\n content: contentParts || null,\n };\n\n if (toolCalls.size > 0) {\n message.tool_calls = [...toolCalls.entries()]\n .sort(([a], [b]) => a - b)\n .map(([, tc]) => tc);\n }\n\n return {\n model,\n choices: [\n {\n index: 0,\n message,\n finish_reason: finishReason,\n },\n ],\n usage,\n };\n },\n };\n}\n\n/** Detect if a request is asking for streaming. */\nexport function isStreamingRequest(body: unknown): boolean {\n if (typeof body === \"object\" && body !== null) {\n return (body as Record<string, unknown>).stream === true;\n }\n return false;\n}\n","/**\n * WebSocket frame parser for capturing messages flowing through the tunnel.\n *\n * Extracts complete text messages from raw TCP data without modifying the\n * stream — the caller forwards the original bytes to the other socket and\n * feeds a copy here for inspection.\n */\n\nexport class WebSocketMessageExtractor {\n private buffer = Buffer.alloc(0);\n private fragments: Buffer[] = [];\n private currentOpcode = 0;\n\n onMessage?: (message: string) => void;\n\n push(data: Buffer): void {\n this.buffer = Buffer.concat([this.buffer, data]);\n this.drain();\n }\n\n private drain(): void {\n while (this.buffer.length >= 2) {\n const byte0 = this.buffer[0];\n const byte1 = this.buffer[1];\n const fin = (byte0 & 0x80) !== 0;\n const opcode = byte0 & 0x0f;\n const masked = (byte1 & 0x80) !== 0;\n let payloadLen = byte1 & 0x7f;\n\n let headerLen = 2;\n if (payloadLen === 126) {\n if (this.buffer.length < 4) return;\n payloadLen = this.buffer.readUInt16BE(2);\n headerLen = 4;\n } else if (payloadLen === 127) {\n if (this.buffer.length < 10) return;\n payloadLen = Number(this.buffer.readBigUInt64BE(2));\n headerLen = 10;\n }\n\n if (masked) headerLen += 4;\n\n const totalLen = headerLen + payloadLen;\n if (this.buffer.length < totalLen) return;\n\n // Extract and optionally unmask payload\n let payload = this.buffer.subarray(headerLen, totalLen);\n if (masked) {\n const maskKey = this.buffer.subarray(headerLen - 4, headerLen);\n payload = Buffer.from(payload); // copy before mutating\n for (let i = 0; i < payload.length; i++) {\n payload[i] ^= maskKey[i % 4];\n }\n }\n\n // Handle text/binary frames and continuations\n if (opcode === 0x1 || opcode === 0x2) {\n this.currentOpcode = opcode;\n this.fragments = [payload];\n } else if (opcode === 0x0) {\n this.fragments.push(payload);\n }\n\n if (fin && this.fragments.length > 0 && opcode <= 0x2) {\n if (this.currentOpcode === 0x1) {\n try {\n const msg = Buffer.concat(this.fragments).toString(\"utf-8\");\n this.onMessage?.(msg);\n } catch {\n // malformed utf-8, skip\n }\n }\n this.fragments = [];\n }\n\n this.buffer = this.buffer.subarray(totalLen);\n }\n }\n}\n"],"mappings":"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","names":["fs","path","path","fs","path","config","path","path","extractTextContent","path","upstream","log"]}
1
+ {"version":3,"sources":["../src/proxy/server.ts","../src/hooks/ingest.ts","../src/eventConfig.ts","../src/hooks/permissions.ts","../src/proxy/emit.ts","../src/proxy/formats/anthropic.ts","../src/proxy/formats/openai.ts","../src/proxy/formats/openai-responses.ts","../src/proxy/sessions.ts","../src/proxy/streaming.ts","../src/proxy/ws-capture.ts"],"sourcesContent":["import http from \"node:http\";\nimport https from \"node:https\";\nimport { config } from \"../config.js\";\nimport { log } from \"../log.js\";\nimport { addBreadcrumb, captureException } from \"../sentry.js\";\nimport { allTargets, getTarget } from \"../targets/index.js\";\nimport { emitHookEventAsync, emitOtelLogs, emitOtelMetrics } from \"./emit.js\";\nimport { anthropicParser } from \"./formats/anthropic.js\";\nimport { openaiParser } from \"./formats/openai.js\";\nimport { openaiResponsesParser } from \"./formats/openai-responses.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./formats/types.js\";\nimport { SessionTracker } from \"./sessions.js\";\nimport {\n createAnthropicAccumulator,\n createOpenaiAccumulator,\n isStreamingRequest,\n} from \"./streaming.js\";\nimport { WebSocketMessageExtractor } from \"./ws-capture.js\";\n\n// Build upstream route table from target adapters that have proxy specs,\n// plus static entries for targets without full adapters (e.g. openai, google)\nfunction buildUpstreamRoutes(): Record<string, string> {\n const routes: Record<string, string> = {};\n for (const v of allTargets()) {\n if (v.proxy && typeof v.proxy.upstreamHost === \"string\") {\n routes[v.id] = v.proxy.upstreamHost;\n }\n }\n // Static routes for API-only targets (not CLI tools with full adapters)\n if (!routes.openai) routes.openai = \"api.openai.com\";\n if (!routes.google) routes.google = \"generativelanguage.googleapis.com\";\n // Alias: Claude adapter registers as \"claude\" but ANTHROPIC_BASE_URL uses /proxy/anthropic\n if (!routes.anthropic) routes.anthropic = \"api.anthropic.com\";\n return routes;\n}\n\nconst UPSTREAM_ROUTES = buildUpstreamRoutes();\n\n// Pre-compute known route prefixes for error messages\nconst KNOWN_ROUTES_MSG = [\n ...Object.keys(UPSTREAM_ROUTES),\n ...allTargets()\n .filter((v) => v.proxy && typeof v.proxy.upstreamHost === \"function\")\n .map((v) => v.id),\n]\n .filter((v, i, a) => a.indexOf(v) === i)\n .map((v) => `/${v}/*`)\n .join(\", \");\n\nconst FORMAT_PARSERS: ApiFormatParser[] = [\n anthropicParser,\n openaiParser,\n openaiResponsesParser,\n];\n\nconst sessions = new SessionTracker();\n\ninterface Route {\n target: string;\n upstream: string;\n path: string;\n}\n\nfunction parseRoute(\n url: string,\n headers?: http.IncomingHttpHeaders,\n): Route | null {\n // Match /target/rest-of-path\n const match = url.match(/^\\/([^/]+)(\\/.*)?$/);\n if (!match) return null;\n\n const targetId = match[1];\n const requestPath = match[2] ?? \"/\";\n\n // Check target adapter for dynamic routing (e.g. Codex JWT auto-detect)\n const targetAdapter = getTarget(targetId);\n if (targetAdapter?.proxy) {\n const { proxy } = targetAdapter;\n const flatHeaders = flattenHeaders(headers ?? {});\n\n const upstream =\n typeof proxy.upstreamHost === \"function\"\n ? proxy.upstreamHost(flatHeaders)\n : proxy.upstreamHost;\n\n const finalPath = proxy.rewritePath\n ? proxy.rewritePath(requestPath, flatHeaders)\n : requestPath;\n\n return { target: targetId, upstream, path: finalPath };\n }\n\n // Fall back to static route table\n const upstream = UPSTREAM_ROUTES[targetId];\n if (!upstream) return null;\n\n return { target: targetId, upstream, path: requestPath };\n}\n\nfunction collectBody(req: http.IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nfunction processCapture(capture: CapturedExchange): void {\n for (const parser of FORMAT_PARSERS) {\n if (!parser.matches(capture.request.path)) continue;\n\n const hookEvents = parser.extractEvents(capture);\n for (const event of hookEvents) {\n event.source = \"proxy\";\n event.target = capture.target;\n emitHookEventAsync(event);\n }\n\n const metrics = parser.extractMetrics(capture);\n for (const metric of metrics) {\n metric.attributes = { ...metric.attributes, source: \"proxy\" };\n }\n if (metrics.length > 0) {\n emitOtelMetrics(metrics);\n }\n\n const logs = parser.extractLogs(capture);\n for (const log of logs) {\n log.attributes = { ...log.attributes, source: \"proxy\" };\n }\n if (logs.length > 0) {\n emitOtelLogs(logs);\n }\n\n return; // Only use first matching parser\n }\n}\n\nfunction forwardNonStreaming(\n route: Route,\n clientReq: http.IncomingMessage,\n clientRes: http.ServerResponse,\n requestBody: Buffer,\n parsedReqBody: unknown,\n): void {\n const startMs = Date.now();\n const { sessionId, isNew } = sessions.getOrCreateSession(\n route.target,\n parsedReqBody,\n );\n\n if (isNew) {\n emitHookEventAsync({\n session_id: sessionId,\n hook_event_name: \"SessionStart\",\n source: \"proxy\",\n target: route.target,\n });\n }\n\n const headers: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(clientReq.headers)) {\n if (value !== undefined && key !== \"host\") {\n headers[key] = value;\n }\n }\n\n const upstreamReq = https.request(\n {\n hostname: route.upstream,\n port: 443,\n path: route.path,\n method: clientReq.method,\n headers,\n },\n (upstreamRes) => {\n const chunks: Buffer[] = [];\n upstreamRes.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n upstreamRes.on(\"end\", () => {\n const responseBody = Buffer.concat(chunks);\n const duration_ms = Date.now() - startMs;\n\n // Forward response to client\n clientRes.writeHead(upstreamRes.statusCode ?? 200, upstreamRes.headers);\n clientRes.end(responseBody);\n\n // Capture and process\n let parsedResBody: unknown;\n try {\n parsedResBody = JSON.parse(responseBody.toString(\"utf-8\"));\n } catch {\n parsedResBody = {};\n }\n\n const capture: CapturedExchange = {\n target: route.target,\n sessionId,\n timestamp_ms: startMs,\n request: {\n path: route.path,\n headers: flattenHeaders(clientReq.headers),\n body: parsedReqBody,\n },\n response: {\n status: upstreamRes.statusCode ?? 0,\n body: parsedResBody,\n },\n duration_ms,\n };\n\n processCapture(capture);\n });\n },\n );\n\n upstreamReq.on(\"error\", (err) => {\n log.proxy.error(`Upstream error (${route.target}):`, err.message);\n addBreadcrumb(\n \"proxy\",\n `Upstream error: ${route.target}`,\n { target: route.target, upstream: route.upstream, error: err.message },\n \"error\",\n );\n if (!clientRes.headersSent) {\n clientRes.writeHead(502);\n clientRes.end(\n JSON.stringify({ error: \"upstream_error\", message: err.message }),\n );\n }\n });\n\n upstreamReq.write(requestBody);\n upstreamReq.end();\n}\n\nfunction forwardStreaming(\n route: Route,\n clientReq: http.IncomingMessage,\n clientRes: http.ServerResponse,\n requestBody: Buffer,\n parsedReqBody: unknown,\n): void {\n const startMs = Date.now();\n const { sessionId, isNew } = sessions.getOrCreateSession(\n route.target,\n parsedReqBody,\n );\n\n if (isNew) {\n emitHookEventAsync({\n session_id: sessionId,\n hook_event_name: \"SessionStart\",\n source: \"proxy\",\n target: route.target,\n });\n }\n\n const targetSpec = getTarget(route.target);\n const accumulator =\n targetSpec?.proxy?.accumulatorType === \"anthropic\"\n ? createAnthropicAccumulator()\n : createOpenaiAccumulator();\n\n const headers: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(clientReq.headers)) {\n if (value !== undefined && key !== \"host\") {\n headers[key] = value;\n }\n }\n\n const upstreamReq = https.request(\n {\n hostname: route.upstream,\n port: 443,\n path: route.path,\n method: clientReq.method,\n headers,\n },\n (upstreamRes) => {\n // Forward headers immediately\n clientRes.writeHead(upstreamRes.statusCode ?? 200, upstreamRes.headers);\n\n // Stream chunks through: forward to client + accumulate for capture\n upstreamRes.on(\"data\", (chunk: Buffer) => {\n accumulator.push(chunk);\n clientRes.write(chunk);\n });\n\n upstreamRes.on(\"end\", () => {\n clientRes.end();\n\n const duration_ms = Date.now() - startMs;\n const reconstructed = accumulator.finish();\n\n const capture: CapturedExchange = {\n target: route.target,\n sessionId,\n timestamp_ms: startMs,\n request: {\n path: route.path,\n headers: flattenHeaders(clientReq.headers),\n body: parsedReqBody,\n },\n response: {\n status: upstreamRes.statusCode ?? 0,\n body: reconstructed,\n },\n duration_ms,\n };\n\n processCapture(capture);\n });\n },\n );\n\n upstreamReq.on(\"error\", (err) => {\n log.proxy.error(`Upstream error (${route.target}):`, err.message);\n addBreadcrumb(\n \"proxy\",\n `Upstream error: ${route.target}`,\n { target: route.target, upstream: route.upstream, error: err.message },\n \"error\",\n );\n if (!clientRes.headersSent) {\n clientRes.writeHead(502);\n clientRes.end(\n JSON.stringify({ error: \"upstream_error\", message: err.message }),\n );\n }\n });\n\n upstreamReq.write(requestBody);\n upstreamReq.end();\n}\n\nfunction flattenHeaders(\n headers: http.IncomingHttpHeaders,\n): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nfunction tunnelWebSocket(\n req: http.IncomingMessage,\n clientSocket: import(\"node:stream\").Duplex,\n head: Buffer,\n): void {\n const url = req.url ?? \"\";\n const route = parseRoute(url, req.headers);\n\n if (!route) {\n clientSocket.end(\"HTTP/1.1 404 Not Found\\r\\n\\r\\n\");\n return;\n }\n\n // Track session for this target\n const { sessionId, isNew } = sessions.getOrCreateSession(route.target, {});\n if (isNew) {\n emitHookEventAsync({\n session_id: sessionId,\n hook_event_name: \"SessionStart\",\n source: \"proxy\",\n target: route.target,\n });\n }\n\n // Catch client socket errors early (before upstream upgrade completes)\n clientSocket.on(\"error\", (err) => {\n log.proxy.error(`WebSocket client error (${route.target}):`, err.message);\n });\n\n // Forward headers, replacing host with upstream.\n // Strip Sec-WebSocket-Extensions to disable permessage-deflate so the\n // frame capture can read uncompressed text payloads.\n const proxyHeaders: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (\n key !== \"host\" &&\n key !== \"sec-websocket-extensions\" &&\n value !== undefined\n ) {\n proxyHeaders[key] = value;\n }\n }\n proxyHeaders.host = route.upstream;\n\n // Use https.request to perform the upstream WebSocket upgrade.\n // Node's HTTP parser handles the 101 response; we get the raw socket back.\n const proxyReq = https.request({\n hostname: route.upstream,\n port: 443,\n path: route.path,\n method: req.method,\n headers: proxyHeaders,\n });\n\n proxyReq.on(\"upgrade\", (proxyRes, proxySocket, proxyHead) => {\n // Reconstruct the 101 response for the client\n let response = `HTTP/${proxyRes.httpVersion} ${proxyRes.statusCode} ${proxyRes.statusMessage}\\r\\n`;\n for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {\n response += `${proxyRes.rawHeaders[i]}: ${proxyRes.rawHeaders[i + 1]}\\r\\n`;\n }\n response += \"\\r\\n\";\n\n clientSocket.write(response);\n if (proxyHead.length > 0) clientSocket.write(proxyHead);\n if (head.length > 0) proxySocket.write(head);\n\n // Tap into WebSocket messages for capture while forwarding bytes unchanged.\n // Client messages contain the request; server \"response.completed\" has the\n // full response with usage data.\n const clientExtractor = new WebSocketMessageExtractor();\n const serverExtractor = new WebSocketMessageExtractor();\n let pendingRequest: unknown;\n let requestTimestamp = Date.now();\n\n clientExtractor.onMessage = (msg) => {\n try {\n pendingRequest = JSON.parse(msg);\n requestTimestamp = Date.now();\n } catch (err) {\n log.proxy.error(\"Failed to parse client WebSocket message:\", err);\n captureException(err, { component: \"proxy\", phase: \"ws-client-parse\" });\n }\n };\n\n serverExtractor.onMessage = (msg) => {\n try {\n const event = JSON.parse(msg) as Record<string, unknown>;\n if (event.type === \"response.completed\" && pendingRequest) {\n const capture: CapturedExchange = {\n target: route.target,\n sessionId,\n timestamp_ms: requestTimestamp,\n request: {\n path: route.path,\n headers: flattenHeaders(req.headers),\n body: pendingRequest,\n },\n response: {\n status: 200,\n body: event.response,\n },\n duration_ms: Date.now() - requestTimestamp,\n };\n processCapture(capture);\n pendingRequest = undefined;\n }\n } catch (err) {\n log.proxy.error(\"Failed to parse server WebSocket message:\", err);\n captureException(err, { component: \"proxy\", phase: \"ws-server-parse\" });\n }\n };\n\n proxySocket.on(\"data\", (chunk: Buffer) => {\n serverExtractor.push(chunk);\n clientSocket.write(chunk);\n });\n clientSocket.on(\"data\", (chunk: Buffer) => {\n clientExtractor.push(chunk);\n proxySocket.write(chunk);\n });\n\n proxySocket.on(\"error\", () => clientSocket.destroy());\n proxySocket.on(\"close\", () => clientSocket.destroy());\n clientSocket.on(\"close\", () => proxySocket.destroy());\n });\n\n proxyReq.on(\"error\", (err) => {\n log.proxy.error(`WebSocket upstream error (${route.target}):`, err.message);\n clientSocket.end(\"HTTP/1.1 502 Bad Gateway\\r\\n\\r\\n\");\n });\n\n proxyReq.on(\"response\", (res) => {\n // Upstream rejected the upgrade (non-101 response) — forward as-is\n let response = `HTTP/${res.httpVersion} ${res.statusCode} ${res.statusMessage}\\r\\n`;\n for (let i = 0; i < res.rawHeaders.length; i += 2) {\n response += `${res.rawHeaders[i]}: ${res.rawHeaders[i + 1]}\\r\\n`;\n }\n response += \"\\r\\n\";\n clientSocket.write(response);\n res.pipe(clientSocket);\n });\n\n proxyReq.end();\n}\n\n/** Handle a proxy API request. Expects the URL to have a target prefix. */\nexport async function handleProxyRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n): Promise<void> {\n const url = req.url ?? \"\";\n\n const route = parseRoute(url, req.headers);\n if (!route) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"unknown_route\",\n message: `Unknown target prefix. Known: ${KNOWN_ROUTES_MSG}`,\n }),\n );\n return;\n }\n\n try {\n const requestBody = await collectBody(req);\n\n // Parse request body once — used for streaming detection and session tracking\n let parsedReqBody: unknown;\n let streaming = false;\n try {\n parsedReqBody = JSON.parse(requestBody.toString(\"utf-8\"));\n streaming = isStreamingRequest(parsedReqBody);\n } catch {\n parsedReqBody = {};\n }\n\n if (streaming) {\n forwardStreaming(route, req, res, requestBody, parsedReqBody);\n } else {\n forwardNonStreaming(route, req, res, requestBody, parsedReqBody);\n }\n } catch (err) {\n log.proxy.error(\"Proxy error:\", err);\n captureException(err, { component: \"proxy\", path: url });\n if (!res.headersSent) {\n res.writeHead(500);\n res.end();\n }\n }\n}\n\n/** Handle a WebSocket upgrade for proxy tunneling. */\nexport { tunnelWebSocket };\n\nexport function createProxyServer(): http.Server {\n const server = http.createServer(async (req, res) => {\n const url = req.url ?? \"\";\n const method = req.method ?? \"\";\n\n if (url === \"/health\" && method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", port: config.proxyPort }));\n return;\n }\n\n if (method !== \"POST\") {\n res.writeHead(405);\n res.end();\n return;\n }\n\n await handleProxyRequest(req, res);\n });\n\n server.on(\"upgrade\", (req, socket, head) => {\n tunnelWebSocket(req, socket, head);\n });\n\n return server;\n}\n\n// When run directly, start the server\nconst entryScript = process.argv[1]?.replaceAll(\"\\\\\", \"/\") ?? \"\";\nif (\n entryScript.endsWith(\"/proxy/server.js\") ||\n entryScript.endsWith(\"/proxy/server.ts\")\n) {\n const server = createProxyServer();\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n log.proxy.warn(\n `Already running on ${config.proxyHost}:${config.proxyPort}`,\n );\n process.exit(0);\n }\n throw err;\n });\n server.listen(config.proxyPort, config.proxyHost, () => {\n log.proxy.info(`Listening on ${config.proxyHost}:${config.proxyPort}`);\n log.proxy.info(\"Routes:\");\n for (const [prefix, host] of Object.entries(UPSTREAM_ROUTES)) {\n log.proxy.info(` /${prefix}/* → https://${host}/*`);\n }\n // Show dynamic-routed targets not in the static table\n for (const v of allTargets()) {\n if (\n v.proxy &&\n typeof v.proxy.upstreamHost === \"function\" &&\n !UPSTREAM_ROUTES[v.id]\n ) {\n log.proxy.info(` /${v.id}/* → (dynamic)`);\n }\n }\n });\n\n const shutdown = () => {\n server.close();\n process.exit(0);\n };\n process.on(\"SIGTERM\", shutdown);\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGHUP\", shutdown);\n}\n","import { execFileSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { config } from \"../config.js\";\nimport {\n incrementEventTypeCount,\n incrementToolCount,\n insertHookEvent,\n insertRepoConfigSnapshot,\n insertUserConfigSnapshot,\n upsertSession,\n upsertSessionCwd,\n upsertSessionRepository,\n} from \"../db/store.js\";\nimport { isEventEnabled } from \"../eventConfig.js\";\nimport { log } from \"../log.js\";\nimport { type RepoInfo, resolveRepoFromCwd } from \"../repo.js\";\nimport { isGitignored, readConfig, resolveGitRoot } from \"../scanner.js\";\nimport { allTargets } from \"../targets/index.js\";\nimport type { TargetAdapter } from \"../targets/types.js\";\nimport { checkBashPermission } from \"./permissions.js\";\n\n// Cache: cwd → { name, email }\nconst gitIdentityCache = new Map<\n string,\n { name: string | null; email: string | null }\n>();\n\nfunction resolveGitIdentity(cwd: string): {\n name: string | null;\n email: string | null;\n} {\n const cached = gitIdentityCache.get(cwd);\n if (cached) return cached;\n\n const result = { name: null as string | null, email: null as string | null };\n try {\n result.name =\n execFileSync(\"git\", [\"-C\", cwd, \"config\", \"user.name\"], {\n encoding: \"utf-8\",\n timeout: 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim() || null;\n } catch {\n // no user.name configured\n }\n try {\n result.email =\n execFileSync(\"git\", [\"-C\", cwd, \"config\", \"user.email\"], {\n encoding: \"utf-8\",\n timeout: 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim() || null;\n } catch {\n // no user.email configured\n }\n gitIdentityCache.set(cwd, result);\n return result;\n}\n\n// Last resolved repo per session — used as fallback for events without paths\n// (e.g. Stop, UserPromptSubmit). Long-lived in the server process.\nconst lastSessionRepo = new Map<string, string>();\n\n// Track sessions where we've already captured user config\nconst userConfigCaptured = new Set<string>();\n\n// Track session:repo pairs where we've already captured repo config\nconst seenSessionRepos = new Set<string>();\n\nexport interface HookInput {\n session_id: string;\n hook_event_name: string;\n cwd?: string;\n repository?: string;\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n source?: string;\n target?: string;\n prompt?: string;\n [key: string]: unknown;\n}\n\nconst ALLOWED_PATH = path.join(config.dataDir, \"permissions\", \"allowed.json\");\n\ninterface AllowedList {\n bash_commands: string[];\n tools: string[];\n}\n\nfunction loadAllowed(): AllowedList | null {\n try {\n return JSON.parse(fs.readFileSync(ALLOWED_PATH, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nexport function isPanopticonMcpTool(toolName: string): boolean {\n return (\n toolName.startsWith(\"mcp__plugin_panopticon_panopticon__\") ||\n toolName.startsWith(\"mcp__panopticon__\")\n );\n}\n\n/**\n * Extract the shell_pwd from hook event data.\n * Claude Code may send it at the top level or nested in tool_input.\n */\nexport function extractShellPwd(data: HookInput): string | null {\n if (typeof data.shell_pwd === \"string\") return data.shell_pwd;\n if (typeof data.tool_input?.shell_pwd === \"string\")\n return data.tool_input.shell_pwd;\n return null;\n}\n\nexport type PathSource =\n | \"shell_pwd\"\n | \"tool_input.file_path\"\n | \"tool_input.path\"\n | \"cwd\";\n\nexport interface EventPath {\n dir: string;\n source: PathSource;\n}\n\n/**\n * Extract every directory path we can find from a hook event, in priority\n * order. Consumers can iterate the list to greedily resolve repos, capture\n * config snapshots, etc. without duplicating extraction logic.\n */\nexport function extractEventPaths(data: HookInput): EventPath[] {\n const paths: EventPath[] = [];\n const seen = new Set<string>();\n const add = (dir: string, source: PathSource) => {\n if (!seen.has(dir)) {\n seen.add(dir);\n paths.push({ dir, source });\n }\n };\n\n const shellPwd = extractShellPwd(data);\n if (shellPwd) add(shellPwd, \"shell_pwd\");\n\n const toolInput = data.tool_input;\n if (toolInput && typeof toolInput === \"object\") {\n const fp = (toolInput as Record<string, unknown>).file_path;\n if (typeof fp === \"string\" && path.isAbsolute(fp)) {\n add(path.dirname(fp), \"tool_input.file_path\");\n }\n const p = (toolInput as Record<string, unknown>).path;\n if (typeof p === \"string\" && path.isAbsolute(p)) {\n add(path.dirname(p), \"tool_input.path\");\n }\n }\n\n if (typeof data.cwd === \"string\") add(data.cwd, \"cwd\");\n\n return paths;\n}\n\nexport type ResolveFn = (dir: string) => RepoInfo | string | null;\n\nfunction normalizeResolveFn(\n resolveFn: ResolveFn,\n): (dir: string) => RepoInfo | null {\n return (dir: string) => {\n const result = resolveFn(dir);\n if (!result) return null;\n if (typeof result === \"string\") return { repo: result };\n return result;\n };\n}\n\n/**\n * Resolve the repository for an event, trying multiple sources in priority order:\n * 1. Explicit repository field\n * 2. shell_pwd (actual cwd at event time)\n * 3. tool_input.file_path / path\n * 4. Session cwd\n * 5. Last resolved repo for this session (cumulative cache)\n */\nexport function resolveEventRepo(\n data: HookInput,\n resolveFn: ResolveFn = resolveRepoFromCwd,\n): string | null {\n const sessionId = data.session_id ?? \"unknown\";\n\n let repo = data.repository ?? null;\n\n if (!repo) {\n const resolve = normalizeResolveFn(resolveFn);\n for (const { dir } of extractEventPaths(data)) {\n const info = resolve(dir);\n if (info) {\n repo = info.repo;\n break;\n }\n }\n }\n\n // Fallback: use the last resolved repo for this session\n if (!repo) {\n repo = lastSessionRepo.get(sessionId) ?? null;\n }\n\n // Cache for future events in this session\n if (repo) {\n lastSessionRepo.set(sessionId, repo);\n }\n\n return repo;\n}\n\n/**\n * Resolve ALL repos touched by this event — the primary repo plus any\n * additional repos referenced via tool_input paths.\n * Returns deduplicated { repo, dir, branch } tuples.\n */\nexport function resolveAllEventRepos(\n data: HookInput,\n resolveFn: ResolveFn = resolveRepoFromCwd,\n): Array<{ repo: string; dir: string; branch?: string | null }> {\n const results: Array<{\n repo: string;\n dir: string;\n branch?: string | null;\n }> = [];\n const seen = new Set<string>();\n const resolve = normalizeResolveFn(resolveFn);\n\n // Explicit repository field first\n if (data.repository) {\n seen.add(data.repository);\n const shellPwd = extractShellPwd(data);\n results.push({\n repo: data.repository,\n dir: shellPwd ?? (data.cwd as string) ?? \".\",\n });\n }\n\n for (const { dir } of extractEventPaths(data)) {\n const info = resolve(dir);\n if (info && !seen.has(info.repo)) {\n seen.add(info.repo);\n results.push({ repo: info.repo, dir, branch: info.branch });\n }\n }\n\n return results;\n}\n\n/** Clear the session repo cache (for testing). */\nexport function _resetSessionRepoCache(): void {\n lastSessionRepo.clear();\n}\n\n/**\n * Process a hook event: normalize, store, and optionally enforce permissions.\n * Returns a JSON-serializable response body (permission decision or {}).\n */\n/** Cache of resolved target per session — once identified, reuse for all events. */\nconst sessionTargetCache = new Map<string, string>();\n\n/** Clear the session target cache (for testing). */\nexport function _resetSessionTargetCache(): void {\n sessionTargetCache.clear();\n}\n\n/**\n * Resolve which target adapter sent this event, using the source/target\n * field, session cache, eventMap matching, or payload heuristics.\n */\nfunction resolveTarget(data: HookInput): TargetAdapter | undefined {\n const targets = allTargets();\n const sessionId = data.session_id;\n\n // 1. Explicit source/target field\n const source = data.source ?? data.target;\n if (source) {\n for (const v of targets) {\n if (v.id === source) {\n if (sessionId) sessionTargetCache.set(sessionId, v.id);\n return v;\n }\n }\n }\n\n // 2. Session cache — reuse previously identified target\n if (sessionId) {\n const cached = sessionTargetCache.get(sessionId);\n if (cached) {\n return targets.find((v) => v.id === cached);\n }\n }\n\n // 3. eventMap match (catches Gemini's BeforeTool, AfterTool, BeforeModel)\n const rawEvent = data.hook_event_name;\n if (rawEvent) {\n let matched: TargetAdapter | undefined;\n for (const v of targets) {\n if (rawEvent in v.events.eventMap) {\n if (matched) {\n log.hooks.warn(\n `Event \"${rawEvent}\" claimed by both \"${matched.id}\" and \"${v.id}\" — using \"${matched.id}\"`,\n );\n break;\n }\n matched = v;\n }\n }\n if (matched) {\n if (sessionId) sessionTargetCache.set(sessionId, matched.id);\n return matched;\n }\n }\n\n // 4. Model-based detection (last resort) — iterate adapter ident specs.\n // Logs a warning so operators know detection was ambiguous (see #73).\n const model = typeof data.model === \"string\" ? data.model : null;\n if (model) {\n let matched: TargetAdapter | undefined;\n for (const v of targets) {\n if (v.ident?.modelPatterns?.some((re) => re.test(model))) {\n matched = v;\n break;\n }\n }\n if (matched) {\n log.hooks.warn(\n `Target resolved via model-name heuristic: model=\"${model}\" → \"${matched.id}\". ` +\n `Set an explicit source/target field to avoid ambiguous detection.`,\n );\n if (sessionId) sessionTargetCache.set(sessionId, matched.id);\n return matched;\n }\n }\n\n return undefined;\n}\n\n/**\n * Process a hook event: normalize, store, and optionally enforce permissions.\n *\n * Called by the server's POST /hooks handler for every hook event from any\n * target (Claude Code, Gemini, Codex). The flow:\n * 1. Resolve which target adapter sent this event\n * 2. Map the event name to canonical form (e.g. Gemini's \"BeforeTool\" → \"PreToolUse\")\n * 3. Resolve the git repository from cwd/file paths\n * 4. Store the event in hook_events table (full payload as gzipped blob)\n * 5. Upsert session metadata (started_at, first_prompt, ended_at, etc.)\n * 6. For PreToolUse: check allowed.json and return permission decision\n *\n * Returns {} for most events. For PreToolUse, may return a permission\n * response that Claude Code uses to auto-approve/deny the tool call.\n */\nexport function processHookEvent(data: HookInput): Record<string, unknown> {\n const sessionId = data.session_id ?? \"unknown\";\n const rawEventType = data.hook_event_name ?? \"Unknown\";\n let eventType = rawEventType;\n const toolName = data.tool_name ?? null;\n const timestampMs = Date.now();\n\n // Resolve target and normalize event type + payload via adapter\n const target = resolveTarget(data);\n if (target) {\n const mapped = target.events.eventMap[eventType];\n if (mapped) eventType = mapped;\n if (target.events.normalizePayload) {\n data = target.events.normalizePayload(data);\n }\n }\n\n const repo = resolveEventRepo(data);\n\n const targetId = target?.id ?? \"unknown\";\n\n // Check if this event type is enabled in the logging config.\n // Permission enforcement still runs even for disabled events so that\n // PreToolUse responses are not silently dropped.\n if (!isEventEnabled(eventType)) {\n // Skip storage but still handle permission enforcement below\n if (eventType === \"PreToolUse\" && toolName) {\n return buildPermissionResponse(toolName, data, target);\n }\n return {};\n }\n\n insertHookEvent({\n session_id: sessionId,\n event_type: eventType,\n timestamp_ms: timestampMs,\n cwd: data.cwd,\n repository: repo ?? undefined,\n tool_name: toolName ?? undefined,\n target: targetId,\n payload: data,\n });\n\n // Upsert session — each event type contributes different fields.\n // SessionStart seeds the row; subsequent events enrich it. The session\n // row is the primary join target for queries across hook_events, otel,\n // and scanner tables.\n const sessionFields: Parameters<typeof upsertSession>[0] = {\n session_id: sessionId,\n target: targetId,\n has_hooks: 1,\n };\n if (eventType === \"SessionStart\") {\n // First event in a session — capture initial state. cwd and\n // permission_mode are snapshot values from launch time.\n sessionFields.started_at_ms = timestampMs;\n sessionFields.created_at = timestampMs;\n sessionFields.permission_mode =\n typeof data.permission_mode === \"string\"\n ? data.permission_mode\n : undefined;\n sessionFields.agent_version =\n typeof data.agent_version === \"string\" ? data.agent_version : undefined;\n // Derive project from cwd\n const cwd = data.cwd as string | undefined;\n if (cwd) {\n const repoInfo = resolveRepoFromCwd(cwd);\n sessionFields.project = repoInfo?.repo ?? path.basename(cwd);\n }\n }\n if (eventType === \"UserPromptSubmit\") {\n // Capture the first user prompt for session search/display. Only the\n // first prompt is stored (upsertSession uses INSERT OR IGNORE semantics\n // for first_prompt).\n const prompt =\n typeof data.prompt === \"string\"\n ? data.prompt\n : typeof data.user_prompt === \"string\"\n ? data.user_prompt\n : undefined;\n if (prompt) sessionFields.first_prompt = prompt;\n }\n if (eventType === \"Stop\" || eventType === \"SessionEnd\") {\n // Mark session end time. Stop fires on every turn completion, so\n // ended_at_ms gets updated repeatedly — the last Stop or SessionEnd\n // wins, giving us the true end time.\n sessionFields.ended_at_ms = timestampMs;\n }\n upsertSession(sessionFields);\n\n // Link subagent sessions to their parents in real-time.\n // SubagentStart fires on the PARENT session with agent_id identifying\n // the child. The subagent session ID follows the scanner convention:\n // \"agent-{agent_id}\" (matches file naming agent-*.jsonl).\n if (eventType === \"SubagentStart\" || eventType === \"SubagentStop\") {\n const agentId = data.agent_id as string | undefined;\n if (agentId) {\n const subagentSessionId = `agent-${agentId}`;\n const subagentFields: Parameters<typeof upsertSession>[0] = {\n session_id: subagentSessionId,\n target: targetId,\n parent_session_id: sessionId,\n relationship_type: \"subagent\",\n is_automated: 1,\n };\n if (eventType === \"SubagentStart\") {\n subagentFields.started_at_ms = timestampMs;\n subagentFields.created_at = timestampMs;\n } else {\n subagentFields.ended_at_ms = timestampMs;\n }\n upsertSession(subagentFields);\n }\n }\n\n // Increment event type + tool counts on the session\n incrementEventTypeCount(sessionId, eventType);\n if (eventType === \"PreToolUse\" && toolName) {\n incrementToolCount(sessionId, toolName);\n }\n\n // Populate session junction tables — greedily resolve all repos touched\n // by this event (primary cwd + any paths in tool_input).\n const allRepos = resolveAllEventRepos(data);\n for (const { repo: r, dir, branch } of allRepos) {\n const gitId = resolveGitIdentity(dir);\n upsertSessionRepository(sessionId, r, timestampMs, gitId, branch);\n\n // Capture repo config on first encounter per session\n const repoKey = `${sessionId}:${r}`;\n if (!seenSessionRepos.has(repoKey)) {\n seenSessionRepos.add(repoKey);\n try {\n const cfg = readConfig(dir);\n const gitRoot = resolveGitRoot(dir);\n const localSettingsPath = path.join(\n gitRoot ?? dir,\n \".claude\",\n \"settings.local.json\",\n );\n insertRepoConfigSnapshot({\n repository: r,\n cwd: dir,\n sessionId,\n hooks: cfg.project?.hooks ?? [],\n mcpServers: cfg.project?.mcpServers ?? [],\n commands: cfg.project?.commands ?? [],\n agents: cfg.project?.agents ?? [],\n rules: cfg.project?.rules ?? [],\n localHooks: cfg.projectLocal?.hooks ?? [],\n localMcpServers: cfg.projectLocal?.mcpServers ?? [],\n localPermissions: cfg.projectLocal?.permissions ?? {\n allow: [],\n ask: [],\n deny: [],\n },\n localIsGitignored: isGitignored(localSettingsPath, gitRoot ?? dir),\n instructions: cfg.instructions,\n });\n } catch {\n // Non-fatal — config scan failure shouldn't break hook processing\n }\n }\n }\n if (data.cwd) {\n upsertSessionCwd(sessionId, data.cwd as string, timestampMs);\n }\n\n // Capture user config on SessionStart (once per session)\n if (eventType === \"SessionStart\" && !userConfigCaptured.has(sessionId)) {\n userConfigCaptured.add(sessionId);\n try {\n const config = readConfig(data.cwd as string | undefined);\n insertUserConfigSnapshot({\n deviceName: os.hostname(),\n permissions: config.user.permissions,\n enabledPlugins: config.enabledPlugins,\n hooks: config.user.hooks,\n commands: config.user.commands,\n rules: config.user.rules,\n skills: config.user.skills,\n pluginHooks: config.pluginHooks,\n });\n } catch {\n // Non-fatal\n }\n }\n\n // Permission enforcement via allowed.json\n if (eventType === \"PreToolUse\" && toolName) {\n return buildPermissionResponse(toolName, data, target);\n }\n\n return {};\n}\n\n/**\n * Evaluate PreToolUse permission for a tool call.\n * Returns a formatted permission response if auto-allowed, otherwise {}.\n */\nfunction buildPermissionResponse(\n toolName: string,\n data: HookInput,\n target: TargetAdapter | undefined,\n): Record<string, unknown> {\n let decision: { allow: true; reason: string } | null = null;\n\n // Always auto-allow panopticon's own MCP tools\n if (isPanopticonMcpTool(toolName)) {\n decision = { allow: true, reason: \"Panopticon tool (always allowed)\" };\n } else {\n const allowed = loadAllowed();\n if (allowed) {\n if (toolName === \"Bash\") {\n const command = data.tool_input?.command;\n if (typeof command === \"string\" && allowed.bash_commands?.length) {\n decision = checkBashPermission(command, allowed.bash_commands);\n }\n } else if (allowed.tools?.includes(toolName)) {\n decision = { allow: true, reason: `Tool \"${toolName}\" is allowed` };\n }\n }\n }\n\n if (decision) {\n if (target) {\n return target.events.formatPermissionResponse(decision);\n }\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"allow\",\n permissionDecisionReason: decision.reason,\n },\n };\n }\n\n return {};\n}\n","/**\n * Per-event logging configuration.\n *\n * Separated from config.ts to avoid circular imports:\n * config.ts → targets/types.ts → hooks/ingest.ts → config.ts\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { config, ensureDataDir } from \"./config.js\";\nimport { ALL_EVENTS, type CanonicalEvent } from \"./targets/types.js\";\n\nexport type EventConfig = Record<CanonicalEvent, boolean>;\n\nconst EVENT_CONFIG_PATH = path.join(config.dataDir, \"event-config.json\");\n\n/** Cached config — null means not yet loaded. */\nlet cachedEventConfig: EventConfig | null = null;\n\nfunction defaultEventConfig(): EventConfig {\n const cfg = {} as EventConfig;\n for (const e of ALL_EVENTS) cfg[e] = true;\n return cfg;\n}\n\n/**\n * Load event logging config from disk.\n * Missing keys default to true (enabled).\n */\nexport function loadEventConfig(): EventConfig {\n if (cachedEventConfig) return cachedEventConfig;\n\n const defaults = defaultEventConfig();\n try {\n const raw = JSON.parse(fs.readFileSync(EVENT_CONFIG_PATH, \"utf-8\"));\n if (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n for (const key of Object.keys(raw)) {\n if (key in defaults && typeof raw[key] === \"boolean\") {\n defaults[key as CanonicalEvent] = raw[key];\n }\n }\n }\n } catch {\n // File doesn't exist or is invalid — use all-enabled defaults.\n }\n cachedEventConfig = defaults;\n return cachedEventConfig;\n}\n\n/** Write event config to disk and refresh cache. */\nexport function saveEventConfig(cfg: EventConfig): void {\n ensureDataDir();\n fs.writeFileSync(EVENT_CONFIG_PATH, `${JSON.stringify(cfg, null, 2)}\\n`);\n cachedEventConfig = cfg;\n}\n\n/** Check whether a specific event type should be logged. */\nexport function isEventEnabled(eventType: string): boolean {\n const cfg = loadEventConfig();\n // Unknown event types (e.g. from other targets) are always logged.\n if (!(eventType in cfg)) return true;\n return cfg[eventType as CanonicalEvent];\n}\n\n/** Force re-read from disk on next access. */\nexport function _resetEventConfigCache(): void {\n cachedEventConfig = null;\n}\n","/** Split a command string on chain operators (&&, ||, ;, |). */\nexport function splitChainComponents(cmd: string): string[] {\n return cmd\n .split(/\\s*(?:&&|\\|\\||;|\\|)\\s*/)\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\n/**\n * Extract all base commands from a single (non-chain) command string.\n * Returns multiple commands when the primary command delegates to others\n * (e.g., `find -exec rm` returns [\"find\", \"rm\"]).\n */\nexport function extractBaseCommands(component: string): string[] {\n // Strip leading env var assignments (FOO=bar cmd → cmd)\n let cmd = component.replace(/^(?:[A-Z_][A-Z0-9_]*=[^\\s]*\\s+)+/, \"\");\n // Strip trailing redirections (2>&1, >/dev/null, etc.)\n cmd = cmd.replace(/\\s*\\d*>[>&]?\\s*\\S+/g, \" \").trim();\n cmd = cmd.replace(/\\s*\\d*<\\s*\\S+/g, \" \").trim();\n\n const tokens = cmd.split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return [];\n\n // Shell re-entry: bash -c / sh -c are arbitrary execution\n if ((tokens[0] === \"bash\" || tokens[0] === \"sh\") && tokens.includes(\"-c\")) {\n return [tokens[0]];\n }\n\n // For compound CLI tools, skip flags to find the real subcommand.\n // The result is \"{tool} {subcommand}\" (e.g., \"git status\", \"xargs grep\").\n const COMPOUND_TOOLS: Record<string, Set<string>> = {\n git: new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]),\n gh: new Set([\"-R\", \"--repo\"]),\n npx: new Set([\"-p\", \"--package\"]),\n pnpm: new Set([\"--filter\", \"-C\", \"--dir\"]),\n xargs: new Set([\n \"-I\",\n \"-L\",\n \"-n\",\n \"-P\",\n \"-s\",\n \"--max-args\",\n \"--max-procs\",\n \"--replace\",\n ]),\n env: new Set([]),\n nice: new Set([\"-n\", \"--adjustment\"]),\n timeout: new Set([\"-k\", \"--kill-after\", \"-s\", \"--signal\"]),\n watch: new Set([\"-n\", \"-d\", \"--interval\"]),\n };\n const flagsWithArg = COMPOUND_TOOLS[tokens[0]];\n if (flagsWithArg && tokens.length > 1) {\n for (let i = 1; i < tokens.length; i++) {\n const t = tokens[i];\n if (t.startsWith(\"-\")) {\n if (flagsWithArg.has(t) && !t.includes(\"=\")) i++;\n continue;\n }\n // env: skip VAR=val assignments\n if (t.includes(\"=\") && tokens[0] === \"env\") continue;\n // timeout: first positional arg is the duration, skip it\n if (tokens[0] === \"timeout\" && /^\\d/.test(t)) continue;\n return [`${tokens[0]} ${t}`];\n }\n return [tokens[0]];\n }\n\n const baseCmd = tokens[0];\n const results = [baseCmd];\n\n // find -exec / -execdir: extract the delegated command\n if (baseCmd === \"find\") {\n for (let i = 1; i < tokens.length; i++) {\n if (tokens[i] === \"-exec\" || tokens[i] === \"-execdir\") {\n const delegated = tokens[i + 1];\n if (delegated && delegated !== \"{}\" && delegated !== \";\") {\n // Extract just the binary name (strip any path prefix)\n const binName = delegated.split(\"/\").pop()!;\n results.push(binName);\n }\n }\n }\n }\n\n return results;\n}\n\n/** Extract the base command from a single (non-chain) command string. */\nexport function extractBaseCommand(component: string): string {\n return extractBaseCommands(component)[0] ?? \"\";\n}\n\n/**\n * Check if a Bash command should be auto-approved.\n * Returns an allow decision only when ALL chain components match.\n * Returns null to fall through to Claude Code's normal prompting.\n */\nexport function checkBashPermission(\n command: string,\n allowedCommands: string[],\n): { allow: true; reason: string } | null {\n if (!allowedCommands.length) return null;\n\n const components = splitChainComponents(command);\n if (components.length === 0) return null;\n\n const bases = components.flatMap(extractBaseCommands);\n const unapproved = bases.filter((b) => !allowedCommands.includes(b));\n\n if (unapproved.length === 0) {\n return {\n allow: true,\n reason: `All ${bases.length} component(s) approved: ${bases.join(\", \")}`,\n };\n }\n return null;\n}\n","import type { OtelLogRow, OtelMetricRow } from \"../db/store.js\";\nimport { insertOtelLogs, insertOtelMetrics } from \"../db/store.js\";\nimport { processHookEvent } from \"../hooks/ingest.js\";\nimport { log } from \"../log.js\";\n\nexport interface HookInput {\n session_id: string;\n hook_event_name: string;\n cwd?: string;\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n prompt?: string;\n source?: string;\n target?: string;\n [key: string]: unknown;\n}\n\n/** Process a hook event in-process (no subprocess spawn). */\nexport function emitHookEvent(event: HookInput): Record<string, unknown> {\n return processHookEvent(event);\n}\n\n/** Fire and forget — log errors but don't block. */\nexport function emitHookEventAsync(event: HookInput): void {\n try {\n processHookEvent(event);\n } catch (err) {\n if (process.env.PANOPTICON_DEBUG) {\n log.proxy.error(\"hook emit error:\", err);\n }\n }\n}\n\nexport interface OtelMetricPayload {\n name: string;\n value: number;\n unit?: string;\n attributes?: Record<string, unknown>;\n sessionId?: string;\n}\n\n/** Write OTel metrics directly to DB (no HTTP round-trip). */\nexport function emitOtelMetrics(metrics: OtelMetricPayload[]): void {\n if (metrics.length === 0) return;\n\n const now = Date.now() * 1_000_000; // ms → ns\n\n try {\n const rows: OtelMetricRow[] = metrics.map((m) => ({\n timestamp_ns: now,\n name: m.name,\n value: m.value,\n metric_type: \"gauge\",\n unit: m.unit,\n attributes: m.attributes,\n resource_attributes: m.sessionId\n ? { \"session.id\": m.sessionId }\n : undefined,\n session_id: m.sessionId,\n }));\n insertOtelMetrics(rows);\n } catch (err) {\n if (process.env.PANOPTICON_DEBUG) {\n log.proxy.error(\"OTel metric emit error:\", err);\n }\n }\n}\n\n/** Write OTel log events directly to DB (no HTTP round-trip). */\nexport function emitOtelLogs(\n logs: {\n body: string;\n attributes?: Record<string, unknown>;\n sessionId?: string;\n severityText?: string;\n }[],\n): void {\n if (logs.length === 0) return;\n\n const now = Date.now() * 1_000_000;\n\n try {\n const rows: OtelLogRow[] = logs.map((l) => ({\n timestamp_ns: now,\n severity_text: l.severityText ?? \"INFO\",\n body: l.body,\n attributes: l.attributes,\n resource_attributes: l.sessionId\n ? { \"session.id\": l.sessionId }\n : undefined,\n session_id: l.sessionId,\n }));\n insertOtelLogs(rows);\n } catch (err) {\n if (process.env.PANOPTICON_DEBUG) {\n log.proxy.error(\"OTel log emit error:\", err);\n }\n }\n}\n","import type { HookInput, OtelMetricPayload } from \"../emit.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./types.js\";\n\n/** Parse Anthropic Messages API (/v1/messages) request/response. */\nexport const anthropicParser: ApiFormatParser = {\n matches(path: string): boolean {\n return path.includes(\"/v1/messages\");\n },\n\n extractEvents(capture: CapturedExchange): HookInput[] {\n const events: HookInput[] = [];\n const { request, response, sessionId } = capture;\n const reqBody = request.body as Record<string, unknown> | undefined;\n const resBody = response.body as Record<string, unknown> | undefined;\n\n if (!reqBody) return events;\n\n // Extract user prompt from the last user message in the request\n const messages = reqBody.messages as\n | Array<{ role: string; content: unknown }>\n | undefined;\n if (messages) {\n const lastUser = [...messages].reverse().find((m) => m.role === \"user\");\n if (lastUser) {\n const prompt = extractTextContent(lastUser.content);\n if (prompt) {\n events.push({\n session_id: sessionId,\n hook_event_name: \"UserPromptSubmit\",\n prompt,\n });\n }\n }\n\n // Extract tool results from request (PostToolUse for prior tool calls)\n for (const msg of messages) {\n if (msg.role === \"tool\" || msg.role === \"tool_result\") {\n const toolMsg = msg as Record<string, unknown>;\n events.push({\n session_id: sessionId,\n hook_event_name: \"PostToolUse\",\n tool_name: (toolMsg.tool_use_id as string) ?? \"unknown\",\n tool_input: {\n tool_use_id: toolMsg.tool_use_id,\n content: extractTextContent(toolMsg.content),\n },\n });\n }\n }\n }\n\n // Extract tool_use blocks from response (PreToolUse)\n if (resBody && Array.isArray(resBody.content)) {\n for (const block of resBody.content) {\n if (block.type === \"tool_use\") {\n events.push({\n session_id: sessionId,\n hook_event_name: \"PreToolUse\",\n tool_name: block.name ?? \"unknown\",\n tool_input: block.input ?? {},\n });\n }\n }\n }\n\n return events;\n },\n\n extractMetrics(capture: CapturedExchange): OtelMetricPayload[] {\n const metrics: OtelMetricPayload[] = [];\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n if (!resBody) return metrics;\n\n const usage = resBody.usage as Record<string, number> | undefined;\n const model = (resBody.model as string) ?? \"unknown\";\n\n if (usage) {\n if (usage.input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.input_tokens,\n attributes: {\n model,\n token_type: \"input\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.output_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.output_tokens,\n attributes: {\n model,\n token_type: \"output\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.cache_read_input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.cache_read_input_tokens,\n attributes: {\n model,\n token_type: \"cacheRead\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.cache_creation_input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.cache_creation_input_tokens,\n attributes: {\n model,\n token_type: \"cacheWrite\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n }\n\n return metrics;\n },\n\n extractLogs(capture: CapturedExchange) {\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n const model =\n (resBody?.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n const usage = resBody?.usage as Record<string, number> | undefined;\n\n return [\n {\n body: \"api_request\",\n sessionId: capture.sessionId,\n attributes: {\n model,\n target: capture.target,\n duration_ms: capture.duration_ms,\n status: capture.response.status,\n stop_reason: resBody?.stop_reason,\n input_tokens: usage?.input_tokens,\n output_tokens: usage?.output_tokens,\n },\n },\n ];\n },\n};\n\nfunction extractTextContent(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return (\n content\n .filter((c: Record<string, unknown>) => c.type === \"text\")\n .map((c: Record<string, unknown>) => c.text)\n .join(\"\\n\") || undefined\n );\n }\n return undefined;\n}\n","import type { HookInput, OtelMetricPayload } from \"../emit.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./types.js\";\n\n/** Parse OpenAI Chat Completions API (/v1/chat/completions) request/response. */\nexport const openaiParser: ApiFormatParser = {\n matches(path: string): boolean {\n return path.includes(\"/v1/chat/completions\");\n },\n\n extractEvents(capture: CapturedExchange): HookInput[] {\n const events: HookInput[] = [];\n const { request, response, sessionId } = capture;\n const reqBody = request.body as Record<string, unknown> | undefined;\n const resBody = response.body as Record<string, unknown> | undefined;\n\n if (!reqBody) return events;\n\n // Extract user prompt from the last user message\n const messages = reqBody.messages as\n | Array<{ role: string; content: unknown; tool_call_id?: string }>\n | undefined;\n if (messages) {\n const lastUser = [...messages].reverse().find((m) => m.role === \"user\");\n if (lastUser) {\n const prompt = extractTextContent(lastUser.content);\n if (prompt) {\n events.push({\n session_id: sessionId,\n hook_event_name: \"UserPromptSubmit\",\n prompt,\n });\n }\n }\n\n // Extract tool results from request (PostToolUse for prior tool calls)\n for (const msg of messages) {\n if (msg.role === \"tool\") {\n events.push({\n session_id: sessionId,\n hook_event_name: \"PostToolUse\",\n tool_name: (msg.tool_call_id as string) ?? \"unknown\",\n tool_input: {\n tool_call_id: msg.tool_call_id,\n content: extractTextContent(msg.content),\n },\n });\n }\n }\n }\n\n // Extract tool_calls from response (PreToolUse)\n if (resBody) {\n const choices = resBody.choices as\n | Array<{ message?: { tool_calls?: Array<Record<string, unknown>> } }>\n | undefined;\n if (choices) {\n for (const choice of choices) {\n const toolCalls = choice.message?.tool_calls;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const fn = tc.function as\n | { name?: string; arguments?: string }\n | undefined;\n let parsedArgs: Record<string, unknown> = {};\n if (fn?.arguments) {\n try {\n parsedArgs = JSON.parse(fn.arguments);\n } catch {\n parsedArgs = { raw: fn.arguments };\n }\n }\n events.push({\n session_id: sessionId,\n hook_event_name: \"PreToolUse\",\n tool_name: fn?.name ?? \"unknown\",\n tool_input: parsedArgs,\n });\n }\n }\n }\n }\n }\n\n return events;\n },\n\n extractMetrics(capture: CapturedExchange): OtelMetricPayload[] {\n const metrics: OtelMetricPayload[] = [];\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n if (!resBody) return metrics;\n\n const usage = resBody.usage as Record<string, number> | undefined;\n const model =\n (resBody.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n\n if (usage) {\n if (usage.prompt_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.prompt_tokens,\n attributes: {\n model,\n token_type: \"input\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.completion_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.completion_tokens,\n attributes: {\n model,\n token_type: \"output\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n }\n\n return metrics;\n },\n\n extractLogs(capture: CapturedExchange) {\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n const model =\n (resBody?.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n const usage = resBody?.usage as Record<string, number> | undefined;\n const choices = resBody?.choices as\n | Array<{ finish_reason?: string }>\n | undefined;\n\n return [\n {\n body: \"api_request\",\n sessionId: capture.sessionId,\n attributes: {\n model,\n target: capture.target,\n duration_ms: capture.duration_ms,\n status: capture.response.status,\n stop_reason: choices?.[0]?.finish_reason,\n input_tokens: usage?.prompt_tokens,\n output_tokens: usage?.completion_tokens,\n },\n },\n ];\n },\n};\n\nfunction extractTextContent(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return (\n content\n .filter((c: Record<string, unknown>) => c.type === \"text\")\n .map((c: Record<string, unknown>) => c.text)\n .join(\"\\n\") || undefined\n );\n }\n return undefined;\n}\n","import type { HookInput, OtelMetricPayload } from \"../emit.js\";\nimport type { ApiFormatParser, CapturedExchange } from \"./types.js\";\n\n/** Parse OpenAI Responses API (/v1/responses, /backend-api/codex/responses). */\nexport const openaiResponsesParser: ApiFormatParser = {\n matches(path: string): boolean {\n return path.endsWith(\"/responses\") || path.includes(\"/responses?\");\n },\n\n extractEvents(capture: CapturedExchange): HookInput[] {\n const events: HookInput[] = [];\n const { request, response, sessionId } = capture;\n const reqBody = request.body as Record<string, unknown> | undefined;\n const resBody = response.body as Record<string, unknown> | undefined;\n\n if (!reqBody) return events;\n\n // Extract user prompt from input field\n const input = reqBody.input;\n const prompt = extractInputText(input);\n if (prompt) {\n events.push({\n session_id: sessionId,\n hook_event_name: \"UserPromptSubmit\",\n prompt,\n });\n }\n\n // Extract tool results from input (function_call_output items)\n if (Array.isArray(input)) {\n for (const item of input) {\n const it = item as Record<string, unknown>;\n if (it.type === \"function_call_output\") {\n events.push({\n session_id: sessionId,\n hook_event_name: \"PostToolUse\",\n tool_name: (it.call_id as string) ?? \"unknown\",\n tool_input: {\n call_id: it.call_id,\n content: it.output,\n },\n });\n }\n }\n }\n\n // Extract tool calls from response output (function_call items)\n if (resBody) {\n const output = resBody.output as\n | Array<Record<string, unknown>>\n | undefined;\n if (output) {\n for (const item of output) {\n if (item.type === \"function_call\") {\n let parsedArgs: Record<string, unknown> = {};\n if (typeof item.arguments === \"string\") {\n try {\n parsedArgs = JSON.parse(item.arguments);\n } catch {\n parsedArgs = { raw: item.arguments };\n }\n }\n events.push({\n session_id: sessionId,\n hook_event_name: \"PreToolUse\",\n tool_name: (item.name as string) ?? \"unknown\",\n tool_input: parsedArgs,\n });\n }\n }\n }\n }\n\n return events;\n },\n\n extractMetrics(capture: CapturedExchange): OtelMetricPayload[] {\n const metrics: OtelMetricPayload[] = [];\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n if (!resBody) return metrics;\n\n const usage = resBody.usage as Record<string, number> | undefined;\n const model =\n (resBody.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n\n if (usage) {\n if (usage.input_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.input_tokens,\n attributes: {\n model,\n token_type: \"input\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n if (usage.output_tokens) {\n metrics.push({\n name: \"token.usage\",\n value: usage.output_tokens,\n attributes: {\n model,\n token_type: \"output\",\n target: capture.target,\n },\n sessionId: capture.sessionId,\n });\n }\n }\n\n return metrics;\n },\n\n extractLogs(capture: CapturedExchange) {\n const resBody = capture.response.body as\n | Record<string, unknown>\n | undefined;\n const reqBody = capture.request.body as Record<string, unknown> | undefined;\n const model =\n (resBody?.model as string) ?? (reqBody?.model as string) ?? \"unknown\";\n const usage = resBody?.usage as Record<string, number> | undefined;\n\n return [\n {\n body: \"api_request\",\n sessionId: capture.sessionId,\n attributes: {\n model,\n target: capture.target,\n duration_ms: capture.duration_ms,\n status: capture.response.status,\n stop_reason: resBody?.status,\n input_tokens: usage?.input_tokens,\n output_tokens: usage?.output_tokens,\n },\n },\n ];\n },\n};\n\n/** Extract text from the Responses API input field. */\nfunction extractInputText(input: unknown): string | undefined {\n // String input: \"hello\"\n if (typeof input === \"string\") return input;\n\n if (!Array.isArray(input)) return undefined;\n\n // Array of messages or items — find the last user content\n const texts: string[] = [];\n for (let i = input.length - 1; i >= 0; i--) {\n const item = input[i] as Record<string, unknown>;\n\n // Message format: { role: \"user\", content: \"hello\" }\n if (item.role === \"user\") {\n const text = extractContentField(item.content);\n if (text) return text;\n }\n\n // Item format: { type: \"message\", role: \"user\", content: [...] }\n if (item.type === \"message\" && item.role === \"user\") {\n const text = extractContentField(item.content);\n if (text) return text;\n }\n\n // Inline text item: { type: \"input_text\", text: \"hello\" }\n if (item.type === \"input_text\" && typeof item.text === \"string\") {\n texts.unshift(item.text);\n }\n }\n\n return texts.length > 0 ? texts.join(\"\\n\") : undefined;\n}\n\nfunction extractContentField(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return (\n content\n .filter(\n (c: Record<string, unknown>) =>\n c.type === \"input_text\" || c.type === \"text\",\n )\n .map((c: Record<string, unknown>) => c.text)\n .join(\"\\n\") || undefined\n );\n }\n return undefined;\n}\n","import { config } from \"../config.js\";\n\ninterface SessionState {\n id: string;\n lastRequestMs: number;\n lastMessageCount: number;\n seq: number;\n}\n\nexport class SessionTracker {\n private sessions = new Map<string, SessionState>();\n\n getOrCreateSession(\n target: string,\n requestBody?: unknown,\n ): { sessionId: string; isNew: boolean } {\n const now = Date.now();\n const existing = this.sessions.get(target);\n const messageCount = countMessages(requestBody);\n\n // Detect new session from conversation context\n if (existing) {\n const isReset = this.isConversationReset(existing, messageCount, now);\n if (!isReset) {\n existing.lastRequestMs = now;\n if (messageCount > 0) existing.lastMessageCount = messageCount;\n return { sessionId: existing.id, isNew: false };\n }\n }\n\n // New session\n const seq = existing ? existing.seq + 1 : 1;\n const date = new Date(now).toISOString().slice(0, 10).replace(/-/g, \"\");\n const sessionId = `${target}-${date}-${String(seq).padStart(3, \"0\")}`;\n\n this.sessions.set(target, {\n id: sessionId,\n lastRequestMs: now,\n lastMessageCount: messageCount,\n seq,\n });\n return { sessionId, isNew: true };\n }\n\n private isConversationReset(\n state: SessionState,\n messageCount: number,\n now: number,\n ): boolean {\n // Context-based detection: if message count drops significantly,\n // the conversation was cleared/reset. A request with <= 2 messages\n // (system + user) after a longer conversation is a strong reset signal.\n if (messageCount > 0 && state.lastMessageCount > 3 && messageCount <= 2) {\n return true;\n }\n\n // Significant drop in message count (more than halved and dropped by 3+)\n if (\n messageCount > 0 &&\n state.lastMessageCount > 0 &&\n messageCount < state.lastMessageCount / 2 &&\n state.lastMessageCount - messageCount >= 3\n ) {\n return true;\n }\n\n // Fallback: idle timeout\n if (now - state.lastRequestMs >= config.proxyIdleSessionMs) {\n return true;\n }\n\n return false;\n }\n}\n\n/** Count non-system messages in a chat request body. */\nfunction countMessages(body: unknown): number {\n if (typeof body !== \"object\" || body === null) return 0;\n const messages = (body as Record<string, unknown>).messages;\n if (!Array.isArray(messages)) return 0;\n\n // Count only non-system messages — system messages don't indicate\n // conversation depth and are present in every request\n return messages.filter((m: Record<string, unknown>) => m.role !== \"system\")\n .length;\n}\n","/**\n * Accumulate SSE streaming responses into a final reconstructed message.\n * Forwards chunks to the client in real-time while buffering for capture.\n */\n\nexport interface StreamAccumulator {\n /** Feed a raw SSE chunk. Returns the chunk unchanged (for forwarding). */\n push(chunk: Buffer): Buffer;\n /** Finalize and return the reconstructed response body. */\n finish(): Record<string, unknown>;\n}\n\n/** Create an accumulator for Anthropic streaming format. */\nexport function createAnthropicAccumulator(): StreamAccumulator {\n let message: Record<string, unknown> = {};\n let usage: Record<string, number> = {};\n const contentBlocks: Array<Record<string, unknown>> = [];\n let currentBlockIndex = -1;\n const textParts = new Map<number, string>();\n\n return {\n push(chunk: Buffer): Buffer {\n const text = chunk.toString(\"utf-8\");\n for (const line of text.split(\"\\n\")) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") continue;\n\n try {\n const event = JSON.parse(data);\n switch (event.type) {\n case \"message_start\":\n message = event.message ?? {};\n usage = (event.message?.usage as Record<string, number>) ?? {};\n break;\n case \"content_block_start\":\n currentBlockIndex = event.index ?? contentBlocks.length;\n contentBlocks[currentBlockIndex] = event.content_block ?? {};\n break;\n case \"content_block_delta\":\n if (event.delta?.type === \"text_delta\" && event.delta.text) {\n const idx = event.index ?? currentBlockIndex;\n textParts.set(\n idx,\n (textParts.get(idx) ?? \"\") + event.delta.text,\n );\n } else if (\n event.delta?.type === \"input_json_delta\" &&\n event.delta.partial_json\n ) {\n const idx = event.index ?? currentBlockIndex;\n textParts.set(\n idx,\n (textParts.get(idx) ?? \"\") + event.delta.partial_json,\n );\n }\n break;\n case \"message_delta\":\n if (event.delta) {\n Object.assign(message, event.delta);\n }\n if (event.usage) {\n Object.assign(usage, event.usage);\n }\n break;\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n return chunk;\n },\n\n finish(): Record<string, unknown> {\n // Reconstruct content blocks with accumulated text\n const content = contentBlocks.map((block, i) => {\n if (block.type === \"text\") {\n return { ...block, text: textParts.get(i) ?? block.text ?? \"\" };\n }\n if (block.type === \"tool_use\") {\n const raw = textParts.get(i);\n let input = block.input;\n if (raw) {\n try {\n input = JSON.parse(raw);\n } catch {\n input = { raw };\n }\n }\n return { ...block, input };\n }\n return block;\n });\n\n return {\n ...message,\n content,\n usage,\n };\n },\n };\n}\n\n/** Create an accumulator for OpenAI streaming format. */\nexport function createOpenaiAccumulator(): StreamAccumulator {\n let model = \"\";\n let finishReason: string | null = null;\n let role = \"\";\n let contentParts = \"\";\n const toolCalls = new Map<\n number,\n { id: string; type: string; function: { name: string; arguments: string } }\n >();\n let usage: Record<string, number> = {};\n\n return {\n push(chunk: Buffer): Buffer {\n const text = chunk.toString(\"utf-8\");\n for (const line of text.split(\"\\n\")) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") continue;\n\n try {\n const event = JSON.parse(data);\n if (event.model) model = event.model;\n if (event.usage) usage = event.usage;\n\n const choice = event.choices?.[0];\n if (!choice) continue;\n\n if (choice.finish_reason) finishReason = choice.finish_reason;\n\n const delta = choice.delta;\n if (!delta) continue;\n\n if (delta.role) role = delta.role;\n if (delta.content) contentParts += delta.content;\n\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n const existing = toolCalls.get(idx);\n if (existing) {\n if (tc.function?.arguments) {\n existing.function.arguments += tc.function.arguments;\n }\n } else {\n toolCalls.set(idx, {\n id: tc.id ?? \"\",\n type: tc.type ?? \"function\",\n function: {\n name: tc.function?.name ?? \"\",\n arguments: tc.function?.arguments ?? \"\",\n },\n });\n }\n }\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n return chunk;\n },\n\n finish(): Record<string, unknown> {\n const message: Record<string, unknown> = {\n role: role || \"assistant\",\n content: contentParts || null,\n };\n\n if (toolCalls.size > 0) {\n message.tool_calls = [...toolCalls.entries()]\n .sort(([a], [b]) => a - b)\n .map(([, tc]) => tc);\n }\n\n return {\n model,\n choices: [\n {\n index: 0,\n message,\n finish_reason: finishReason,\n },\n ],\n usage,\n };\n },\n };\n}\n\n/** Detect if a request is asking for streaming. */\nexport function isStreamingRequest(body: unknown): boolean {\n if (typeof body === \"object\" && body !== null) {\n return (body as Record<string, unknown>).stream === true;\n }\n return false;\n}\n","/**\n * WebSocket frame parser for capturing messages flowing through the tunnel.\n *\n * Extracts complete text messages from raw TCP data without modifying the\n * stream — the caller forwards the original bytes to the other socket and\n * feeds a copy here for inspection.\n */\n\nexport class WebSocketMessageExtractor {\n private buffer = Buffer.alloc(0);\n private fragments: Buffer[] = [];\n private currentOpcode = 0;\n\n onMessage?: (message: string) => void;\n\n push(data: Buffer): void {\n this.buffer = Buffer.concat([this.buffer, data]);\n this.drain();\n }\n\n private drain(): void {\n while (this.buffer.length >= 2) {\n const byte0 = this.buffer[0];\n const byte1 = this.buffer[1];\n const fin = (byte0 & 0x80) !== 0;\n const opcode = byte0 & 0x0f;\n const masked = (byte1 & 0x80) !== 0;\n let payloadLen = byte1 & 0x7f;\n\n let headerLen = 2;\n if (payloadLen === 126) {\n if (this.buffer.length < 4) return;\n payloadLen = this.buffer.readUInt16BE(2);\n headerLen = 4;\n } else if (payloadLen === 127) {\n if (this.buffer.length < 10) return;\n payloadLen = Number(this.buffer.readBigUInt64BE(2));\n headerLen = 10;\n }\n\n if (masked) headerLen += 4;\n\n const totalLen = headerLen + payloadLen;\n if (this.buffer.length < totalLen) return;\n\n // Extract and optionally unmask payload\n let payload = this.buffer.subarray(headerLen, totalLen);\n if (masked) {\n const maskKey = this.buffer.subarray(headerLen - 4, headerLen);\n payload = Buffer.from(payload); // copy before mutating\n for (let i = 0; i < payload.length; i++) {\n payload[i] ^= maskKey[i % 4];\n }\n }\n\n // Handle text/binary frames and continuations\n if (opcode === 0x1 || opcode === 0x2) {\n this.currentOpcode = opcode;\n this.fragments = [payload];\n } else if (opcode === 0x0) {\n this.fragments.push(payload);\n }\n\n if (fin && this.fragments.length > 0 && opcode <= 0x2) {\n if (this.currentOpcode === 0x1) {\n try {\n const msg = Buffer.concat(this.fragments).toString(\"utf-8\");\n this.onMessage?.(msg);\n } catch {\n // malformed utf-8, skip\n }\n }\n this.fragments = [];\n }\n\n this.buffer = this.buffer.subarray(totalLen);\n }\n }\n}\n"],"mappings":"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","names":["fs","path","path","fs","path","config","path","path","extractTextContent","path","upstream","log"]}
@@ -3,7 +3,7 @@ import {
3
3
  upsertSession,
4
4
  upsertSessionCwd,
5
5
  upsertSessionRepository
6
- } from "./chunk-BVOE7A2Z.js";
6
+ } from "./chunk-V3XR2TAN.js";
7
7
  import {
8
8
  resolveRepoFromCwd
9
9
  } from "./chunk-YVRWVDIA.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-7Q3BJMLG.js";
13
13
  import {
14
14
  refreshIfStale
15
- } from "./chunk-3TZAKV3M.js";
15
+ } from "./chunk-FMAHQRIU.js";
16
16
  import {
17
17
  allTargets
18
18
  } from "./chunk-QVK6VGCV.js";
@@ -22,8 +22,9 @@ import {
22
22
  closeDb,
23
23
  getDb,
24
24
  markResyncComplete,
25
- needsResync
26
- } from "./chunk-DZ5HJFB4.js";
25
+ needsResync,
26
+ runMigrations
27
+ } from "./chunk-SKZHAYNF.js";
27
28
  import {
28
29
  config
29
30
  } from "./chunk-K7YUPLES.js";
@@ -798,7 +799,7 @@ function createScannerLoop(opts) {
798
799
  reparseChecked = true;
799
800
  if (needsResync()) {
800
801
  log.scanner.info("Data version outdated \u2014 running atomic reparse...");
801
- import("./reparse-636YZCE3.js").then(({ reparseAll: reparseAll2 }) => {
802
+ import("./reparse-VHUSGCPN.js").then(({ reparseAll: reparseAll2 }) => {
802
803
  try {
803
804
  const result = reparseAll2((msg) => log.scanner.info(msg));
804
805
  if (result.success) {
@@ -918,6 +919,7 @@ function initTempDb(tempPath) {
918
919
  (blob) => blob ? gunzipSync2(blob).toString() : null
919
920
  );
920
921
  db.exec(SCHEMA_SQL);
922
+ runMigrations(db);
921
923
  return db;
922
924
  }
923
925
  function reparseAll(log2 = () => {
@@ -1117,4 +1119,4 @@ export {
1117
1119
  reparseAll,
1118
1120
  createScannerLoop
1119
1121
  };
1120
- //# sourceMappingURL=chunk-NXH7AONS.js.map
1122
+ //# sourceMappingURL=chunk-OROLSIWZ.js.map