@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
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-QVK6VGCV.js";
4
4
  import {
5
5
  getDb
6
- } from "./chunk-DZ5HJFB4.js";
6
+ } from "./chunk-SKZHAYNF.js";
7
7
 
8
8
  // src/db/query.ts
9
9
  function parseSince(since) {
@@ -623,4 +623,4 @@ export {
623
623
  rawQuery,
624
624
  dbStats
625
625
  };
626
- //# sourceMappingURL=chunk-LWXF7YRG.js.map
626
+ //# sourceMappingURL=chunk-GPTBERQD.js.map
@@ -166,4 +166,4 @@ export {
166
166
  syncTargetAdd,
167
167
  syncTargetRemove
168
168
  };
169
- //# sourceMappingURL=chunk-4SM2H22C.js.map
169
+ //# sourceMappingURL=chunk-HO443ZQM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/client.ts"],"sourcesContent":["/**\n * HTTP client for the panopticon server API.\n *\n * Provides typed wrappers around POST /api/tool and POST /api/exec\n * so that CLI and MCP can query the server instead of opening the\n * database directly.\n */\nimport http from \"node:http\";\nimport { config } from \"../config.js\";\nimport type {\n ActivitySummaryResult,\n SearchResult,\n SessionListResult,\n SessionTimelineResult,\n SpendingResult,\n} from \"../types.js\";\n\n// ── Core transport ───────────────────────────────────────────────────────────\n\nfunction post(\n path: string,\n body: unknown,\n timeoutMs: number,\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const json = JSON.stringify(body);\n const req = http.request(\n {\n hostname: config.host,\n port: config.port,\n path,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(json),\n },\n timeout: timeoutMs,\n },\n (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"end\", () => {\n const text = Buffer.concat(chunks).toString(\"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n reject(\n new Error(\n `Invalid JSON response from server: ${text.slice(0, 200)}`,\n ),\n );\n return;\n }\n if (\n res.statusCode &&\n res.statusCode >= 400 &&\n typeof parsed === \"object\" &&\n parsed !== null &&\n \"error\" in parsed\n ) {\n reject(new Error((parsed as { error: string }).error));\n return;\n }\n resolve(parsed);\n });\n },\n );\n req.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"ECONNREFUSED\") {\n reject(\n new Error(\n \"Panopticon server is not running. Start with: panopticon start\",\n ),\n );\n return;\n }\n reject(err);\n });\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(\"Request to panopticon server timed out\"));\n });\n req.write(json);\n req.end();\n });\n}\n\n// ── Tool calls (read-only) ──────────────────────────────────────────────────\n\nconst TOOL_TIMEOUT = 30_000;\n\nexport function callTool(\n name: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n return post(\"/api/tool\", { name, params }, TOOL_TIMEOUT);\n}\n\nexport function listSessions(opts?: {\n limit?: number;\n since?: string;\n}): Promise<SessionListResult> {\n return callTool(\n \"sessions\",\n opts as Record<string, unknown>,\n ) as Promise<SessionListResult>;\n}\n\nexport function sessionTimeline(opts: {\n sessionId: string;\n limit?: number;\n offset?: number;\n fullPayloads?: boolean;\n}): Promise<SessionTimelineResult> {\n return callTool(\n \"timeline\",\n opts as Record<string, unknown>,\n ) as Promise<SessionTimelineResult>;\n}\n\nexport function costBreakdown(opts?: {\n since?: string;\n groupBy?: \"session\" | \"model\" | \"day\";\n}): Promise<SpendingResult> {\n return callTool(\n \"costs\",\n opts as Record<string, unknown>,\n ) as Promise<SpendingResult>;\n}\n\nexport function activitySummary(opts?: {\n since?: string;\n}): Promise<ActivitySummaryResult> {\n return callTool(\n \"summary\",\n opts as Record<string, unknown>,\n ) as Promise<ActivitySummaryResult>;\n}\n\nexport function listPlans(opts?: {\n session_id?: string;\n since?: string;\n limit?: number;\n}): Promise<unknown> {\n return callTool(\"plans\", opts as Record<string, unknown>);\n}\n\nexport function search(opts: {\n query: string;\n eventTypes?: string[];\n since?: string;\n limit?: number;\n offset?: number;\n fullPayloads?: boolean;\n}): Promise<SearchResult> {\n return callTool(\n \"search\",\n opts as Record<string, unknown>,\n ) as Promise<SearchResult>;\n}\n\nexport function print(opts: {\n source: \"hook\" | \"otel\" | \"message\";\n id: number;\n}): Promise<unknown> {\n return callTool(\"get\", opts as Record<string, unknown>);\n}\n\nexport function rawQuery(sql: string): Promise<unknown> {\n return callTool(\"query\", { sql });\n}\n\nexport function dbStats(): Promise<unknown> {\n return callTool(\"status\");\n}\n\n// ── Exec calls (write operations, CLI only) ─────────────────────────────────\n\nconst EXEC_TIMEOUT = 60_000;\n\nexport function callExec(\n command: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n return post(\"/api/exec\", { command, params }, EXEC_TIMEOUT);\n}\n\nexport function pruneEstimate(cutoffMs: number): Promise<unknown> {\n return callExec(\"prune\", { cutoffMs, dryRun: true });\n}\n\nexport function pruneExecute(\n cutoffMs: number,\n opts?: { vacuum?: boolean },\n): Promise<unknown> {\n return callExec(\"prune\", { cutoffMs, ...opts });\n}\n\nexport function refreshPricing(): Promise<unknown> {\n return callExec(\"refresh-pricing\");\n}\n\nexport function syncReset(target?: string): Promise<unknown> {\n return callExec(\"sync-reset\", target ? { target } : {});\n}\n\nexport function syncWatermarkGet(\n target: string,\n table?: string,\n): Promise<unknown> {\n return callExec(\"sync-watermark-get\", { target, table });\n}\n\nexport function syncWatermarkSet(\n target: string,\n table: string,\n value: number,\n): Promise<unknown> {\n return callExec(\"sync-watermark-set\", { target, table, value });\n}\n\nexport function syncPending(target: string): Promise<{\n target: string;\n totalPending: number;\n tables: Record<string, { maxId: number; watermark: number; pending: number }>;\n}> {\n return callExec(\"sync-pending\", { target }) as Promise<{\n target: string;\n totalPending: number;\n tables: Record<\n string,\n { maxId: number; watermark: number; pending: number }\n >;\n }>;\n}\n\nexport function syncTargetList(): Promise<unknown> {\n return callExec(\"sync-target-list\");\n}\n\nexport function syncTargetAdd(target: {\n name: string;\n url: string;\n token?: string;\n tokenCommand?: string;\n}): Promise<unknown> {\n return callExec(\"sync-target-add\", target as Record<string, unknown>);\n}\n\nexport function syncTargetRemove(name: string): Promise<unknown> {\n return callExec(\"sync-target-remove\", { name });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/api/client.ts"],"sourcesContent":["/**\n * HTTP client for the panopticon server API.\n *\n * Provides typed wrappers around POST /api/tool and POST /api/exec\n * so that CLI and MCP can query the server instead of opening the\n * database directly.\n */\nimport http from \"node:http\";\nimport { config } from \"../config.js\";\nimport type {\n ActivitySummaryResult,\n SearchResult,\n SessionListResult,\n SessionTimelineResult,\n SpendingResult,\n} from \"../types.js\";\n\n// ── Core transport ───────────────────────────────────────────────────────────\n\nfunction post(\n path: string,\n body: unknown,\n timeoutMs: number,\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const json = JSON.stringify(body);\n const req = http.request(\n {\n hostname: config.host,\n port: config.port,\n path,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(json),\n },\n timeout: timeoutMs,\n },\n (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"end\", () => {\n const text = Buffer.concat(chunks).toString(\"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n reject(\n new Error(\n `Invalid JSON response from server: ${text.slice(0, 200)}`,\n ),\n );\n return;\n }\n if (\n res.statusCode &&\n res.statusCode >= 400 &&\n typeof parsed === \"object\" &&\n parsed !== null &&\n \"error\" in parsed\n ) {\n reject(new Error((parsed as { error: string }).error));\n return;\n }\n resolve(parsed);\n });\n },\n );\n req.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"ECONNREFUSED\") {\n reject(\n new Error(\n \"Panopticon server is not running. Start with: panopticon start\",\n ),\n );\n return;\n }\n reject(err);\n });\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(\"Request to panopticon server timed out\"));\n });\n req.write(json);\n req.end();\n });\n}\n\n// ── Tool calls (read-only) ──────────────────────────────────────────────────\n\nconst TOOL_TIMEOUT = 30_000;\n\nexport function callTool(\n name: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n return post(\"/api/tool\", { name, params }, TOOL_TIMEOUT);\n}\n\nexport function listSessions(opts?: {\n limit?: number;\n since?: string;\n}): Promise<SessionListResult> {\n return callTool(\n \"sessions\",\n opts as Record<string, unknown>,\n ) as Promise<SessionListResult>;\n}\n\nexport function sessionTimeline(opts: {\n sessionId: string;\n limit?: number;\n offset?: number;\n fullPayloads?: boolean;\n}): Promise<SessionTimelineResult> {\n return callTool(\n \"timeline\",\n opts as Record<string, unknown>,\n ) as Promise<SessionTimelineResult>;\n}\n\nexport function costBreakdown(opts?: {\n since?: string;\n groupBy?: \"session\" | \"model\" | \"day\";\n}): Promise<SpendingResult> {\n return callTool(\n \"costs\",\n opts as Record<string, unknown>,\n ) as Promise<SpendingResult>;\n}\n\nexport function activitySummary(opts?: {\n since?: string;\n}): Promise<ActivitySummaryResult> {\n return callTool(\n \"summary\",\n opts as Record<string, unknown>,\n ) as Promise<ActivitySummaryResult>;\n}\n\nexport function listPlans(opts?: {\n session_id?: string;\n since?: string;\n limit?: number;\n}): Promise<unknown> {\n return callTool(\"plans\", opts as Record<string, unknown>);\n}\n\nexport function search(opts: {\n query: string;\n eventTypes?: string[];\n since?: string;\n limit?: number;\n offset?: number;\n fullPayloads?: boolean;\n}): Promise<SearchResult> {\n return callTool(\n \"search\",\n opts as Record<string, unknown>,\n ) as Promise<SearchResult>;\n}\n\nexport function print(opts: {\n source: \"hook\" | \"otel\" | \"message\";\n id: number;\n}): Promise<unknown> {\n return callTool(\"get\", opts as Record<string, unknown>);\n}\n\nexport function rawQuery(sql: string): Promise<unknown> {\n return callTool(\"query\", { sql });\n}\n\nexport function dbStats(): Promise<unknown> {\n return callTool(\"status\");\n}\n\n// ── Exec calls (write operations, CLI only) ─────────────────────────────────\n\nconst EXEC_TIMEOUT = 60_000;\n\nexport function callExec(\n command: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n return post(\"/api/exec\", { command, params }, EXEC_TIMEOUT);\n}\n\nexport function pruneEstimate(cutoffMs: number): Promise<unknown> {\n return callExec(\"prune\", { cutoffMs, dryRun: true });\n}\n\nexport function pruneExecute(\n cutoffMs: number,\n opts?: { vacuum?: boolean },\n): Promise<unknown> {\n return callExec(\"prune\", { cutoffMs, ...opts });\n}\n\nexport function refreshPricing(): Promise<unknown> {\n return callExec(\"refresh-pricing\");\n}\n\nexport function syncReset(target?: string): Promise<unknown> {\n return callExec(\"sync-reset\", target ? { target } : {});\n}\n\nexport function syncWatermarkGet(\n target: string,\n table?: string,\n): Promise<unknown> {\n return callExec(\"sync-watermark-get\", { target, table });\n}\n\nexport function syncWatermarkSet(\n target: string,\n table: string,\n value: number,\n): Promise<unknown> {\n return callExec(\"sync-watermark-set\", { target, table, value });\n}\n\nexport function syncPending(target: string): Promise<{\n target: string;\n totalPending: number;\n tables: Record<string, { total: number; synced: number; pending: number }>;\n}> {\n return callExec(\"sync-pending\", { target }) as Promise<{\n target: string;\n totalPending: number;\n tables: Record<string, { total: number; synced: number; pending: number }>;\n }>;\n}\n\nexport function syncTargetList(): Promise<unknown> {\n return callExec(\"sync-target-list\");\n}\n\nexport function syncTargetAdd(target: {\n name: string;\n url: string;\n token?: string;\n tokenCommand?: string;\n}): Promise<unknown> {\n return callExec(\"sync-target-add\", target as Record<string, unknown>);\n}\n\nexport function syncTargetRemove(name: string): Promise<unknown> {\n return callExec(\"sync-target-remove\", { name });\n}\n"],"mappings":"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","names":[]}
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  refreshPricing
3
- } from "./chunk-3TZAKV3M.js";
3
+ } from "./chunk-FMAHQRIU.js";
4
4
  import {
5
5
  allTargets
6
6
  } from "./chunk-QVK6VGCV.js";
7
7
  import {
8
8
  closeDb,
9
9
  getDb
10
- } from "./chunk-DZ5HJFB4.js";
10
+ } from "./chunk-SKZHAYNF.js";
11
11
  import {
12
12
  config,
13
13
  ensureDataDir
@@ -127,4 +127,4 @@ export {
127
127
  fetchPricing,
128
128
  configureShellEnv
129
129
  };
130
- //# sourceMappingURL=chunk-L7G27XWF.js.map
130
+ //# sourceMappingURL=chunk-HRNZUHTA.js.map
@@ -19,7 +19,7 @@ import {
19
19
  } from "@sentry/core";
20
20
  var initialized = false;
21
21
  function getVersion() {
22
- return true ? "0.1.0+2aee981" : "dev";
22
+ return true ? "0.1.2+430c018" : "dev";
23
23
  }
24
24
  var SCRUBBED_BREADCRUMB_FIELDS = [
25
25
  "prompt",
@@ -167,4 +167,4 @@ export {
167
167
  setTag,
168
168
  flushSentry
169
169
  };
170
- //# sourceMappingURL=chunk-CF4GPWLI.js.map
170
+ //# sourceMappingURL=chunk-J3HVD4VI.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getDb
3
- } from "./chunk-DZ5HJFB4.js";
3
+ } from "./chunk-SKZHAYNF.js";
4
4
 
5
5
  // src/sync/reader.ts
6
6
  function parseJson(raw) {
@@ -239,7 +239,7 @@ function parseJsonObject(raw) {
239
239
  }
240
240
  var USER_CONFIG_SQL = `
241
241
  SELECT id, device_name, snapshot_at_ms, content_hash,
242
- permissions, enabled_plugins, hooks, commands, rules, skills
242
+ permissions, enabled_plugins, hooks, commands, rules, skills, plugin_hooks
243
243
  FROM user_config_snapshots
244
244
  WHERE id > ?
245
245
  ORDER BY id
@@ -258,7 +258,8 @@ function readUserConfigSnapshots(afterId, limit) {
258
258
  hooks: parseJsonArray(r.hooks),
259
259
  commands: parseJsonArray(r.commands),
260
260
  rules: parseJsonArray(r.rules),
261
- skills: parseJsonArray(r.skills)
261
+ skills: parseJsonArray(r.skills),
262
+ pluginHooks: parseJsonArray(r.plugin_hooks)
262
263
  }));
263
264
  const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
264
265
  return { rows, maxId };
@@ -785,4 +786,4 @@ export {
785
786
  writeWatermark,
786
787
  resetWatermarks
787
788
  };
788
- //# sourceMappingURL=chunk-SEXU2WYG.js.map
789
+ //# sourceMappingURL=chunk-MEVW27U4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sync/reader.ts","../src/sync/registry.ts","../src/sync/watermark.ts"],"sourcesContent":["import { getDb } from \"../db/schema.js\";\nimport type {\n HookEventRecord,\n MessageSyncRecord,\n MetricRow,\n OtelLogRecord,\n OtelSpanRecord,\n RepoConfigSnapshotRecord,\n ScannerEventRecord,\n ScannerTurnRecord,\n SessionSyncRecord,\n ToolCallSyncRecord,\n UserConfigSnapshotRecord,\n} from \"./types.js\";\n\nfunction parseJson(raw: string | null): Record<string, unknown> | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw);\n return typeof parsed === \"object\" && parsed !== null ? parsed : null;\n } catch {\n return null;\n }\n}\n\n// ── Hook events ──────────────────────────────────────────────────────────────\n\nconst HOOK_EVENTS_SQL = `\n SELECT h.id, h.session_id, h.sync_id, h.event_type, h.timestamp_ms, h.cwd, h.repository,\n h.tool_name, decompress(h.payload) as payload,\n h.user_prompt, h.file_path, h.command, h.tool_result,\n s.target\n FROM hook_events h\n LEFT JOIN sessions s ON s.session_id = h.session_id\n WHERE h.id > ?\n ORDER BY h.id\n LIMIT ?\n`;\n\nexport function readHookEvents(\n afterId: number,\n limit: number,\n): { rows: HookEventRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(HOOK_EVENTS_SQL).all(afterId, limit) as Array<{\n id: number;\n session_id: string;\n sync_id: string | null;\n event_type: string;\n timestamp_ms: number;\n cwd: string | null;\n repository: string | null;\n tool_name: string | null;\n payload: string | null;\n user_prompt: string | null;\n file_path: string | null;\n command: string | null;\n tool_result: string | null;\n target: string | null;\n }>;\n\n const rows: HookEventRecord[] = rawRows.map((r) => ({\n hookId: r.id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n eventType: r.event_type,\n timestampMs: r.timestamp_ms,\n cwd: r.cwd,\n repository: r.repository,\n toolName: r.tool_name,\n payload: parseJson(r.payload),\n userPrompt: r.user_prompt,\n filePath: r.file_path,\n command: r.command,\n toolResult: r.tool_result,\n target: r.target,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].hookId : afterId;\n return { rows, maxId };\n}\n\n// ── OTLP logs ────────────────────────────────────────────────────────────────\n\n/** Body types that hooks already cover — filtered out when hooks are installed. */\nconst HOOK_COVERED_BODIES = [\n // Claude Code\n \"claude_code.user_prompt\",\n \"claude_code.tool_decision\",\n \"claude_code.tool_result\",\n // Gemini CLI\n \"gemini_cli.user_prompt\",\n \"gemini_cli.tool_call\",\n \"gemini_cli.hook_call\",\n];\n\nconst ALL_LOGS_SQL = `\n SELECT id, sync_id, timestamp_ns, body, attributes, resource_attributes,\n severity_text, session_id, prompt_id, trace_id, span_id\n FROM otel_logs\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\ninterface RawOtelLogRow {\n id: number;\n sync_id: string | null;\n timestamp_ns: number;\n body: string | null;\n attributes: string | null;\n resource_attributes: string | null;\n severity_text: string | null;\n session_id: string | null;\n prompt_id: string | null;\n trace_id: string | null;\n span_id: string | null;\n}\n\nfunction mapOtelRows(rawRows: RawOtelLogRow[]): OtelLogRecord[] {\n return rawRows.map((r) => ({\n id: r.id,\n syncId: r.sync_id,\n timestampNs: r.timestamp_ns,\n body: r.body,\n attributes: parseJson(r.attributes),\n resourceAttributes: parseJson(r.resource_attributes),\n severityText: r.severity_text,\n sessionId: r.session_id,\n promptId: r.prompt_id,\n traceId: r.trace_id,\n spanId: r.span_id,\n }));\n}\n\n/**\n * Read OTLP logs in batches.\n * When hooksInstalled is true, filters out body types that hooks cover\n * (tool_decision, tool_result, user_prompt) to avoid double-counting.\n */\n/**\n * Read OTLP logs in batches.\n * When hooksInstalled is true, filters out body types that hooks cover\n * (tool_decision, tool_result, user_prompt) to avoid double-counting.\n *\n * maxId advances to the highest id scanned (not just the highest returned),\n * so the watermark skips past blocks of filtered rows without stalling.\n */\nexport function readOtelLogs(\n afterId: number,\n limit: number,\n hooksInstalled: boolean,\n): { rows: OtelLogRecord[]; maxId: number } {\n const db = getDb();\n\n if (!hooksInstalled) {\n const rawRows = db\n .prepare(ALL_LOGS_SQL)\n .all(afterId, limit) as RawOtelLogRow[];\n const rows = mapOtelRows(rawRows);\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n }\n\n // When filtering, we need the max id from the unfiltered range so the\n // watermark advances past blocks of hook-covered rows.\n const scanMaxId = (\n db\n .prepare(\n \"SELECT MAX(id) as m FROM (SELECT id FROM otel_logs WHERE id > ? ORDER BY id LIMIT ?)\",\n )\n .get(afterId, limit) as { m: number | null }\n ).m;\n\n if (scanMaxId == null) return { rows: [], maxId: afterId };\n\n // Only return filtered rows within the scanned range (id <= scanMaxId),\n // not beyond it — otherwise we'd skip ahead of the scan window.\n const rawRows = db\n .prepare(\n `SELECT id, sync_id, timestamp_ns, body, attributes, resource_attributes,\n severity_text, session_id, prompt_id, trace_id, span_id\n FROM otel_logs\n WHERE id > ? AND id <= ?\n AND body NOT IN (${HOOK_COVERED_BODIES.map(() => \"?\").join(\", \")})\n ORDER BY id`,\n )\n .all(afterId, scanMaxId, ...HOOK_COVERED_BODIES) as RawOtelLogRow[];\n\n const rows = mapOtelRows(rawRows);\n return { rows, maxId: scanMaxId };\n}\n\n// ── Metrics ──────────────────────────────────────────────────────────────────\n\nconst METRICS_SQL = `\n SELECT id, sync_id, timestamp_ns, name, value, metric_type, unit,\n attributes, resource_attributes, session_id\n FROM otel_metrics\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\nexport function readMetrics(\n afterId: number,\n limit: number,\n): { rows: MetricRow[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(METRICS_SQL).all(afterId, limit) as Array<{\n id: number;\n sync_id: string | null;\n timestamp_ns: number;\n name: string;\n value: number;\n metric_type: string | null;\n unit: string | null;\n attributes: string | null;\n resource_attributes: string | null;\n session_id: string | null;\n }>;\n\n const rows: MetricRow[] = rawRows.map((r) => ({\n id: r.id,\n syncId: r.sync_id,\n timestampNs: r.timestamp_ns,\n name: r.name,\n value: r.value,\n metricType: r.metric_type,\n unit: r.unit,\n attributes: parseJson(r.attributes),\n resourceAttributes: parseJson(r.resource_attributes),\n sessionId: r.session_id,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── Scanner turns ───────────────────────────────────────────────────────────\n\nconst SCANNER_TURNS_SQL = `\n SELECT t.id, t.session_id, t.sync_id, t.source, t.turn_index, t.timestamp_ms,\n t.model, t.role, t.content_preview,\n t.input_tokens, t.output_tokens, t.cache_read_tokens,\n t.cache_creation_tokens, t.reasoning_tokens,\n s.cli_version\n FROM scanner_turns t\n LEFT JOIN sessions s ON s.session_id = t.session_id\n WHERE t.id > ?\n ORDER BY t.id\n LIMIT ?\n`;\n\nexport function readScannerTurns(\n afterId: number,\n limit: number,\n): { rows: ScannerTurnRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(SCANNER_TURNS_SQL).all(afterId, limit) as Array<{\n id: number;\n session_id: string;\n sync_id: string | null;\n source: string;\n turn_index: number;\n timestamp_ms: number;\n model: string | null;\n role: string | null;\n content_preview: string | null;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n reasoning_tokens: number;\n cli_version: string | null;\n }>;\n\n const rows: ScannerTurnRecord[] = rawRows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n source: r.source,\n turnIndex: r.turn_index,\n timestampMs: r.timestamp_ms,\n model: r.model,\n role: r.role,\n contentPreview: r.content_preview,\n inputTokens: r.input_tokens,\n outputTokens: r.output_tokens,\n cacheReadTokens: r.cache_read_tokens,\n cacheCreationTokens: r.cache_creation_tokens,\n reasoningTokens: r.reasoning_tokens,\n cliVersion: r.cli_version,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── Scanner events ──────────────────────────────────────────────────────────\n\nconst SCANNER_EVENTS_SQL = `\n SELECT id, session_id, sync_id, source, event_type, timestamp_ms,\n tool_name, tool_input, tool_output, content, metadata\n FROM scanner_events\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\nexport function readScannerEvents(\n afterId: number,\n limit: number,\n): { rows: ScannerEventRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(SCANNER_EVENTS_SQL).all(afterId, limit) as Array<{\n id: number;\n session_id: string;\n sync_id: string | null;\n source: string;\n event_type: string;\n timestamp_ms: number;\n tool_name: string | null;\n tool_input: string | null;\n tool_output: string | null;\n content: string | null;\n metadata: string | null;\n }>;\n\n const rows: ScannerEventRecord[] = rawRows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n source: r.source,\n eventType: r.event_type,\n timestampMs: r.timestamp_ms,\n toolName: r.tool_name,\n toolInput: r.tool_input,\n toolOutput: r.tool_output,\n content: r.content,\n metadata: parseJson(r.metadata),\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── OTLP spans ─────────────────────────────────────────────────────────────\n\nconst SPANS_SQL = `\n SELECT id, trace_id, span_id, parent_span_id, name, kind,\n start_time_ns, end_time_ns, status_code, status_message,\n attributes, resource_attributes, session_id\n FROM otel_spans\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\nexport function readOtelSpans(\n afterId: number,\n limit: number,\n): { rows: OtelSpanRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(SPANS_SQL).all(afterId, limit) as Array<{\n id: number;\n trace_id: string;\n span_id: string;\n parent_span_id: string | null;\n name: string;\n kind: number | null;\n start_time_ns: number;\n end_time_ns: number;\n status_code: number | null;\n status_message: string | null;\n attributes: string | null;\n resource_attributes: string | null;\n session_id: string | null;\n }>;\n\n const rows: OtelSpanRecord[] = rawRows.map((r) => ({\n id: r.id,\n traceId: r.trace_id,\n spanId: r.span_id,\n parentSpanId: r.parent_span_id,\n name: r.name,\n kind: r.kind,\n startTimeNs: r.start_time_ns,\n endTimeNs: r.end_time_ns,\n statusCode: r.status_code,\n statusMessage: r.status_message,\n attributes: parseJson(r.attributes),\n resourceAttributes: parseJson(r.resource_attributes),\n sessionId: r.session_id,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── JSON parse helpers for config snapshots ─────────────────────────────────\n\nfunction parseJsonArray(raw: string | null): unknown[] {\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nfunction parseJsonObject(raw: string | null): Record<string, unknown> {\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw);\n return typeof parsed === \"object\" &&\n parsed !== null &&\n !Array.isArray(parsed)\n ? parsed\n : {};\n } catch {\n return {};\n }\n}\n\n// ── User config snapshots ──────────────────────────────────────────────────\n\nconst USER_CONFIG_SQL = `\n SELECT id, device_name, snapshot_at_ms, content_hash,\n permissions, enabled_plugins, hooks, commands, rules, skills, plugin_hooks\n FROM user_config_snapshots\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\nexport function readUserConfigSnapshots(\n afterId: number,\n limit: number,\n): { rows: UserConfigSnapshotRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(USER_CONFIG_SQL).all(afterId, limit) as Array<{\n id: number;\n device_name: string;\n snapshot_at_ms: number;\n content_hash: string;\n permissions: string | null;\n enabled_plugins: string | null;\n hooks: string | null;\n commands: string | null;\n rules: string | null;\n skills: string | null;\n plugin_hooks: string | null;\n }>;\n\n const rows: UserConfigSnapshotRecord[] = rawRows.map((r) => ({\n id: r.id,\n deviceName: r.device_name,\n snapshotAtMs: r.snapshot_at_ms,\n contentHash: r.content_hash,\n permissions: parseJsonObject(r.permissions),\n enabledPlugins: parseJsonArray(r.enabled_plugins),\n hooks: parseJsonArray(r.hooks),\n commands: parseJsonArray(r.commands),\n rules: parseJsonArray(r.rules),\n skills: parseJsonArray(r.skills),\n pluginHooks: parseJsonArray(r.plugin_hooks),\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── Repo config snapshots ──────────────────────────────────────────────────\n\nconst REPO_CONFIG_SQL = `\n SELECT id, repository, cwd, session_id, snapshot_at_ms, content_hash,\n hooks, mcp_servers, commands, agents, rules,\n local_hooks, local_mcp_servers, local_permissions,\n local_is_gitignored, instructions\n FROM repo_config_snapshots\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\nexport function readRepoConfigSnapshots(\n afterId: number,\n limit: number,\n): { rows: RepoConfigSnapshotRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(REPO_CONFIG_SQL).all(afterId, limit) as Array<{\n id: number;\n repository: string;\n cwd: string;\n session_id: string | null;\n snapshot_at_ms: number;\n content_hash: string;\n hooks: string | null;\n mcp_servers: string | null;\n commands: string | null;\n agents: string | null;\n rules: string | null;\n local_hooks: string | null;\n local_mcp_servers: string | null;\n local_permissions: string | null;\n local_is_gitignored: number;\n instructions: string | null;\n }>;\n\n const rows: RepoConfigSnapshotRecord[] = rawRows.map((r) => ({\n id: r.id,\n repository: r.repository,\n cwd: r.cwd,\n sessionId: r.session_id,\n snapshotAtMs: r.snapshot_at_ms,\n contentHash: r.content_hash,\n hooks: parseJsonArray(r.hooks),\n mcpServers: parseJsonArray(r.mcp_servers),\n commands: parseJsonArray(r.commands),\n agents: parseJsonArray(r.agents),\n rules: parseJsonArray(r.rules),\n localHooks: parseJsonArray(r.local_hooks),\n localMcpServers: parseJsonArray(r.local_mcp_servers),\n localPermissions: parseJsonObject(r.local_permissions),\n localIsGitignored: r.local_is_gitignored === 1,\n instructions: parseJsonArray(r.instructions),\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── Messages ────────────────────────────────────────────────────────────────\n\nconst MESSAGES_SQL = `\n SELECT id, session_id, ordinal, role, content, timestamp_ms,\n has_thinking, has_tool_use, content_length, is_system,\n model, token_usage, context_tokens, output_tokens,\n has_context_tokens, has_output_tokens\n FROM messages\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\nexport function readMessages(\n afterId: number,\n limit: number,\n): { rows: MessageSyncRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(MESSAGES_SQL).all(afterId, limit) as Array<{\n id: number;\n session_id: string;\n ordinal: number;\n role: string;\n content: string;\n timestamp_ms: number | null;\n has_thinking: number;\n has_tool_use: number;\n content_length: number;\n is_system: number;\n model: string;\n token_usage: string;\n context_tokens: number;\n output_tokens: number;\n has_context_tokens: number;\n has_output_tokens: number;\n }>;\n\n const rows: MessageSyncRecord[] = rawRows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n ordinal: r.ordinal,\n role: r.role,\n content: r.content,\n timestampMs: r.timestamp_ms,\n hasThinking: r.has_thinking === 1,\n hasToolUse: r.has_tool_use === 1,\n contentLength: r.content_length,\n isSystem: r.is_system === 1,\n model: r.model,\n tokenUsage: r.token_usage,\n contextTokens: r.context_tokens,\n outputTokens: r.output_tokens,\n hasContextTokens: r.has_context_tokens === 1,\n hasOutputTokens: r.has_output_tokens === 1,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── Tool calls ──────────────────────────────────────────────────────────────\n\nconst TOOL_CALLS_SQL = `\n SELECT id, message_id, session_id, sync_id, tool_name, category, tool_use_id,\n input_json, skill_name, result_content_length, result_content,\n subagent_session_id\n FROM tool_calls\n WHERE id > ?\n ORDER BY id\n LIMIT ?\n`;\n\nexport function readToolCalls(\n afterId: number,\n limit: number,\n): { rows: ToolCallSyncRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(TOOL_CALLS_SQL).all(afterId, limit) as Array<{\n id: number;\n message_id: number;\n session_id: string;\n sync_id: string | null;\n tool_name: string;\n category: string;\n tool_use_id: string | null;\n input_json: string | null;\n skill_name: string | null;\n result_content_length: number | null;\n result_content: string | null;\n subagent_session_id: string | null;\n }>;\n\n const rows: ToolCallSyncRecord[] = rawRows.map((r) => ({\n id: r.id,\n messageId: r.message_id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n toolName: r.tool_name,\n category: r.category,\n toolUseId: r.tool_use_id,\n inputJson: r.input_json,\n skillName: r.skill_name,\n resultContentLength: r.result_content_length,\n resultContent: r.result_content,\n subagentSessionId: r.subagent_session_id,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n// ── Sessions (watermark on sync_seq) ────────────────────────────────────────\n\nconst SESSIONS_SQL = `\n SELECT session_id, target, started_at_ms, ended_at_ms, cwd, first_prompt,\n permission_mode, agent_version,\n total_input_tokens, total_output_tokens, total_cache_read_tokens,\n total_cache_creation_tokens, total_reasoning_tokens, turn_count,\n models, summary, tool_counts, hook_tool_counts, event_type_counts, hook_event_type_counts, sync_seq,\n project, machine, message_count, user_message_count,\n parent_session_id, relationship_type, is_automated, created_at\n FROM sessions\n WHERE sync_seq > ?\n ORDER BY sync_seq\n LIMIT ?\n`;\n\nexport function readSessions(\n afterSeq: number,\n limit: number,\n): { rows: SessionSyncRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db.prepare(SESSIONS_SQL).all(afterSeq, limit) as Array<{\n session_id: string;\n target: string | null;\n started_at_ms: number | null;\n ended_at_ms: number | null;\n cwd: string | null;\n first_prompt: string | null;\n permission_mode: string | null;\n agent_version: string | null;\n total_input_tokens: number | null;\n total_output_tokens: number | null;\n total_cache_read_tokens: number | null;\n total_cache_creation_tokens: number | null;\n total_reasoning_tokens: number | null;\n turn_count: number | null;\n models: string | null;\n summary: string | null;\n tool_counts: string | null;\n hook_tool_counts: string | null;\n event_type_counts: string | null;\n hook_event_type_counts: string | null;\n sync_seq: number;\n project: string | null;\n machine: string;\n message_count: number;\n user_message_count: number;\n parent_session_id: string | null;\n relationship_type: string;\n is_automated: number;\n created_at: number | null;\n }>;\n\n if (rawRows.length === 0) return { rows: [], maxId: afterSeq };\n\n const sessionIds = rawRows.map((r) => r.session_id);\n const placeholders = sessionIds.map(() => \"?\").join(\", \");\n\n const repoRows = db\n .prepare(\n `SELECT session_id, repository, first_seen_ms, git_user_name, git_user_email, branch\n FROM session_repositories\n WHERE session_id IN (${placeholders})`,\n )\n .all(...sessionIds) as Array<{\n session_id: string;\n repository: string;\n first_seen_ms: number;\n git_user_name: string | null;\n git_user_email: string | null;\n branch: string | null;\n }>;\n\n const cwdRows = db\n .prepare(\n `SELECT session_id, cwd, first_seen_ms\n FROM session_cwds\n WHERE session_id IN (${placeholders})`,\n )\n .all(...sessionIds) as Array<{\n session_id: string;\n cwd: string;\n first_seen_ms: number;\n }>;\n\n const reposBySession = new Map<string, SessionSyncRecord[\"repositories\"]>();\n for (const r of repoRows) {\n const list = reposBySession.get(r.session_id) ?? [];\n list.push({\n repository: r.repository,\n firstSeenMs: r.first_seen_ms,\n gitUserName: r.git_user_name,\n gitUserEmail: r.git_user_email,\n branch: r.branch,\n });\n reposBySession.set(r.session_id, list);\n }\n\n const cwdsBySession = new Map<string, SessionSyncRecord[\"cwds\"]>();\n for (const r of cwdRows) {\n const list = cwdsBySession.get(r.session_id) ?? [];\n list.push({ cwd: r.cwd, firstSeenMs: r.first_seen_ms });\n cwdsBySession.set(r.session_id, list);\n }\n\n const rows: SessionSyncRecord[] = rawRows.map((r) => ({\n sessionId: r.session_id,\n target: r.target,\n startedAtMs: r.started_at_ms,\n endedAtMs: r.ended_at_ms,\n cwd: r.cwd,\n firstPrompt: r.first_prompt,\n permissionMode: r.permission_mode,\n agentVersion: r.agent_version,\n totalInputTokens: r.total_input_tokens,\n totalOutputTokens: r.total_output_tokens,\n totalCacheReadTokens: r.total_cache_read_tokens,\n totalCacheCreationTokens: r.total_cache_creation_tokens,\n totalReasoningTokens: r.total_reasoning_tokens,\n turnCount: r.turn_count,\n models: r.models,\n summary: r.summary,\n toolCounts: parseJsonObject(r.tool_counts) as Record<string, number>,\n hookToolCounts: parseJsonObject(r.hook_tool_counts) as Record<\n string,\n number\n >,\n eventTypeCounts: parseJsonObject(r.event_type_counts) as Record<\n string,\n number\n >,\n hookEventTypeCounts: parseJsonObject(r.hook_event_type_counts) as Record<\n string,\n number\n >,\n project: r.project,\n machine: r.machine,\n messageCount: r.message_count,\n userMessageCount: r.user_message_count,\n parentSessionId: r.parent_session_id,\n relationshipType: r.relationship_type,\n isAutomated: r.is_automated === 1,\n createdAt: r.created_at,\n repositories: reposBySession.get(r.session_id) ?? [],\n cwds: cwdsBySession.get(r.session_id) ?? [],\n }));\n\n const maxId = rawRows[rawRows.length - 1].sync_seq;\n return { rows, maxId };\n}\n\n// ── Per-session readers (for gated sync) ──────────────────────────────────\n\nexport function readSessionMessages(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: MessageSyncRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT id, session_id, ordinal, role, content, timestamp_ms,\n has_thinking, has_tool_use, content_length, is_system,\n model, token_usage, context_tokens, output_tokens,\n has_context_tokens, has_output_tokens\n FROM messages\n WHERE session_id = ? AND id > ?\n ORDER BY id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as Array<{\n id: number;\n session_id: string;\n ordinal: number;\n role: string;\n content: string;\n timestamp_ms: number | null;\n has_thinking: number;\n has_tool_use: number;\n content_length: number;\n is_system: number;\n model: string;\n token_usage: string;\n context_tokens: number;\n output_tokens: number;\n has_context_tokens: number;\n has_output_tokens: number;\n }>;\n\n const rows: MessageSyncRecord[] = rawRows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n ordinal: r.ordinal,\n role: r.role,\n content: r.content,\n timestampMs: r.timestamp_ms,\n hasThinking: r.has_thinking === 1,\n hasToolUse: r.has_tool_use === 1,\n contentLength: r.content_length,\n isSystem: r.is_system === 1,\n model: r.model,\n tokenUsage: r.token_usage,\n contextTokens: r.context_tokens,\n outputTokens: r.output_tokens,\n hasContextTokens: r.has_context_tokens === 1,\n hasOutputTokens: r.has_output_tokens === 1,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\nexport function readSessionToolCalls(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: ToolCallSyncRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT id, message_id, session_id, sync_id, tool_name, category, tool_use_id,\n input_json, skill_name, result_content_length, result_content,\n subagent_session_id\n FROM tool_calls\n WHERE session_id = ? AND id > ?\n ORDER BY id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as Array<{\n id: number;\n message_id: number;\n session_id: string;\n sync_id: string | null;\n tool_name: string;\n category: string;\n tool_use_id: string | null;\n input_json: string | null;\n skill_name: string | null;\n result_content_length: number | null;\n result_content: string | null;\n subagent_session_id: string | null;\n }>;\n\n const rows: ToolCallSyncRecord[] = rawRows.map((r) => ({\n id: r.id,\n messageId: r.message_id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n toolName: r.tool_name,\n category: r.category,\n toolUseId: r.tool_use_id,\n inputJson: r.input_json,\n skillName: r.skill_name,\n resultContentLength: r.result_content_length,\n resultContent: r.result_content,\n subagentSessionId: r.subagent_session_id,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\nexport function readSessionScannerTurns(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: ScannerTurnRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT t.id, t.session_id, t.sync_id, t.source, t.turn_index, t.timestamp_ms,\n t.model, t.role, t.content_preview,\n t.input_tokens, t.output_tokens, t.cache_read_tokens,\n t.cache_creation_tokens, t.reasoning_tokens,\n s.cli_version\n FROM scanner_turns t\n LEFT JOIN sessions s ON s.session_id = t.session_id\n WHERE t.session_id = ? AND t.id > ?\n ORDER BY t.id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as Array<{\n id: number;\n session_id: string;\n sync_id: string | null;\n source: string;\n turn_index: number;\n timestamp_ms: number;\n model: string | null;\n role: string | null;\n content_preview: string | null;\n input_tokens: number;\n output_tokens: number;\n cache_read_tokens: number;\n cache_creation_tokens: number;\n reasoning_tokens: number;\n cli_version: string | null;\n }>;\n\n const rows: ScannerTurnRecord[] = rawRows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n source: r.source,\n turnIndex: r.turn_index,\n timestampMs: r.timestamp_ms,\n model: r.model,\n role: r.role,\n contentPreview: r.content_preview,\n inputTokens: r.input_tokens,\n outputTokens: r.output_tokens,\n cacheReadTokens: r.cache_read_tokens,\n cacheCreationTokens: r.cache_creation_tokens,\n reasoningTokens: r.reasoning_tokens,\n cliVersion: r.cli_version,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\nexport function readSessionScannerEvents(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: ScannerEventRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT id, session_id, sync_id, source, event_type, timestamp_ms,\n tool_name, tool_input, tool_output, content, metadata\n FROM scanner_events\n WHERE session_id = ? AND id > ?\n ORDER BY id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as Array<{\n id: number;\n session_id: string;\n sync_id: string | null;\n source: string;\n event_type: string;\n timestamp_ms: number;\n tool_name: string | null;\n tool_input: string | null;\n tool_output: string | null;\n content: string | null;\n metadata: string | null;\n }>;\n\n const rows: ScannerEventRecord[] = rawRows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n source: r.source,\n eventType: r.event_type,\n timestampMs: r.timestamp_ms,\n toolName: r.tool_name,\n toolInput: r.tool_input,\n toolOutput: r.tool_output,\n content: r.content,\n metadata: parseJson(r.metadata),\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\nexport function readSessionHookEvents(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: HookEventRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT h.id, h.session_id, h.sync_id, h.event_type, h.timestamp_ms, h.cwd, h.repository,\n h.tool_name, decompress(h.payload) as payload,\n h.user_prompt, h.file_path, h.command, h.tool_result,\n s.target\n FROM hook_events h\n LEFT JOIN sessions s ON s.session_id = h.session_id\n WHERE h.session_id = ? AND h.id > ?\n ORDER BY h.id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as Array<{\n id: number;\n session_id: string;\n sync_id: string | null;\n event_type: string;\n timestamp_ms: number;\n cwd: string | null;\n repository: string | null;\n tool_name: string | null;\n payload: string | null;\n user_prompt: string | null;\n file_path: string | null;\n command: string | null;\n tool_result: string | null;\n target: string | null;\n }>;\n\n const rows: HookEventRecord[] = rawRows.map((r) => ({\n hookId: r.id,\n sessionId: r.session_id,\n syncId: r.sync_id,\n eventType: r.event_type,\n timestampMs: r.timestamp_ms,\n cwd: r.cwd,\n repository: r.repository,\n toolName: r.tool_name,\n payload: parseJson(r.payload),\n userPrompt: r.user_prompt,\n filePath: r.file_path,\n command: r.command,\n toolResult: r.tool_result,\n target: r.target,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].hookId : afterId;\n return { rows, maxId };\n}\n\nexport function readSessionOtelLogs(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: OtelLogRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT id, sync_id, timestamp_ns, body, attributes, resource_attributes,\n severity_text, session_id, prompt_id, trace_id, span_id\n FROM otel_logs\n WHERE session_id = ? AND id > ?\n ORDER BY id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as RawOtelLogRow[];\n\n const rows = mapOtelRows(rawRows);\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\nexport function readSessionOtelMetrics(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: MetricRow[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT id, sync_id, timestamp_ns, name, value, metric_type, unit,\n attributes, resource_attributes, session_id\n FROM otel_metrics\n WHERE session_id = ? AND id > ?\n ORDER BY id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as Array<{\n id: number;\n sync_id: string | null;\n timestamp_ns: number;\n name: string;\n value: number;\n metric_type: string | null;\n unit: string | null;\n attributes: string | null;\n resource_attributes: string | null;\n session_id: string | null;\n }>;\n\n const rows: MetricRow[] = rawRows.map((r) => ({\n id: r.id,\n syncId: r.sync_id,\n timestampNs: r.timestamp_ns,\n name: r.name,\n value: r.value,\n metricType: r.metric_type,\n unit: r.unit,\n attributes: parseJson(r.attributes),\n resourceAttributes: parseJson(r.resource_attributes),\n sessionId: r.session_id,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\nexport function readSessionOtelSpans(\n sessionId: string,\n afterId: number,\n limit: number,\n): { rows: OtelSpanRecord[]; maxId: number } {\n const db = getDb();\n const rawRows = db\n .prepare(\n `SELECT id, trace_id, span_id, parent_span_id, name, kind,\n start_time_ns, end_time_ns, status_code, status_message,\n attributes, resource_attributes, session_id\n FROM otel_spans\n WHERE session_id = ? AND id > ?\n ORDER BY id\n LIMIT ?`,\n )\n .all(sessionId, afterId, limit) as Array<{\n id: number;\n trace_id: string;\n span_id: string;\n parent_span_id: string | null;\n name: string;\n kind: number | null;\n start_time_ns: number;\n end_time_ns: number;\n status_code: number | null;\n status_message: string | null;\n attributes: string | null;\n resource_attributes: string | null;\n session_id: string | null;\n }>;\n\n const rows: OtelSpanRecord[] = rawRows.map((r) => ({\n id: r.id,\n traceId: r.trace_id,\n spanId: r.span_id,\n parentSpanId: r.parent_span_id,\n name: r.name,\n kind: r.kind,\n startTimeNs: r.start_time_ns,\n endTimeNs: r.end_time_ns,\n statusCode: r.status_code,\n statusMessage: r.status_message,\n attributes: parseJson(r.attributes),\n resourceAttributes: parseJson(r.resource_attributes),\n sessionId: r.session_id,\n }));\n\n const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;\n return { rows, maxId };\n}\n\n/** Map of table name → per-session reader for gated sync. */\nexport const SESSION_READERS: Record<\n string,\n (\n sessionId: string,\n afterId: number,\n limit: number,\n ) => { rows: unknown[]; maxId: number }\n> = {\n messages: readSessionMessages,\n tool_calls: readSessionToolCalls,\n scanner_turns: readSessionScannerTurns,\n scanner_events: readSessionScannerEvents,\n hook_events: readSessionHookEvents,\n otel_logs: readSessionOtelLogs,\n otel_metrics: readSessionOtelMetrics,\n otel_spans: readSessionOtelSpans,\n};\n","import {\n readHookEvents,\n readMessages,\n readMetrics,\n readOtelLogs,\n readOtelSpans,\n readRepoConfigSnapshots,\n readScannerEvents,\n readScannerTurns,\n readSessions,\n readToolCalls,\n readUserConfigSnapshots,\n} from \"./reader.js\";\nimport type { TableSyncDescriptor } from \"./types.js\";\n\n/**\n * Ordered list of table sync descriptors. The order matches the\n * round-robin execution order in the sync loop.\n *\n * All tables sync via POST /v1/sync with {table, rows} payload.\n * Session-linked tables are filtered by repo attribution in the sync loop.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const TABLE_SYNC_REGISTRY: TableSyncDescriptor<any>[] = [\n // ── Session-linked tables (filtered by repo attribution) ─────────────────\n\n {\n table: \"sessions\",\n logNoun: \"sessions\",\n read: (afterId, limit) => readSessions(afterId, limit),\n sessionLinked: true,\n },\n\n {\n table: \"messages\",\n logNoun: \"messages\",\n read: (afterId, limit) => readMessages(afterId, limit),\n sessionLinked: true,\n },\n\n {\n table: \"tool_calls\",\n logNoun: \"tool calls\",\n read: (afterId, limit) => readToolCalls(afterId, limit),\n sessionLinked: true,\n },\n\n {\n table: \"scanner_turns\",\n logNoun: \"turns\",\n read: (afterId, limit) => readScannerTurns(afterId, limit),\n sessionLinked: true,\n },\n\n {\n table: \"scanner_events\",\n logNoun: \"events\",\n read: (afterId, limit) => readScannerEvents(afterId, limit),\n sessionLinked: true,\n },\n\n {\n table: \"hook_events\",\n logNoun: \"events\",\n read: (afterId, limit) => readHookEvents(afterId, limit),\n sessionLinked: true,\n },\n\n {\n table: \"otel_logs\",\n logNoun: \"logs\",\n read: (afterId, limit) => readOtelLogs(afterId, limit, false),\n sessionLinked: true,\n },\n\n {\n table: \"otel_metrics\",\n logNoun: \"metrics\",\n read: (afterId, limit) => readMetrics(afterId, limit),\n sessionLinked: true,\n },\n\n {\n table: \"otel_spans\",\n logNoun: \"spans\",\n read: (afterId, limit) => readOtelSpans(afterId, limit),\n sessionLinked: true,\n },\n\n // ── Non-session tables (always synced) ────────────────────────────────────\n\n {\n table: \"user_config_snapshots\",\n logNoun: \"snapshots\",\n read: (afterId, limit) => readUserConfigSnapshots(afterId, limit),\n sessionLinked: false,\n },\n\n {\n table: \"repo_config_snapshots\",\n logNoun: \"snapshots\",\n read: (afterId, limit) => readRepoConfigSnapshots(afterId, limit),\n sessionLinked: false,\n },\n];\n","import { getDb } from \"../db/schema.js\";\nimport { TABLE_SYNC_REGISTRY } from \"./registry.js\";\n\nexport function watermarkKey(table: string, targetName: string): string {\n return `${table}:${targetName}`;\n}\n\nexport function readWatermark(key: string): number {\n const db = getDb();\n const row = db\n .prepare(\"SELECT value FROM watermarks WHERE key = ?\")\n .get(key) as { value: number } | undefined;\n return row?.value ?? 0;\n}\n\nexport function writeWatermark(key: string, value: number): void {\n const db = getDb();\n db.prepare(\n \"INSERT INTO watermarks (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value\",\n ).run(key, value);\n}\n\nexport function resetWatermarks(targetName?: string): void {\n const db = getDb();\n if (targetName) {\n const stmt = db.prepare(\"DELETE FROM watermarks WHERE key = ?\");\n for (const desc of TABLE_SYNC_REGISTRY) {\n stmt.run(watermarkKey(desc.table, targetName));\n }\n db.prepare(\"DELETE FROM target_session_sync WHERE target = ?\").run(\n targetName,\n );\n } else {\n db.prepare(\"DELETE FROM watermarks\").run();\n db.prepare(\"DELETE FROM target_session_sync\").run();\n }\n}\n"],"mappings":"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","names":["rawRows","rows"]}
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  readWatermark,
3
3
  watermarkKey
4
- } from "./chunk-SEXU2WYG.js";
4
+ } from "./chunk-MEVW27U4.js";
5
5
  import {
6
6
  loadUnifiedConfig
7
7
  } from "./chunk-QK5442ZP.js";
8
8
  import {
9
9
  dbStats
10
- } from "./chunk-LWXF7YRG.js";
10
+ } from "./chunk-GPTBERQD.js";
11
11
  import {
12
12
  allTargets
13
13
  } from "./chunk-QVK6VGCV.js";
14
14
  import {
15
15
  closeDb,
16
16
  getDb
17
- } from "./chunk-DZ5HJFB4.js";
17
+ } from "./chunk-SKZHAYNF.js";
18
18
  import {
19
19
  config
20
20
  } from "./chunk-K7YUPLES.js";
@@ -261,4 +261,4 @@ async function doctor() {
261
261
  export {
262
262
  doctor
263
263
  };
264
- //# sourceMappingURL=chunk-SUGSQ4YI.js.map
264
+ //# sourceMappingURL=chunk-N7NCNJZU.js.map
@@ -9,7 +9,7 @@ import {
9
9
  upsertSession,
10
10
  upsertSessionCwd,
11
11
  upsertSessionRepository
12
- } from "./chunk-BVOE7A2Z.js";
12
+ } from "./chunk-V3XR2TAN.js";
13
13
  import {
14
14
  resolveRepoFromCwd
15
15
  } from "./chunk-YVRWVDIA.js";
@@ -17,11 +17,11 @@ import {
17
17
  isGitignored,
18
18
  readConfig,
19
19
  resolveGitRoot
20
- } from "./chunk-3BUJ7URA.js";
20
+ } from "./chunk-3ILOOWUF.js";
21
21
  import {
22
22
  addBreadcrumb,
23
23
  captureException
24
- } from "./chunk-CF4GPWLI.js";
24
+ } from "./chunk-J3HVD4VI.js";
25
25
  import {
26
26
  log
27
27
  } from "./chunk-7Q3BJMLG.js";
@@ -465,7 +465,8 @@ function processHookEvent(data) {
465
465
  hooks: config2.user.hooks,
466
466
  commands: config2.user.commands,
467
467
  rules: config2.user.rules,
468
- skills: config2.user.skills
468
+ skills: config2.user.skills,
469
+ pluginHooks: config2.pluginHooks
469
470
  });
470
471
  } catch {
471
472
  }
@@ -1696,4 +1697,4 @@ export {
1696
1697
  handleProxyRequest,
1697
1698
  createProxyServer
1698
1699
  };
1699
- //# sourceMappingURL=chunk-RX2RXHBH.js.map
1700
+ //# sourceMappingURL=chunk-NE7VBLQD.js.map