psyclaw 0.30.2 → 0.30.4

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 (52) hide show
  1. package/README.md +6 -6
  2. package/dist/apps/panel/index.html +27 -1
  3. package/dist/apps/panel/observability.js +6 -3
  4. package/dist/src/adapters/pi/extension.js +16 -6
  5. package/dist/src/adapters/pi/extension.js.map +1 -1
  6. package/dist/src/adapters/pi/model.d.ts +1 -0
  7. package/dist/src/adapters/pi/model.js +30 -18
  8. package/dist/src/adapters/pi/model.js.map +1 -1
  9. package/dist/src/cli.js +4 -4
  10. package/dist/src/cli.js.map +1 -1
  11. package/dist/src/core/provider-endpoint.d.ts +4 -0
  12. package/dist/src/core/provider-endpoint.js +23 -0
  13. package/dist/src/core/provider-endpoint.js.map +1 -0
  14. package/dist/src/observability/config.d.ts +6 -0
  15. package/dist/src/observability/config.js +36 -5
  16. package/dist/src/observability/config.js.map +1 -1
  17. package/dist/src/observability/error-context.d.ts +20 -0
  18. package/dist/src/observability/error-context.js +104 -0
  19. package/dist/src/observability/error-context.js.map +1 -0
  20. package/dist/src/observability/expected.d.ts +10 -0
  21. package/dist/src/observability/expected.js +37 -0
  22. package/dist/src/observability/expected.js.map +1 -0
  23. package/dist/src/observability/identity.d.ts +10 -0
  24. package/dist/src/observability/identity.js +24 -0
  25. package/dist/src/observability/identity.js.map +1 -0
  26. package/dist/src/observability/index.d.ts +12 -2
  27. package/dist/src/observability/index.js +67 -9
  28. package/dist/src/observability/index.js.map +1 -1
  29. package/dist/src/observability/langfuse.d.ts +27 -0
  30. package/dist/src/observability/langfuse.js +65 -0
  31. package/dist/src/observability/langfuse.js.map +1 -0
  32. package/dist/src/observability/llm.d.ts +105 -0
  33. package/dist/src/observability/llm.js +185 -0
  34. package/dist/src/observability/llm.js.map +1 -0
  35. package/dist/src/observability/node-sdks.d.ts +9 -0
  36. package/dist/src/observability/node-sdks.js +80 -10
  37. package/dist/src/observability/node-sdks.js.map +1 -1
  38. package/dist/src/observability/notice.js +1 -1
  39. package/dist/src/observability/notice.js.map +1 -1
  40. package/dist/src/observability/preference.d.ts +3 -0
  41. package/dist/src/observability/preference.js +13 -0
  42. package/dist/src/observability/preference.js.map +1 -1
  43. package/dist/src/orchestration/pi-executor.js +10 -1
  44. package/dist/src/orchestration/pi-executor.js.map +1 -1
  45. package/dist/src/orchestration/runner.js +3 -3
  46. package/dist/src/orchestration/runner.js.map +1 -1
  47. package/dist/src/panel/server.js +64 -3
  48. package/dist/src/panel/server.js.map +1 -1
  49. package/dist/src/setup.d.ts +1 -0
  50. package/dist/src/setup.js +4 -8
  51. package/dist/src/setup.js.map +1 -1
  52. package/package.json +1 -1
@@ -1,12 +1,19 @@
1
1
  import { type NodeObservabilityConfig } from "./config.js";
2
+ import type { LlmGenerationInput } from "./llm.js";
2
3
  import type { ObservabilityHandle } from "./node-sdks.js";
3
4
  import { type TelemetryPreference } from "./preference.js";
4
- export { DEFAULT_POSTHOG_HOST, OBS_CONFIG_SCRIPT_ID, browserConfigForPreference, emptyBrowserObservabilityConfig, injectBrowserObservabilityConfig, observabilityEnabled, readBrowserObservabilityConfig, readNodeObservabilityConfig, sanitizeAgentEventProperties, } from "./config.js";
5
+ export { DEFAULT_LANGFUSE_HOST, DEFAULT_POSTHOG_HOST, OBS_CONFIG_SCRIPT_ID, browserConfigForPreference, emptyBrowserObservabilityConfig, injectBrowserObservabilityConfig, observabilityEnabled, readBrowserObservabilityConfig, readNodeObservabilityConfig, sanitizeAgentEventProperties, } from "./config.js";
5
6
  export type { BrowserObservabilityConfig, NodeObservabilityConfig, AgentEventPropertyValue } from "./config.js";
6
- export { DEFAULT_TELEMETRY_PREFERENCE, parseTelemetryPreference, psyclawSettingsPath, readTelemetryEnvOverride, readTelemetryPreference, resolveTelemetryEnabled, shouldShowTelemetryNotice, telemetryPreferenceOptions, writeTelemetryPreference, } from "./preference.js";
7
+ export { DEFAULT_TELEMETRY_PREFERENCE, parseAnonymousDistinctId, parseTelemetryPreference, psyclawSettingsPath, readTelemetryEnvOverride, readTelemetryPreference, resolveTelemetryEnabled, shouldShowTelemetryNotice, telemetryPreferenceOptions, writeTelemetryPreference, } from "./preference.js";
7
8
  export type { TelemetryPreference } from "./preference.js";
8
9
  export { maybeShowTelemetryNotice } from "./notice.js";
9
10
  export type { TelemetryNoticeChoice } from "./notice.js";
11
+ export { ensureAnonymousDistinctId, newAnonymousDistinctId } from "./identity.js";
12
+ export { filesystemErrorContext, fsWriteErrorMessage, redactUserPath } from "./error-context.js";
13
+ export { extractPiGeneration, lastPiGeneration, posthogAiGenerationProperties } from "./llm.js";
14
+ export type { LlmGenerationInput } from "./llm.js";
15
+ export { readLangfuseConfig } from "./langfuse.js";
16
+ export { ExpectedUserError, isExpectedUserError, isExpectedUserErrorMessage } from "./expected.js";
10
17
  type BootFn = (config: NodeObservabilityConfig) => Promise<ObservabilityHandle>;
11
18
  /**
12
19
  * Initialize Sentry and/or PostHog when product telemetry is enabled.
@@ -23,6 +30,9 @@ export declare function isObservabilityActive(): boolean;
23
30
  export declare function trackAgentEvent(name: string, properties?: Record<string, unknown>): Promise<void>;
24
31
  export declare function captureAgentError(error: unknown, context?: Record<string, string>): Promise<void>;
25
32
  export declare function trackGateWaiting(gate: "plan_approval" | "run_approval" | "tool_approval"): Promise<void>;
33
+ export declare function trackSkillInstall(status: "queued" | "started" | "finished" | "error", properties?: Record<string, unknown>): Promise<void>;
34
+ export declare function trackLlmGeneration(input: LlmGenerationInput): Promise<void>;
35
+ export declare function withAgentSpan<T>(name: string, attributes: Record<string, string>, fn: () => Promise<T>): Promise<T>;
26
36
  export declare function shutdownObservability(): Promise<void>;
27
37
  /** Test hook: swap the live handle without loading SDKs. */
28
38
  export declare function setObservabilityHandleForTests(next: ObservabilityHandle | undefined): void;
@@ -1,8 +1,18 @@
1
+ import { arch, platform } from "node:os";
2
+ import { PSYCLAW_VERSION } from "../branding.js";
1
3
  import { observabilityEnabled, readNodeObservabilityConfig, sanitizeAgentEventProperties, } from "./config.js";
4
+ import { filesystemErrorContext, redactUserPath } from "./error-context.js";
5
+ import { ensureAnonymousDistinctId, newAnonymousDistinctId } from "./identity.js";
6
+ import { isExpectedUserError } from "./expected.js";
2
7
  import { readTelemetryPreference, resolveTelemetryEnabled, telemetryPreferenceOptions } from "./preference.js";
3
- export { DEFAULT_POSTHOG_HOST, OBS_CONFIG_SCRIPT_ID, browserConfigForPreference, emptyBrowserObservabilityConfig, injectBrowserObservabilityConfig, observabilityEnabled, readBrowserObservabilityConfig, readNodeObservabilityConfig, sanitizeAgentEventProperties, } from "./config.js";
4
- export { DEFAULT_TELEMETRY_PREFERENCE, parseTelemetryPreference, psyclawSettingsPath, readTelemetryEnvOverride, readTelemetryPreference, resolveTelemetryEnabled, shouldShowTelemetryNotice, telemetryPreferenceOptions, writeTelemetryPreference, } from "./preference.js";
8
+ export { DEFAULT_LANGFUSE_HOST, DEFAULT_POSTHOG_HOST, OBS_CONFIG_SCRIPT_ID, browserConfigForPreference, emptyBrowserObservabilityConfig, injectBrowserObservabilityConfig, observabilityEnabled, readBrowserObservabilityConfig, readNodeObservabilityConfig, sanitizeAgentEventProperties, } from "./config.js";
9
+ export { DEFAULT_TELEMETRY_PREFERENCE, parseAnonymousDistinctId, parseTelemetryPreference, psyclawSettingsPath, readTelemetryEnvOverride, readTelemetryPreference, resolveTelemetryEnabled, shouldShowTelemetryNotice, telemetryPreferenceOptions, writeTelemetryPreference, } from "./preference.js";
5
10
  export { maybeShowTelemetryNotice } from "./notice.js";
11
+ export { ensureAnonymousDistinctId, newAnonymousDistinctId } from "./identity.js";
12
+ export { filesystemErrorContext, fsWriteErrorMessage, redactUserPath } from "./error-context.js";
13
+ export { extractPiGeneration, lastPiGeneration, posthogAiGenerationProperties } from "./llm.js";
14
+ export { readLangfuseConfig } from "./langfuse.js";
15
+ export { ExpectedUserError, isExpectedUserError, isExpectedUserErrorMessage } from "./expected.js";
6
16
  let bootPromise;
7
17
  let handle;
8
18
  async function defaultBoot(config) {
@@ -20,7 +30,15 @@ export async function initNodeObservability(options = {}) {
20
30
  if (!resolveTelemetryEnabled(preference, env)) {
21
31
  return handle !== undefined;
22
32
  }
23
- const config = readNodeObservabilityConfig(env);
33
+ const distinctId = preference.anonymousId
34
+ ?? env.PSYCLAW_DISTINCT_ID?.trim()
35
+ ?? (options.preference !== undefined && options.settingsPath === undefined
36
+ ? newAnonymousDistinctId()
37
+ : await ensureAnonymousDistinctId(telemetryPreferenceOptions(options.settingsPath)));
38
+ const config = {
39
+ ...readNodeObservabilityConfig(env),
40
+ distinctId,
41
+ };
24
42
  // Disabled must not latch the singleton: tests and a later enable in the
25
43
  // same process still need to be able to boot.
26
44
  if (!observabilityEnabled(config)) {
@@ -44,16 +62,51 @@ export function trackAgentEvent(name, properties = {}) {
44
62
  return emitWhenReady((active) => active.captureEvent(name, safe));
45
63
  }
46
64
  export function captureAgentError(error, context = {}) {
47
- const safeContext = {};
48
- for (const [key, value] of Object.entries(context)) {
49
- if (typeof value === "string" && value.trim())
50
- safeContext[key] = value.trim().slice(0, 80);
65
+ if (isExpectedUserError(error))
66
+ return Promise.resolve();
67
+ const extracted = filesystemErrorContext(error);
68
+ const safeContext = {
69
+ package_version: PSYCLAW_VERSION,
70
+ os_platform: platform(),
71
+ os_arch: arch(),
72
+ };
73
+ for (const [key, value] of Object.entries({ ...extracted, ...context })) {
74
+ if (typeof value === "string" && value.trim()) {
75
+ const limit = key === "failed_path" || key === "cwd" ? 240 : 80;
76
+ const next = key === "failed_path" || key === "cwd" ? redactUserPath(value.trim()) : value.trim();
77
+ safeContext[key] = next.slice(0, limit);
78
+ }
51
79
  }
52
80
  return emitWhenReady((active) => active.captureError(error, safeContext));
53
81
  }
54
82
  export function trackGateWaiting(gate) {
55
83
  return trackAgentEvent("gate_waiting_for_human", { phase: "gate", gate, status: "waiting" });
56
84
  }
85
+ export function trackSkillInstall(status, properties = {}) {
86
+ return trackAgentEvent(`skill_install_${status === "queued" ? "queued" : status === "started" ? "started" : status === "error" ? "failed" : "finished"}`, {
87
+ phase: "skill_install",
88
+ status,
89
+ ...properties,
90
+ });
91
+ }
92
+ export function trackLlmGeneration(input) {
93
+ return emitWhenReady((active) => {
94
+ if (typeof active.captureLlmGeneration === "function")
95
+ active.captureLlmGeneration(input);
96
+ });
97
+ }
98
+ export async function withAgentSpan(name, attributes, fn) {
99
+ try {
100
+ if (bootPromise)
101
+ await bootPromise;
102
+ }
103
+ catch {
104
+ /* telemetry boot failure must not block the agent */
105
+ }
106
+ if (handle && typeof handle.runSpan === "function")
107
+ return handle.runSpan(name, attributes, fn);
108
+ return fn();
109
+ }
57
110
  export async function shutdownObservability() {
58
111
  const active = handle;
59
112
  handle = undefined;
@@ -67,8 +120,13 @@ export function setObservabilityHandleForTests(next) {
67
120
  bootPromise = Promise.resolve(next);
68
121
  }
69
122
  async function emitWhenReady(fn) {
70
- if (bootPromise)
71
- await bootPromise;
123
+ try {
124
+ if (bootPromise)
125
+ await bootPromise;
126
+ }
127
+ catch {
128
+ return;
129
+ }
72
130
  if (handle)
73
131
  fn(handle);
74
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/observability/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/observability/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Optional Langfuse Cloud tracing via the public ingestion API.
3
+ *
4
+ * Sentry Performance already owns the global OpenTelemetry TracerProvider
5
+ * (`@sentry/node` init). Langfuse is therefore a separate REST client: no
6
+ * second NodeSDK / TracerProvider is registered. When LANGFUSE_PUBLIC_KEY or
7
+ * LANGFUSE_SECRET_KEY is unset, every call is a no-op.
8
+ */
9
+ import { DEFAULT_LANGFUSE_HOST } from "./config.js";
10
+ import type { LlmGenerationInput } from "./llm.js";
11
+ export { DEFAULT_LANGFUSE_HOST };
12
+ export interface LangfuseConfig {
13
+ publicKey: string;
14
+ secretKey: string;
15
+ host: string;
16
+ }
17
+ export declare function readLangfuseConfig(env?: NodeJS.ProcessEnv): LangfuseConfig | undefined;
18
+ export interface LangfuseHandle {
19
+ captureGeneration(input: LlmGenerationInput, options: {
20
+ sessionId: string;
21
+ userId: string;
22
+ }): void;
23
+ flush(): Promise<void>;
24
+ }
25
+ export declare function createLangfuseHandle(config: LangfuseConfig, options?: {
26
+ fetchImpl?: typeof fetch;
27
+ }): LangfuseHandle;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Optional Langfuse Cloud tracing via the public ingestion API.
3
+ *
4
+ * Sentry Performance already owns the global OpenTelemetry TracerProvider
5
+ * (`@sentry/node` init). Langfuse is therefore a separate REST client: no
6
+ * second NodeSDK / TracerProvider is registered. When LANGFUSE_PUBLIC_KEY or
7
+ * LANGFUSE_SECRET_KEY is unset, every call is a no-op.
8
+ */
9
+ import { PSYCLAW_VERSION } from "../branding.js";
10
+ import { DEFAULT_LANGFUSE_HOST } from "./config.js";
11
+ import { langfuseGenerationBody } from "./llm.js";
12
+ export { DEFAULT_LANGFUSE_HOST };
13
+ function trimEnv(value) {
14
+ const trimmed = value?.trim();
15
+ return trimmed ? trimmed : undefined;
16
+ }
17
+ export function readLangfuseConfig(env = process.env) {
18
+ const publicKey = trimEnv(env.LANGFUSE_PUBLIC_KEY);
19
+ const secretKey = trimEnv(env.LANGFUSE_SECRET_KEY);
20
+ if (!publicKey || !secretKey)
21
+ return undefined;
22
+ const host = (trimEnv(env.LANGFUSE_HOST) ?? trimEnv(env.LANGFUSE_BASE_URL) ?? DEFAULT_LANGFUSE_HOST)
23
+ .replace(/\/+$/, "");
24
+ return { publicKey, secretKey, host };
25
+ }
26
+ export function createLangfuseHandle(config, options = {}) {
27
+ const pending = new Set();
28
+ const fetchImpl = options.fetchImpl ?? fetch;
29
+ const authorization = `Basic ${Buffer.from(`${config.publicKey}:${config.secretKey}`).toString("base64")}`;
30
+ const enqueue = (batch) => {
31
+ const job = fetchImpl(`${config.host}/api/public/ingestion`, {
32
+ method: "POST",
33
+ headers: {
34
+ "content-type": "application/json",
35
+ authorization,
36
+ "x-langfuse-sdk-name": "psyclaw",
37
+ "x-langfuse-sdk-version": PSYCLAW_VERSION,
38
+ },
39
+ body: JSON.stringify({
40
+ batch,
41
+ metadata: { batch_size: Array.isArray(batch) ? batch.length : 1, sdk_integration: "psyclaw" },
42
+ }),
43
+ }).then((response) => {
44
+ if (!response.ok)
45
+ return undefined;
46
+ return undefined;
47
+ }).catch(() => undefined).then(() => undefined);
48
+ pending.add(job);
49
+ void job.finally(() => pending.delete(job));
50
+ };
51
+ return {
52
+ captureGeneration(input, ids) {
53
+ try {
54
+ enqueue(langfuseGenerationBody(input, ids).ingest.batch);
55
+ }
56
+ catch {
57
+ /* never break the agent on telemetry */
58
+ }
59
+ },
60
+ async flush() {
61
+ await Promise.allSettled([...pending]);
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=langfuse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langfuse.js","sourceRoot":"","sources":["../../../src/observability/langfuse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * PostHog LLM analytics + Langfuse generation payloads for the Pi stack.
3
+ *
4
+ * PsyClaw talks to models through `@earendil-works/pi-ai` / pi-coding-agent,
5
+ * not OpenAI/LangChain SDKs. Official PostHog guidance for that harness is
6
+ * either `@posthog/pi` or manual `$ai_generation` capture. We capture manually
7
+ * so events stay behind PsyClaw telemetry consent, share the CLI/panel
8
+ * distinct_id, and never include prompts, completions, or tool I/O.
9
+ */
10
+ export interface LlmUsage {
11
+ input?: number;
12
+ output?: number;
13
+ cacheRead?: number;
14
+ cacheWrite?: number;
15
+ cost?: {
16
+ total?: number;
17
+ };
18
+ }
19
+ export interface LlmGenerationInput {
20
+ provider?: string;
21
+ model?: string;
22
+ usage?: LlmUsage;
23
+ latencyMs?: number;
24
+ error?: boolean;
25
+ errorName?: string;
26
+ httpStatus?: number;
27
+ stream?: boolean;
28
+ traceId?: string;
29
+ sessionId?: string;
30
+ spanName?: string;
31
+ surface?: string;
32
+ }
33
+ export type PosthogAiPropertyValue = string | number | boolean;
34
+ export declare function usageFromUnknown(value: unknown): LlmUsage | undefined;
35
+ /**
36
+ * Pull token usage out of a Pi session / RPC event without reading message
37
+ * content. Matches the shapes already used by the panel usage aggregator.
38
+ */
39
+ export declare function extractPiGeneration(entry: unknown): LlmGenerationInput | undefined;
40
+ export declare function lastPiGeneration(entries: readonly unknown[]): LlmGenerationInput | undefined;
41
+ /** PostHog `$ai_generation` properties. Prompt/completion fields are omitted on purpose. */
42
+ export declare function posthogAiGenerationProperties(input: LlmGenerationInput, options: {
43
+ distinctId: string;
44
+ sessionId: string;
45
+ }): Record<string, PosthogAiPropertyValue>;
46
+ export declare function langfuseGenerationBody(input: LlmGenerationInput, options: {
47
+ sessionId: string;
48
+ userId: string;
49
+ }): {
50
+ traceId: string;
51
+ observationId: `${string}-${string}-${string}-${string}-${string}`;
52
+ started: string;
53
+ ended: string;
54
+ ingest: {
55
+ batch: ({
56
+ id: `${string}-${string}-${string}-${string}-${string}`;
57
+ timestamp: string;
58
+ type: string;
59
+ body: {
60
+ id: string;
61
+ timestamp: string;
62
+ name: string;
63
+ sessionId: string;
64
+ userId: string;
65
+ metadata: {
66
+ provider: string;
67
+ surface: string;
68
+ privacyMode: boolean;
69
+ };
70
+ };
71
+ } | {
72
+ id: `${string}-${string}-${string}-${string}-${string}`;
73
+ timestamp: string;
74
+ type: string;
75
+ body: {
76
+ metadata: {
77
+ provider: string;
78
+ surface: string;
79
+ privacyMode: boolean;
80
+ };
81
+ statusMessage?: string;
82
+ level: string;
83
+ costDetails?: {
84
+ total: number;
85
+ };
86
+ id: `${string}-${string}-${string}-${string}-${string}`;
87
+ traceId: string;
88
+ name: string;
89
+ startTime: string;
90
+ endTime: string;
91
+ model: string;
92
+ usageDetails: {
93
+ input: number;
94
+ output: number;
95
+ total: number;
96
+ cache_read: number;
97
+ cache_write: number;
98
+ };
99
+ timestamp?: never;
100
+ sessionId?: never;
101
+ userId?: never;
102
+ };
103
+ })[];
104
+ };
105
+ };
@@ -0,0 +1,185 @@
1
+ /**
2
+ * PostHog LLM analytics + Langfuse generation payloads for the Pi stack.
3
+ *
4
+ * PsyClaw talks to models through `@earendil-works/pi-ai` / pi-coding-agent,
5
+ * not OpenAI/LangChain SDKs. Official PostHog guidance for that harness is
6
+ * either `@posthog/pi` or manual `$ai_generation` capture. We capture manually
7
+ * so events stay behind PsyClaw telemetry consent, share the CLI/panel
8
+ * distinct_id, and never include prompts, completions, or tool I/O.
9
+ */
10
+ import { randomUUID } from "node:crypto";
11
+ import { estimateUsageCost } from "../core/pricing.js";
12
+ function finiteNumber(value) {
13
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
14
+ }
15
+ export function usageFromUnknown(value) {
16
+ if (!value || typeof value !== "object")
17
+ return undefined;
18
+ const record = value;
19
+ if (finiteNumber(record.input) === undefined
20
+ && finiteNumber(record.output) === undefined
21
+ && finiteNumber(record.cacheRead) === undefined) {
22
+ return undefined;
23
+ }
24
+ return record;
25
+ }
26
+ /**
27
+ * Pull token usage out of a Pi session / RPC event without reading message
28
+ * content. Matches the shapes already used by the panel usage aggregator.
29
+ */
30
+ export function extractPiGeneration(entry) {
31
+ if (!entry || typeof entry !== "object")
32
+ return undefined;
33
+ const record = entry;
34
+ if (record.type === "message" && record.message && typeof record.message === "object") {
35
+ const message = record.message;
36
+ const usage = usageFromUnknown(message.usage);
37
+ if (!usage)
38
+ return undefined;
39
+ const provider = typeof message.provider === "string" ? message.provider : undefined;
40
+ const model = typeof message.responseModel === "string"
41
+ ? message.responseModel
42
+ : typeof message.model === "string"
43
+ ? message.model
44
+ : undefined;
45
+ const error = Boolean(message.errorMessage) || Boolean(message.error);
46
+ return {
47
+ ...(provider === undefined ? {} : { provider }),
48
+ ...(model === undefined ? {} : { model }),
49
+ usage,
50
+ error,
51
+ spanName: "pi-message",
52
+ };
53
+ }
54
+ if ((record.type === "agent_end" || record.type === "agent_settled") && record.message && typeof record.message === "object") {
55
+ return extractPiGeneration({ type: "message", message: record.message });
56
+ }
57
+ if ((record.type === "branch_summary" || record.type === "compaction") && record.usage) {
58
+ const usage = usageFromUnknown(record.usage);
59
+ if (!usage)
60
+ return undefined;
61
+ return { provider: "summaries", model: "summaries", usage, spanName: String(record.type) };
62
+ }
63
+ return undefined;
64
+ }
65
+ export function lastPiGeneration(entries) {
66
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
67
+ const found = extractPiGeneration(entries[index]);
68
+ if (found)
69
+ return found;
70
+ }
71
+ return undefined;
72
+ }
73
+ /** PostHog `$ai_generation` properties. Prompt/completion fields are omitted on purpose. */
74
+ export function posthogAiGenerationProperties(input, options) {
75
+ const provider = input.provider?.trim().slice(0, 80) || "unknown";
76
+ const model = input.model?.trim().slice(0, 120) || "unknown";
77
+ const usage = input.usage ?? {};
78
+ const inputTokens = finiteNumber(usage.input) ?? 0;
79
+ const outputTokens = finiteNumber(usage.output) ?? 0;
80
+ const cacheRead = finiteNumber(usage.cacheRead) ?? 0;
81
+ const cacheWrite = finiteNumber(usage.cacheWrite) ?? 0;
82
+ const reportedCost = finiteNumber(usage.cost?.total);
83
+ const estimatedCost = estimateUsageCost(usage, provider, model);
84
+ const latencySeconds = finiteNumber(input.latencyMs) !== undefined
85
+ ? Number((input.latencyMs / 1000).toFixed(4))
86
+ : undefined;
87
+ const properties = {
88
+ $ai_trace_id: (input.traceId ?? randomUUID()).slice(0, 80),
89
+ $ai_session_id: (input.sessionId ?? options.sessionId).slice(0, 80),
90
+ $ai_span_name: (input.spanName ?? "pi-generation").slice(0, 80),
91
+ $ai_model: model,
92
+ $ai_provider: provider,
93
+ $ai_input_tokens: inputTokens,
94
+ $ai_output_tokens: outputTokens,
95
+ $ai_cache_read_tokens: cacheRead,
96
+ $ai_cache_write_tokens: cacheWrite,
97
+ $ai_http_status: input.httpStatus ?? (input.error ? 500 : 200),
98
+ $ai_is_error: input.error === true,
99
+ $ai_stream: input.stream === true,
100
+ $ai_privacy_mode: true,
101
+ surface: (input.surface ?? "cli").slice(0, 32),
102
+ $process_person_profile: false,
103
+ };
104
+ if (latencySeconds !== undefined)
105
+ properties.$ai_latency = latencySeconds;
106
+ if (reportedCost !== undefined)
107
+ properties.$ai_total_cost_usd = reportedCost;
108
+ else if (estimatedCost > 0)
109
+ properties.$ai_total_cost_usd = estimatedCost;
110
+ if (input.errorName)
111
+ properties.$ai_error = input.errorName.slice(0, 80);
112
+ return properties;
113
+ }
114
+ export function langfuseGenerationBody(input, options) {
115
+ const provider = input.provider?.trim().slice(0, 80) || "unknown";
116
+ const model = input.model?.trim().slice(0, 120) || "unknown";
117
+ const usage = input.usage ?? {};
118
+ const inputTokens = finiteNumber(usage.input) ?? 0;
119
+ const outputTokens = finiteNumber(usage.output) ?? 0;
120
+ const cacheRead = finiteNumber(usage.cacheRead) ?? 0;
121
+ const cacheWrite = finiteNumber(usage.cacheWrite) ?? 0;
122
+ const started = new Date(Date.now() - (finiteNumber(input.latencyMs) ?? 0)).toISOString();
123
+ const ended = new Date().toISOString();
124
+ const traceId = input.traceId ?? randomUUID();
125
+ const observationId = randomUUID();
126
+ const reportedCost = finiteNumber(usage.cost?.total);
127
+ const estimatedCost = estimateUsageCost(usage, provider, model);
128
+ const cost = reportedCost ?? (estimatedCost > 0 ? estimatedCost : undefined);
129
+ return {
130
+ traceId,
131
+ observationId,
132
+ started,
133
+ ended,
134
+ ingest: {
135
+ batch: [
136
+ {
137
+ id: randomUUID(),
138
+ timestamp: ended,
139
+ type: "trace-create",
140
+ body: {
141
+ id: traceId,
142
+ timestamp: started,
143
+ name: input.spanName ?? "pi-generation",
144
+ sessionId: options.sessionId,
145
+ userId: options.userId,
146
+ metadata: {
147
+ provider,
148
+ surface: input.surface ?? "cli",
149
+ privacyMode: true,
150
+ },
151
+ },
152
+ },
153
+ {
154
+ id: randomUUID(),
155
+ timestamp: ended,
156
+ type: "generation-create",
157
+ body: {
158
+ id: observationId,
159
+ traceId,
160
+ name: input.spanName ?? "pi-generation",
161
+ startTime: started,
162
+ endTime: ended,
163
+ model,
164
+ usageDetails: {
165
+ input: inputTokens,
166
+ output: outputTokens,
167
+ total: inputTokens + outputTokens + cacheRead + cacheWrite,
168
+ cache_read: cacheRead,
169
+ cache_write: cacheWrite,
170
+ },
171
+ ...(cost === undefined ? {} : { costDetails: { total: cost } }),
172
+ level: input.error ? "ERROR" : "DEFAULT",
173
+ ...(input.errorName === undefined ? {} : { statusMessage: input.errorName.slice(0, 200) }),
174
+ metadata: {
175
+ provider,
176
+ surface: input.surface ?? "cli",
177
+ privacyMode: true,
178
+ },
179
+ },
180
+ },
181
+ ],
182
+ },
183
+ };
184
+ }
185
+ //# sourceMappingURL=llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../../../src/observability/llm.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,20 @@
1
1
  /**
2
2
  * Loaded only after telemetry is confirmed enabled. Importing this file will
3
3
  * pull in Sentry/PostHog; callers must keep that behind the preference gate.
4
+ *
5
+ * Init order (documented in docs/telemetry.md):
6
+ * 1. Sentry.init — owns the global OpenTelemetry TracerProvider for Performance.
7
+ * 2. PostHog Node client — product events + `$ai_generation`.
8
+ * 3. Langfuse REST ingestion — optional, isolated from OTel (no second TracerProvider).
4
9
  */
5
10
  import type { AgentEventPropertyValue, NodeObservabilityConfig } from "./config.js";
11
+ import { type LlmGenerationInput } from "./llm.js";
6
12
  export interface ObservabilityHandle {
13
+ readonly distinctId?: string;
7
14
  captureEvent(name: string, properties: Record<string, AgentEventPropertyValue>): void;
8
15
  captureError(error: unknown, context?: Record<string, string>): void;
16
+ captureLlmGeneration?(input: LlmGenerationInput): void;
17
+ runSpan?<T>(name: string, attributes: Record<string, string>, fn: () => Promise<T>): Promise<T>;
9
18
  flush(): Promise<void>;
10
19
  }
11
20
  export declare function bootNodeSdks(config: NodeObservabilityConfig): Promise<ObservabilityHandle>;