@agimon-ai/po-log 0.0.0-alpha.2 → 0.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- let _agimon_ai_log_sink_mcp = require("@agimon-ai/log-sink-mcp");
1
+ let _agimon_ai_po_logsink = require("@agimon-ai/po-logsink");
2
2
  //#region src/services/metricsSource/index.ts
3
3
  /**
4
4
  * Historical metrics access for the `SPC h l` overlay.
@@ -62,7 +62,7 @@ async function queryHttp(endpoint, params, fetchImpl) {
62
62
  */
63
63
  function resolveInstance(identity) {
64
64
  try {
65
- const resolved = (0, _agimon_ai_log_sink_mcp.resolveLogSinkInstance)(identity);
65
+ const resolved = (0, _agimon_ai_po_logsink.resolveLogSinkInstance)(identity);
66
66
  return {
67
67
  scope: resolved.scope,
68
68
  dbPath: resolved.dbPath,
@@ -87,7 +87,7 @@ function createMetricsSource(options = {}) {
87
87
  const resolveEndpoint = async () => {
88
88
  if (endpoint !== void 0) return endpoint ?? void 0;
89
89
  try {
90
- endpoint = (await (0, _agimon_ai_log_sink_mcp.resolveLogSinkPort)({
90
+ endpoint = (await (0, _agimon_ai_po_logsink.resolveLogSinkPort)({
91
91
  ...identity,
92
92
  healthCheck: true
93
93
  }))?.endpoint ?? null;
@@ -108,7 +108,7 @@ function createMetricsSource(options = {}) {
108
108
  } catch {
109
109
  endpoint = null;
110
110
  }
111
- workerReader ??= (0, _agimon_ai_log_sink_mcp.createAsyncLogMetricsReader)(instance === void 0 ? { cwd: options.cwd } : { dbPath: instance.dbPath });
111
+ workerReader ??= (0, _agimon_ai_po_logsink.createAsyncLogMetricsReader)(instance === void 0 ? { cwd: options.cwd } : { dbPath: instance.dbPath });
112
112
  const report = await workerReader.getMetrics(params.filter, {
113
113
  groupBy: params.groupBy,
114
114
  period: params.period,
@@ -132,4 +132,4 @@ Object.defineProperty(exports, "createMetricsSource", {
132
132
  }
133
133
  });
134
134
 
135
- //# sourceMappingURL=metricsSource-DdwQNzoY.cjs.map
135
+ //# sourceMappingURL=metricsSource-Cy8f47Q0.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metricsSource-Cy8f47Q0.cjs","names":["resolveLogSinkInstance","resolveLogSinkPort","createAsyncLogMetricsReader"],"sources":["../src/services/metricsSource/index.ts"],"sourcesContent":["/**\n * Historical metrics access for the `SPC h l` overlay.\n *\n * Two transports, in order: the running sink's HTTP API, then a worker-backed\n * database reader. The sink daemon is long-lived and often lags the installed\n * package, so a daemon that rejects newer query parameters is treated as\n * unavailable rather than fatal. SQLite queries run in the package worker so a\n * one-day window cannot freeze the TUI.\n *\n * Both transports resolve the sink instance from the same identity the Pi\n * telemetry extension writes under. Without it log-sink falls back to\n * `npm_package_name`, which a running Pi process does not set, so the reader\n * would land on the local instance while the writer exported to the global one\n * and the panel would report an empty history that no session ever filled.\n */\nimport {\n createAsyncLogMetricsReader,\n type AsyncLogMetricsReader,\n type LogMetricsReport,\n resolveLogSinkInstance,\n resolveLogSinkPort,\n} from '@agimon-ai/po-logsink';\n\nimport type { MetricsInstance, MetricsQueryParams, MetricsSource, MetricsTransport } from '../../types/metricsSource';\n\nconst HTTP_TIMEOUT_MS = 5000;\n/**\n * The overlay ranks one tool, but a page pairing tool calls against failure\n * counts needs a denominator for every tool that failed, so callers ask.\n */\nconst DEFAULT_TOOL_LIMIT = 1;\n/** The panel ranks consumers, so ask for the biggest rather than the most recent. */\nconst TOKEN_SORT = 'total-tokens';\n\n/** The identity both transports resolve their sink instance from. */\ninterface ResolveIdentity {\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n packageName?: string;\n serviceName?: string;\n}\n\nexport interface MetricsSourceOptions {\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n /** Identity the sink is registered under; must match the telemetry writer's. */\n packageName?: string;\n serviceName?: string;\n /** Overrides HTTP transport discovery in tests. */\n fetchImpl?: typeof fetch;\n /** Overrides the worker-backed reader in tests. */\n workerReader?: Pick<AsyncLogMetricsReader, 'getMetrics' | 'close'>;\n}\n\nconst FILTER_QUERY_KEYS = {\n sessionId: 'sessionId',\n agentName: 'agentName',\n model: 'model',\n provider: 'provider',\n} as const;\n\nfunction filterEntries(params: MetricsQueryParams): [keyof typeof FILTER_QUERY_KEYS, string][] {\n const filter = params.filter;\n if (filter === undefined) return [];\n return (Object.keys(FILTER_QUERY_KEYS) as (keyof typeof FILTER_QUERY_KEYS)[]).flatMap((key) => {\n const value = filter[key];\n return value === undefined || value === '' ? [] : [[key, value] as [keyof typeof FILTER_QUERY_KEYS, string]];\n });\n}\n\nfunction searchParams(params: MetricsQueryParams): string {\n const search = new URLSearchParams({\n groupBy: params.groupBy,\n period: params.period,\n sort: TOKEN_SORT,\n limit: String(params.limit),\n toolLimit: String(params.toolLimit ?? DEFAULT_TOOL_LIMIT),\n });\n for (const [key, value] of filterEntries(params)) search.set(FILTER_QUERY_KEYS[key], value);\n return search.toString();\n}\n\nasync function queryHttp(\n endpoint: string,\n params: MetricsQueryParams,\n fetchImpl: typeof fetch,\n): Promise<LogMetricsReport> {\n const response = await fetchImpl(`${endpoint}/api/metrics?${searchParams(params)}`, {\n signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),\n });\n if (!response.ok) throw new Error(`sink responded ${response.status}`);\n\n const body = (await response.json()) as LogMetricsReport & { error?: string };\n // An older daemon rejects unknown group-by values and omits the timeline.\n if (body.error || !Array.isArray(body.timeline)) throw new Error(body.error ?? 'sink is an older version');\n return body;\n}\n\n/**\n * A malformed `.logsink.yaml` must not take the overlay down with it. Both\n * transports still run when resolution fails; they fall back to log-sink's own\n * resolution and the panel reports an unknown instance.\n */\nfunction resolveInstance(identity: ResolveIdentity): MetricsInstance | undefined {\n try {\n const resolved = resolveLogSinkInstance(identity);\n return {\n scope: resolved.scope,\n dbPath: resolved.dbPath,\n ...(resolved.registeredName === undefined ? {} : { registeredName: resolved.registeredName }),\n };\n } catch {\n return undefined;\n }\n}\n\nexport function createMetricsSource(options: MetricsSourceOptions = {}): MetricsSource {\n const fetchImpl = options.fetchImpl ?? globalThis.fetch;\n const identity = {\n env: options.env,\n cwd: options.cwd,\n packageName: options.packageName,\n serviceName: options.serviceName,\n };\n let transport: MetricsTransport | undefined;\n let endpoint: string | undefined | null;\n const instance = resolveInstance(identity);\n let workerReader = options.workerReader;\n\n const resolveEndpoint = async (): Promise<string | undefined> => {\n if (endpoint !== undefined) return endpoint ?? undefined;\n try {\n const resolved = await resolveLogSinkPort({ ...identity, healthCheck: true });\n endpoint = resolved?.endpoint ?? null;\n } catch {\n endpoint = null;\n }\n return endpoint ?? undefined;\n };\n\n return {\n lastTransport: () => transport,\n instance: () => instance,\n async query(params) {\n const httpEndpoint = await resolveEndpoint();\n if (httpEndpoint && fetchImpl) {\n try {\n const report = await queryHttp(httpEndpoint, params, fetchImpl);\n transport = 'http';\n return report;\n } catch {\n // A stale or unhealthy daemon must not hide data the worker can still read.\n endpoint = null;\n }\n }\n\n workerReader ??= createAsyncLogMetricsReader(\n instance === undefined ? { cwd: options.cwd } : { dbPath: instance.dbPath },\n );\n const report = await workerReader.getMetrics(params.filter, {\n groupBy: params.groupBy,\n period: params.period,\n sort: TOKEN_SORT,\n limit: params.limit,\n toolLimit: params.toolLimit ?? DEFAULT_TOOL_LIMIT,\n });\n transport = 'worker';\n return report;\n },\n async close() {\n await workerReader?.close();\n },\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { LogMetricsGroupBy, LogMetricsPeriod, LogMetricsReport } from "@agimon-ai/log-sink-mcp";
1
+ import { LogMetricsGroupBy, LogMetricsPeriod, LogMetricsReport } from "@agimon-ai/po-logsink";
2
2
  //#region src/types/metricsSource.d.ts
3
3
  /**
4
4
  * Narrowing one query to a single group, for a reader who clicked a row.
@@ -48,4 +48,4 @@ interface MetricsSource {
48
48
  }
49
49
  //#endregion
50
50
  export { MetricsTransport as a, MetricsSource as i, MetricsQuery as n, MetricsQueryParams as r, MetricsInstance as t };
51
- //# sourceMappingURL=metricsSource-cZnErdv4.d.mts.map
51
+ //# sourceMappingURL=metricsSource-DNT8BrTF.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metricsSource-cZnErdv4.d.mts","names":[],"sources":["../src/types/metricsSource.ts"],"mappings":";;;;;;;;;;;UAWiB;EACf;EACA;EACA;EACA;;UAGe;EACf,SAAS;EACT,QAAQ;EACR;;EAEA;EACA,SAAS;;KAGC,gBAAgB,QAAQ,uBAAuB,QAAQ;KAEvD;;;;;;;;UASK;EACf;EACA;;EAEA;;UAGe;EACf,OAAO;EACP,iBAAiB;;EAEjB,aAAa;;EAEb,UAAU"}
1
+ {"version":3,"file":"metricsSource-DNT8BrTF.d.mts","names":[],"sources":["../src/types/metricsSource.ts"],"mappings":";;;;;;;;;;;UAWiB;EACf;EACA;EACA;EACA;;UAGe;EACf,SAAS;EACT,QAAQ;EACR;;EAEA;EACA,SAAS;;KAGC,gBAAgB,QAAQ,uBAAuB,QAAQ;KAEvD;;;;;;;;UASK;EACf;EACA;;EAEA;;UAGe;EACf,OAAO;EACP,iBAAiB;;EAEjB,aAAa;;EAEb,UAAU"}
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_metricsSource = require("./metricsSource-DdwQNzoY.cjs");
2
+ const require_metricsSource = require("./metricsSource-Cy8f47Q0.cjs");
3
3
  exports.createMetricsSource = require_metricsSource.createMetricsSource;
@@ -1,5 +1,5 @@
1
- import { a as MetricsTransport, i as MetricsSource, n as MetricsQuery, r as MetricsQueryParams, t as MetricsInstance } from "./metricsSource-cZnErdv4.mjs";
2
- import { AsyncLogMetricsReader } from "@agimon-ai/log-sink-mcp";
1
+ import { a as MetricsTransport, i as MetricsSource, n as MetricsQuery, r as MetricsQueryParams, t as MetricsInstance } from "./metricsSource-DNT8BrTF.mjs";
2
+ import { AsyncLogMetricsReader } from "@agimon-ai/po-logsink";
3
3
  //#region src/services/metricsSource/index.d.ts
4
4
  interface MetricsSourceOptions {
5
5
  env?: NodeJS.ProcessEnv;
@@ -1,2 +1 @@
1
- import { t as createMetricsSource } from "./metricsSource-DXZLawQu.mjs";
2
- export { createMetricsSource };
1
+ export { t as createMetricsSource } from "./metricsSource-BaoBR_Ia.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-log",
3
- "version": "0.0.0-alpha.2",
3
+ "version": "0.0.0-alpha.21",
4
4
  "description": "Session metrics, findings, and sink status for headless agent observability.",
5
5
  "keywords": [
6
6
  "agent-observability",
@@ -19,7 +19,7 @@
19
19
  "bugs": {
20
20
  "url": "https://github.com/AgiFlow/po/issues"
21
21
  },
22
- "license": "MIT",
22
+ "license": "BUSL-1.1",
23
23
  "author": "Vuong Ngo",
24
24
  "repository": {
25
25
  "type": "git",
@@ -72,33 +72,33 @@
72
72
  "registry": "https://registry.npmjs.org/"
73
73
  },
74
74
  "dependencies": {
75
- "@agimon-ai/log-sink-mcp": "0.30.12",
76
- "@agimon-ai/po-core": "0.0.0-alpha.2",
77
- "@agimon-ai/po-telemetry": "0.0.0-alpha.2",
78
- "@agimon-ai/po-web-components": "0.0.0-alpha.2",
79
- "@agimon-ai/po-web-security": "0.0.0-alpha.2",
80
- "@deepseek-ai/cordis": "4.0.4",
81
- "hono": "4.13.12",
82
- "typebox": "1.3.34"
75
+ "@agimon-ai/po-core": "0.0.0-alpha.21",
76
+ "@agimon-ai/po-logsink": "0.0.1-alpha.3",
77
+ "@agimon-ai/po-telemetry": "0.0.0-alpha.21",
78
+ "@agimon-ai/po-web-components": "0.0.0-alpha.21",
79
+ "@agimon-ai/po-web-security": "0.0.0-alpha.21",
80
+ "@deepseek-ai/cordis": "4.0.5",
81
+ "hono": "4.13.13",
82
+ "typebox": "1.3.37"
83
83
  },
84
84
  "devDependencies": {
85
- "@agimon-ai/po-build": "0.0.0-alpha.2",
86
- "@agimon-ai/po-ui": "0.0.0-alpha.2",
87
- "@earendil-works/pi-coding-agent": "1.2.0",
88
- "@modelcontextprotocol/client": "2.2.0",
85
+ "@agimon-ai/po-build": "0.0.0-alpha.21",
86
+ "@agimon-ai/po-ui": "0.0.0-alpha.21",
87
+ "@earendil-works/pi-coding-agent": "1.3.0",
88
+ "@modelcontextprotocol/client": "2.3.1",
89
89
  "@tanstack/react-store": "0.11.2",
90
90
  "@tanstack/store": "0.11.2",
91
- "@types/node": "26.6.4",
91
+ "@types/node": "26.6.5",
92
92
  "@types/react": "19.3.0",
93
- "@vitest/coverage-v8": "5.0.3",
93
+ "@vitest/coverage-v8": "5.0.4",
94
94
  "react": "19.3.0",
95
95
  "react-dom": "19.3.0",
96
- "tsdown": "0.23.0",
96
+ "tsdown": "0.23.1",
97
97
  "typescript": "7.0.2",
98
- "vitest": "5.0.3"
98
+ "vitest": "5.0.4"
99
99
  },
100
100
  "peerDependencies": {
101
- "@earendil-works/pi-coding-agent": "1.2.0"
101
+ "@earendil-works/pi-coding-agent": "1.3.0"
102
102
  },
103
103
  "peerDependenciesMeta": {
104
104
  "@earendil-works/pi-coding-agent": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"metricsSource-DXZLawQu.mjs","names":[],"sources":["../src/services/metricsSource/index.ts"],"sourcesContent":["/**\n * Historical metrics access for the `SPC h l` overlay.\n *\n * Two transports, in order: the running sink's HTTP API, then a worker-backed\n * database reader. The sink daemon is long-lived and often lags the installed\n * package, so a daemon that rejects newer query parameters is treated as\n * unavailable rather than fatal. SQLite queries run in the package worker so a\n * one-day window cannot freeze the TUI.\n *\n * Both transports resolve the sink instance from the same identity the Pi\n * telemetry extension writes under. Without it log-sink falls back to\n * `npm_package_name`, which a running Pi process does not set, so the reader\n * would land on the local instance while the writer exported to the global one\n * and the panel would report an empty history that no session ever filled.\n */\nimport {\n createAsyncLogMetricsReader,\n type AsyncLogMetricsReader,\n type LogMetricsReport,\n resolveLogSinkInstance,\n resolveLogSinkPort,\n} from '@agimon-ai/log-sink-mcp';\n\nimport type { MetricsInstance, MetricsQueryParams, MetricsSource, MetricsTransport } from '../../types/metricsSource';\n\nconst HTTP_TIMEOUT_MS = 5000;\n/**\n * The overlay ranks one tool, but a page pairing tool calls against failure\n * counts needs a denominator for every tool that failed, so callers ask.\n */\nconst DEFAULT_TOOL_LIMIT = 1;\n/** The panel ranks consumers, so ask for the biggest rather than the most recent. */\nconst TOKEN_SORT = 'total-tokens';\n\n/** The identity both transports resolve their sink instance from. */\ninterface ResolveIdentity {\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n packageName?: string;\n serviceName?: string;\n}\n\nexport interface MetricsSourceOptions {\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n /** Identity the sink is registered under; must match the telemetry writer's. */\n packageName?: string;\n serviceName?: string;\n /** Overrides HTTP transport discovery in tests. */\n fetchImpl?: typeof fetch;\n /** Overrides the worker-backed reader in tests. */\n workerReader?: Pick<AsyncLogMetricsReader, 'getMetrics' | 'close'>;\n}\n\nconst FILTER_QUERY_KEYS = {\n sessionId: 'sessionId',\n agentName: 'agentName',\n model: 'model',\n provider: 'provider',\n} as const;\n\nfunction filterEntries(params: MetricsQueryParams): [keyof typeof FILTER_QUERY_KEYS, string][] {\n const filter = params.filter;\n if (filter === undefined) return [];\n return (Object.keys(FILTER_QUERY_KEYS) as (keyof typeof FILTER_QUERY_KEYS)[]).flatMap((key) => {\n const value = filter[key];\n return value === undefined || value === '' ? [] : [[key, value] as [keyof typeof FILTER_QUERY_KEYS, string]];\n });\n}\n\nfunction searchParams(params: MetricsQueryParams): string {\n const search = new URLSearchParams({\n groupBy: params.groupBy,\n period: params.period,\n sort: TOKEN_SORT,\n limit: String(params.limit),\n toolLimit: String(params.toolLimit ?? DEFAULT_TOOL_LIMIT),\n });\n for (const [key, value] of filterEntries(params)) search.set(FILTER_QUERY_KEYS[key], value);\n return search.toString();\n}\n\nasync function queryHttp(\n endpoint: string,\n params: MetricsQueryParams,\n fetchImpl: typeof fetch,\n): Promise<LogMetricsReport> {\n const response = await fetchImpl(`${endpoint}/api/metrics?${searchParams(params)}`, {\n signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),\n });\n if (!response.ok) throw new Error(`sink responded ${response.status}`);\n\n const body = (await response.json()) as LogMetricsReport & { error?: string };\n // An older daemon rejects unknown group-by values and omits the timeline.\n if (body.error || !Array.isArray(body.timeline)) throw new Error(body.error ?? 'sink is an older version');\n return body;\n}\n\n/**\n * A malformed `.logsink.yaml` must not take the overlay down with it. Both\n * transports still run when resolution fails; they fall back to log-sink's own\n * resolution and the panel reports an unknown instance.\n */\nfunction resolveInstance(identity: ResolveIdentity): MetricsInstance | undefined {\n try {\n const resolved = resolveLogSinkInstance(identity);\n return {\n scope: resolved.scope,\n dbPath: resolved.dbPath,\n ...(resolved.registeredName === undefined ? {} : { registeredName: resolved.registeredName }),\n };\n } catch {\n return undefined;\n }\n}\n\nexport function createMetricsSource(options: MetricsSourceOptions = {}): MetricsSource {\n const fetchImpl = options.fetchImpl ?? globalThis.fetch;\n const identity = {\n env: options.env,\n cwd: options.cwd,\n packageName: options.packageName,\n serviceName: options.serviceName,\n };\n let transport: MetricsTransport | undefined;\n let endpoint: string | undefined | null;\n const instance = resolveInstance(identity);\n let workerReader = options.workerReader;\n\n const resolveEndpoint = async (): Promise<string | undefined> => {\n if (endpoint !== undefined) return endpoint ?? undefined;\n try {\n const resolved = await resolveLogSinkPort({ ...identity, healthCheck: true });\n endpoint = resolved?.endpoint ?? null;\n } catch {\n endpoint = null;\n }\n return endpoint ?? undefined;\n };\n\n return {\n lastTransport: () => transport,\n instance: () => instance,\n async query(params) {\n const httpEndpoint = await resolveEndpoint();\n if (httpEndpoint && fetchImpl) {\n try {\n const report = await queryHttp(httpEndpoint, params, fetchImpl);\n transport = 'http';\n return report;\n } catch {\n // A stale or unhealthy daemon must not hide data the worker can still read.\n endpoint = null;\n }\n }\n\n workerReader ??= createAsyncLogMetricsReader(\n instance === undefined ? { cwd: options.cwd } : { dbPath: instance.dbPath },\n );\n const report = await workerReader.getMetrics(params.filter, {\n groupBy: params.groupBy,\n period: params.period,\n sort: TOKEN_SORT,\n limit: params.limit,\n toolLimit: params.toolLimit ?? DEFAULT_TOOL_LIMIT,\n });\n transport = 'worker';\n return report;\n },\n async close() {\n await workerReader?.close();\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"metricsSource-DdwQNzoY.cjs","names":["resolveLogSinkInstance","resolveLogSinkPort","createAsyncLogMetricsReader"],"sources":["../src/services/metricsSource/index.ts"],"sourcesContent":["/**\n * Historical metrics access for the `SPC h l` overlay.\n *\n * Two transports, in order: the running sink's HTTP API, then a worker-backed\n * database reader. The sink daemon is long-lived and often lags the installed\n * package, so a daemon that rejects newer query parameters is treated as\n * unavailable rather than fatal. SQLite queries run in the package worker so a\n * one-day window cannot freeze the TUI.\n *\n * Both transports resolve the sink instance from the same identity the Pi\n * telemetry extension writes under. Without it log-sink falls back to\n * `npm_package_name`, which a running Pi process does not set, so the reader\n * would land on the local instance while the writer exported to the global one\n * and the panel would report an empty history that no session ever filled.\n */\nimport {\n createAsyncLogMetricsReader,\n type AsyncLogMetricsReader,\n type LogMetricsReport,\n resolveLogSinkInstance,\n resolveLogSinkPort,\n} from '@agimon-ai/log-sink-mcp';\n\nimport type { MetricsInstance, MetricsQueryParams, MetricsSource, MetricsTransport } from '../../types/metricsSource';\n\nconst HTTP_TIMEOUT_MS = 5000;\n/**\n * The overlay ranks one tool, but a page pairing tool calls against failure\n * counts needs a denominator for every tool that failed, so callers ask.\n */\nconst DEFAULT_TOOL_LIMIT = 1;\n/** The panel ranks consumers, so ask for the biggest rather than the most recent. */\nconst TOKEN_SORT = 'total-tokens';\n\n/** The identity both transports resolve their sink instance from. */\ninterface ResolveIdentity {\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n packageName?: string;\n serviceName?: string;\n}\n\nexport interface MetricsSourceOptions {\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n /** Identity the sink is registered under; must match the telemetry writer's. */\n packageName?: string;\n serviceName?: string;\n /** Overrides HTTP transport discovery in tests. */\n fetchImpl?: typeof fetch;\n /** Overrides the worker-backed reader in tests. */\n workerReader?: Pick<AsyncLogMetricsReader, 'getMetrics' | 'close'>;\n}\n\nconst FILTER_QUERY_KEYS = {\n sessionId: 'sessionId',\n agentName: 'agentName',\n model: 'model',\n provider: 'provider',\n} as const;\n\nfunction filterEntries(params: MetricsQueryParams): [keyof typeof FILTER_QUERY_KEYS, string][] {\n const filter = params.filter;\n if (filter === undefined) return [];\n return (Object.keys(FILTER_QUERY_KEYS) as (keyof typeof FILTER_QUERY_KEYS)[]).flatMap((key) => {\n const value = filter[key];\n return value === undefined || value === '' ? [] : [[key, value] as [keyof typeof FILTER_QUERY_KEYS, string]];\n });\n}\n\nfunction searchParams(params: MetricsQueryParams): string {\n const search = new URLSearchParams({\n groupBy: params.groupBy,\n period: params.period,\n sort: TOKEN_SORT,\n limit: String(params.limit),\n toolLimit: String(params.toolLimit ?? DEFAULT_TOOL_LIMIT),\n });\n for (const [key, value] of filterEntries(params)) search.set(FILTER_QUERY_KEYS[key], value);\n return search.toString();\n}\n\nasync function queryHttp(\n endpoint: string,\n params: MetricsQueryParams,\n fetchImpl: typeof fetch,\n): Promise<LogMetricsReport> {\n const response = await fetchImpl(`${endpoint}/api/metrics?${searchParams(params)}`, {\n signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),\n });\n if (!response.ok) throw new Error(`sink responded ${response.status}`);\n\n const body = (await response.json()) as LogMetricsReport & { error?: string };\n // An older daemon rejects unknown group-by values and omits the timeline.\n if (body.error || !Array.isArray(body.timeline)) throw new Error(body.error ?? 'sink is an older version');\n return body;\n}\n\n/**\n * A malformed `.logsink.yaml` must not take the overlay down with it. Both\n * transports still run when resolution fails; they fall back to log-sink's own\n * resolution and the panel reports an unknown instance.\n */\nfunction resolveInstance(identity: ResolveIdentity): MetricsInstance | undefined {\n try {\n const resolved = resolveLogSinkInstance(identity);\n return {\n scope: resolved.scope,\n dbPath: resolved.dbPath,\n ...(resolved.registeredName === undefined ? {} : { registeredName: resolved.registeredName }),\n };\n } catch {\n return undefined;\n }\n}\n\nexport function createMetricsSource(options: MetricsSourceOptions = {}): MetricsSource {\n const fetchImpl = options.fetchImpl ?? globalThis.fetch;\n const identity = {\n env: options.env,\n cwd: options.cwd,\n packageName: options.packageName,\n serviceName: options.serviceName,\n };\n let transport: MetricsTransport | undefined;\n let endpoint: string | undefined | null;\n const instance = resolveInstance(identity);\n let workerReader = options.workerReader;\n\n const resolveEndpoint = async (): Promise<string | undefined> => {\n if (endpoint !== undefined) return endpoint ?? undefined;\n try {\n const resolved = await resolveLogSinkPort({ ...identity, healthCheck: true });\n endpoint = resolved?.endpoint ?? null;\n } catch {\n endpoint = null;\n }\n return endpoint ?? undefined;\n };\n\n return {\n lastTransport: () => transport,\n instance: () => instance,\n async query(params) {\n const httpEndpoint = await resolveEndpoint();\n if (httpEndpoint && fetchImpl) {\n try {\n const report = await queryHttp(httpEndpoint, params, fetchImpl);\n transport = 'http';\n return report;\n } catch {\n // A stale or unhealthy daemon must not hide data the worker can still read.\n endpoint = null;\n }\n }\n\n workerReader ??= createAsyncLogMetricsReader(\n instance === undefined ? { cwd: options.cwd } : { dbPath: instance.dbPath },\n );\n const report = await workerReader.getMetrics(params.filter, {\n groupBy: params.groupBy,\n period: params.period,\n sort: TOKEN_SORT,\n limit: params.limit,\n toolLimit: params.toolLimit ?? DEFAULT_TOOL_LIMIT,\n });\n transport = 'worker';\n return report;\n },\n async close() {\n await workerReader?.close();\n },\n };\n}\n"],"mappings":"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"}