@senad-d/observme 0.1.4 → 0.1.6

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 (71) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +24 -8
  3. package/docs/agent-subagent-observability-requirements.md +4 -3
  4. package/docs/compatibility-matrix.md +14 -3
  5. package/docs/configuration.md +2 -2
  6. package/docs/extension-integration.md +20 -13
  7. package/docs/reference/03-pi-event-and-session-model.md +6 -6
  8. package/docs/reference/04-telemetry-semantic-conventions.md +1 -0
  9. package/docs/reference/06-security-privacy-redaction.md +13 -6
  10. package/docs/reference/08-query-grafana-integration.md +30 -7
  11. package/docs/reference/11-deployment-runbooks.md +21 -0
  12. package/docs/reference/12-configuration-reference.md +30 -4
  13. package/docs/review-validation.md +10 -0
  14. package/examples/integrations/subagent-runner.ts +8 -5
  15. package/examples/observme.yaml +2 -2
  16. package/package.json +12 -4
  17. package/src/commands/obs-agents.ts +17 -12
  18. package/src/commands/obs-backfill.ts +2 -2
  19. package/src/commands/obs-command-support.ts +2 -1
  20. package/src/commands/obs-cost.ts +15 -7
  21. package/src/commands/obs-health.ts +22 -2
  22. package/src/commands/obs-loki-summary.ts +4 -8
  23. package/src/commands/obs-session.ts +35 -28
  24. package/src/commands/obs-status.ts +56 -13
  25. package/src/commands/obs-tools.ts +19 -8
  26. package/src/commands/obs-trace.ts +9 -0
  27. package/src/commands/obs.ts +6 -1
  28. package/src/config/bootstrap-project-config.ts +49 -32
  29. package/src/config/defaults.ts +0 -2
  30. package/src/config/load-config.ts +270 -92
  31. package/src/config/project-paths.ts +318 -6
  32. package/src/config/query-limits.ts +10 -0
  33. package/src/config/schema.ts +9 -8
  34. package/src/config/transport-security.ts +107 -0
  35. package/src/config/validate.ts +68 -11
  36. package/src/extension.ts +2 -12
  37. package/src/integration.ts +6 -2
  38. package/src/otel/logs.ts +24 -13
  39. package/src/otel/metrics.ts +24 -13
  40. package/src/otel/otlp-endpoint.ts +41 -2
  41. package/src/otel/otlp-http-options.ts +11 -0
  42. package/src/otel/sdk.ts +155 -9
  43. package/src/otel/shutdown.ts +39 -11
  44. package/src/otel/traces.ts +34 -16
  45. package/src/pi/agent-tree-tracker.ts +14 -1
  46. package/src/pi/compatibility.ts +30 -0
  47. package/src/pi/event-handlers/agent-turn.ts +16 -9
  48. package/src/pi/event-handlers/lifecycle.ts +207 -75
  49. package/src/pi/event-handlers/llm.ts +17 -9
  50. package/src/pi/event-handlers/session-events.ts +53 -19
  51. package/src/pi/event-handlers/tool-bash.ts +21 -27
  52. package/src/pi/handler-internals.ts +16 -26
  53. package/src/pi/handler-runtime.ts +142 -55
  54. package/src/pi/handler-types.ts +30 -15
  55. package/src/pi/handlers.ts +12 -1
  56. package/src/pi/integration-api.ts +27 -15
  57. package/src/pi/session-correlation.ts +167 -0
  58. package/src/pi/subagent-spawn.ts +164 -31
  59. package/src/privacy/redact.ts +574 -68
  60. package/src/privacy/secret-patterns.ts +6 -1
  61. package/src/query/grafana-readiness.ts +18 -2
  62. package/src/query/grafana-transport.ts +150 -27
  63. package/src/query/grafana-url.ts +36 -0
  64. package/src/query/grafana.ts +4 -136
  65. package/src/query/loki.ts +2 -4
  66. package/src/query/prometheus.ts +8 -10
  67. package/src/query/tempo.ts +2 -4
  68. package/src/query/trace-link.ts +186 -0
  69. package/src/safety/display-bounds.ts +84 -0
  70. package/src/semconv/metrics.ts +1 -0
  71. package/src/semconv/values.ts +2 -0
@@ -1,4 +1,11 @@
1
1
  import { SpanStatusCode } from "@opentelemetry/api";
2
+ import type {
3
+ ExtensionContext,
4
+ SessionBeforeTreeEvent,
5
+ SessionCompactEvent,
6
+ SessionInfoChangedEvent,
7
+ SessionTreeEvent,
8
+ } from "@earendil-works/pi-coding-agent";
2
9
  import { COMPACTION_ATTRIBUTES, SESSION_ATTRIBUTES } from "../../semconv/attributes.ts";
3
10
  import { LOG_EVENT_NAMES } from "../../semconv/metrics.ts";
4
11
  import { SPAN_NAMES } from "../../semconv/spans.ts";
@@ -6,12 +13,14 @@ import {
6
13
  buildBranchAttributes,
7
14
  buildBranchPreparationState,
8
15
  buildCompactionAttributes,
16
+ buildLineageMetricSafeLogAttributes,
9
17
  buildModelChangeAttributes,
10
18
  buildThinkingLevelChangeAttributes,
11
19
  emitStructuredLog,
12
20
  endActiveSpan,
13
21
  metricLabels,
14
22
  modelChangeMetricLabels,
23
+ readString,
15
24
  resolveOperationParentSpan,
16
25
  startActiveChildSpan,
17
26
  thinkingLevelChangeMetricLabels,
@@ -20,27 +29,52 @@ import {
20
29
  import type { HandlerRegistrar } from "../handler-runtime.ts";
21
30
  import type {
22
31
  AttributeMap,
23
- Handler,
24
32
  HandlerSessionState,
25
- ObservMeHandlerContext,
26
33
  ObservMeTelemetrySession,
34
+ PiEvent,
35
+ PiHandler,
27
36
  } from "../handler-types.ts";
28
37
 
29
38
  export function registerSessionEventHandlers(registrar: HandlerRegistrar, state: HandlerSessionState): void {
39
+ registrar.add("session_info_changed", createSessionInfoChangedHandler(state));
30
40
  registrar.add("model_select", createModelChangeHandler(state));
31
- registrar.add("model_change", createModelChangeHandler(state));
32
41
  registrar.add("thinking_level_select", createThinkingLevelChangeHandler(state));
33
- registrar.add("thinking_level_change", createThinkingLevelChangeHandler(state));
34
42
  registrar.add("session_before_tree", createSessionBeforeTreeHandler(state));
35
43
  registrar.add("session_tree", createBranchHandler(state));
36
44
  registrar.add("session_compact", createCompactionHandler(state));
37
45
  }
38
46
 
39
- function createModelChangeHandler(state: HandlerSessionState): Handler {
47
+ function createSessionInfoChangedHandler(state: HandlerSessionState): PiHandler<"session_info_changed"> {
48
+ return handleSessionInfoChanged.bind(undefined, state);
49
+ }
50
+
51
+ function handleSessionInfoChanged(
52
+ state: HandlerSessionState,
53
+ event: SessionInfoChangedEvent,
54
+ ctx: ExtensionContext,
55
+ ): void {
56
+ const session = state.session;
57
+ if (!session) return;
58
+
59
+ const attributes = {
60
+ ...buildLineageMetricSafeLogAttributes(session),
61
+ [SESSION_ATTRIBUTES.PI_SESSION_NAME]: readString(event, "name") ?? ctx.sessionManager?.getSessionName() ?? "unknown",
62
+ };
63
+
64
+ updateCurrentSessionAttributes(session, attributes, [SESSION_ATTRIBUTES.PI_SESSION_NAME]);
65
+ session.sessionSpan?.addEvent(LOG_EVENT_NAMES.SESSION_NAMED, attributes);
66
+ emitStructuredLog(session.logger, LOG_EVENT_NAMES.SESSION_NAMED, "session", attributes);
67
+ }
68
+
69
+ function createModelChangeHandler(state: HandlerSessionState): PiHandler<"model_select"> {
40
70
  return handleModelChange.bind(undefined, state);
41
71
  }
42
72
 
43
- function handleModelChange(state: HandlerSessionState, event: unknown, ctx: ObservMeHandlerContext): void {
73
+ function handleModelChange(
74
+ state: HandlerSessionState,
75
+ event: PiEvent<"model_select">,
76
+ ctx: ExtensionContext,
77
+ ): void {
44
78
  const session = state.session;
45
79
  if (!session) return;
46
80
 
@@ -55,19 +89,19 @@ function handleModelChange(state: HandlerSessionState, event: unknown, ctx: Obse
55
89
  emitStructuredLog(session.logger, LOG_EVENT_NAMES.MODEL_CHANGED, "model", attributes);
56
90
  }
57
91
 
58
- function createThinkingLevelChangeHandler(state: HandlerSessionState): Handler {
92
+ function createThinkingLevelChangeHandler(state: HandlerSessionState): PiHandler<"thinking_level_select"> {
59
93
  return handleThinkingLevelChange.bind(undefined, state);
60
94
  }
61
95
 
62
96
  function handleThinkingLevelChange(
63
97
  state: HandlerSessionState,
64
- event: unknown,
65
- ctx: ObservMeHandlerContext,
98
+ event: PiEvent<"thinking_level_select">,
99
+ _ctx: ExtensionContext,
66
100
  ): void {
67
101
  const session = state.session;
68
102
  if (!session) return;
69
103
 
70
- const attributes = buildThinkingLevelChangeAttributes(event, ctx, session);
104
+ const attributes = buildThinkingLevelChangeAttributes(event, session);
71
105
 
72
106
  updateCurrentSessionAttributes(session, attributes, [SESSION_ATTRIBUTES.PI_THINKING_LEVEL_CURRENT]);
73
107
  session.metrics.thinkingLevelChanges.add(1, thinkingLevelChangeMetricLabels(session));
@@ -75,14 +109,14 @@ function handleThinkingLevelChange(
75
109
  emitStructuredLog(session.logger, LOG_EVENT_NAMES.THINKING_CHANGED, "thinking", attributes);
76
110
  }
77
111
 
78
- function createSessionBeforeTreeHandler(state: HandlerSessionState): Handler {
112
+ function createSessionBeforeTreeHandler(state: HandlerSessionState): PiHandler<"session_before_tree"> {
79
113
  return handleSessionBeforeTree.bind(undefined, state);
80
114
  }
81
115
 
82
116
  function handleSessionBeforeTree(
83
117
  state: HandlerSessionState,
84
- event: unknown,
85
- _ctx: ObservMeHandlerContext,
118
+ event: SessionBeforeTreeEvent,
119
+ _ctx: ExtensionContext,
86
120
  ): void {
87
121
  const session = state.session;
88
122
  if (!session) return;
@@ -90,11 +124,11 @@ function handleSessionBeforeTree(
90
124
  session.currentBranchPreparation = buildBranchPreparationState(event, session.config);
91
125
  }
92
126
 
93
- function createBranchHandler(state: HandlerSessionState): Handler {
127
+ function createBranchHandler(state: HandlerSessionState): PiHandler<"session_tree"> {
94
128
  return handleBranch.bind(undefined, state);
95
129
  }
96
130
 
97
- function handleBranch(state: HandlerSessionState, event: unknown, _ctx: ObservMeHandlerContext): void {
131
+ function handleBranch(state: HandlerSessionState, event: SessionTreeEvent, _ctx: ExtensionContext): void {
98
132
  const session = state.session;
99
133
  if (!session) return;
100
134
 
@@ -105,7 +139,7 @@ function handleBranch(state: HandlerSessionState, event: unknown, _ctx: ObservMe
105
139
  }
106
140
  }
107
141
 
108
- function recordBranch(session: ObservMeTelemetrySession, event: unknown): void {
142
+ function recordBranch(session: ObservMeTelemetrySession, event: SessionTreeEvent): void {
109
143
  const attributes = buildBranchAttributes(event, session);
110
144
  const labels = metricLabels(session.config, session.lineage);
111
145
  const span = startActiveChildSpan(session, SPAN_NAMES.PI_BRANCH, resolveOperationParentSpan(session), attributes, "branch");
@@ -117,18 +151,18 @@ function recordBranch(session: ObservMeTelemetrySession, event: unknown): void {
117
151
  emitStructuredLog(session.logger, LOG_EVENT_NAMES.BRANCH_CREATED, "branch", attributes);
118
152
  }
119
153
 
120
- function createCompactionHandler(state: HandlerSessionState): Handler {
154
+ function createCompactionHandler(state: HandlerSessionState): PiHandler<"session_compact"> {
121
155
  return handleCompaction.bind(undefined, state);
122
156
  }
123
157
 
124
- function handleCompaction(state: HandlerSessionState, event: unknown, _ctx: ObservMeHandlerContext): void {
158
+ function handleCompaction(state: HandlerSessionState, event: SessionCompactEvent, _ctx: ExtensionContext): void {
125
159
  const session = state.session;
126
160
  if (!session) return;
127
161
 
128
162
  recordCompaction(session, event);
129
163
  }
130
164
 
131
- function recordCompaction(session: ObservMeTelemetrySession, event: unknown): void {
165
+ function recordCompaction(session: ObservMeTelemetrySession, event: SessionCompactEvent): void {
132
166
  const attributes = buildCompactionAttributes(event, session);
133
167
  const labels = metricLabels(session.config, session.lineage);
134
168
  const span = startActiveChildSpan(
@@ -1,4 +1,10 @@
1
1
  import { SpanStatusCode } from "@opentelemetry/api";
2
+ import type {
3
+ ExtensionContext,
4
+ ToolCallEvent,
5
+ ToolResultEvent,
6
+ UserBashEvent,
7
+ } from "@earendil-works/pi-coding-agent";
2
8
  import { TOOL_ATTRIBUTES } from "../../semconv/attributes.ts";
3
9
  import { LOG_EVENT_NAMES } from "../../semconv/metrics.ts";
4
10
  import { SPAN_NAMES } from "../../semconv/spans.ts";
@@ -42,11 +48,11 @@ import {
42
48
  import { monotonicNowMs } from "../handler-runtime.ts";
43
49
  import type { HandlerRegistrar } from "../handler-runtime.ts";
44
50
  import type {
45
- Handler,
46
51
  HandlerSessionState,
47
- ObservMeHandlerContext,
48
52
  ObservMeTelemetrySession,
49
53
  PendingBashOperationState,
54
+ PiEvent,
55
+ PiHandler,
50
56
  ToolCallState,
51
57
  } from "../handler-types.ts";
52
58
 
@@ -56,7 +62,6 @@ export function registerToolBashHandlers(registrar: HandlerRegistrar, state: Han
56
62
  registrar.add("tool_result", createToolResultHandler(state));
57
63
  registrar.add("tool_execution_end", createToolExecutionEndHandler(state));
58
64
  registrar.add("user_bash", createUserBashPreExecutionHandler(state));
59
- registrar.add("bashExecution", createBashExecutionHandler(state));
60
65
  }
61
66
 
62
67
  export function recordBashExecution(session: ObservMeTelemetrySession, event: unknown): void {
@@ -97,14 +102,14 @@ export function recordBashExecution(session: ObservMeTelemetrySession, event: un
97
102
  emitLifecycleLog(session.logger, LOG_EVENT_NAMES.BASH_COMPLETED, attributes, failed ? "ERROR" : "INFO");
98
103
  }
99
104
 
100
- function createToolExecutionStartHandler(state: HandlerSessionState): Handler {
105
+ function createToolExecutionStartHandler(state: HandlerSessionState): PiHandler<"tool_execution_start"> {
101
106
  return handleToolExecutionStart.bind(undefined, state);
102
107
  }
103
108
 
104
109
  function handleToolExecutionStart(
105
110
  state: HandlerSessionState,
106
- event: unknown,
107
- _ctx: ObservMeHandlerContext,
111
+ event: PiEvent<"tool_execution_start">,
112
+ _ctx: ExtensionContext,
108
113
  ): void {
109
114
  const session = state.session;
110
115
  if (!session) return;
@@ -114,11 +119,11 @@ function handleToolExecutionStart(
114
119
  recordOptionalToolArguments(session, toolState.span, event);
115
120
  }
116
121
 
117
- function createToolCallHandler(state: HandlerSessionState): Handler {
122
+ function createToolCallHandler(state: HandlerSessionState): PiHandler<"tool_call"> {
118
123
  return handleToolCall.bind(undefined, state);
119
124
  }
120
125
 
121
- function handleToolCall(state: HandlerSessionState, event: unknown, _ctx: ObservMeHandlerContext): void {
126
+ function handleToolCall(state: HandlerSessionState, event: ToolCallEvent, _ctx: ExtensionContext): void {
122
127
  const session = state.session;
123
128
  if (!session) return;
124
129
  if (dropAmbiguousToolLifecycleEvent(session, event, "tool_call")) return;
@@ -132,11 +137,11 @@ function handleToolCall(state: HandlerSessionState, event: unknown, _ctx: Observ
132
137
  recordOptionalToolArguments(session, toolState.span, event);
133
138
  }
134
139
 
135
- function createToolResultHandler(state: HandlerSessionState): Handler {
140
+ function createToolResultHandler(state: HandlerSessionState): PiHandler<"tool_result"> {
136
141
  return handleToolResult.bind(undefined, state);
137
142
  }
138
143
 
139
- function handleToolResult(state: HandlerSessionState, event: unknown, _ctx: ObservMeHandlerContext): void {
144
+ function handleToolResult(state: HandlerSessionState, event: ToolResultEvent, _ctx: ExtensionContext): void {
140
145
  const session = state.session;
141
146
  if (!session) return;
142
147
  if (dropAmbiguousToolLifecycleEvent(session, event, "tool_result")) return;
@@ -155,14 +160,14 @@ function captureToolResult(session: ObservMeTelemetrySession, toolState: ToolCal
155
160
  if (capturedResult) toolState.capturedResult = capturedResult;
156
161
  }
157
162
 
158
- function createToolExecutionEndHandler(state: HandlerSessionState): Handler {
163
+ function createToolExecutionEndHandler(state: HandlerSessionState): PiHandler<"tool_execution_end"> {
159
164
  return handleToolExecutionEnd.bind(undefined, state);
160
165
  }
161
166
 
162
167
  function handleToolExecutionEnd(
163
168
  state: HandlerSessionState,
164
- event: unknown,
165
- _ctx: ObservMeHandlerContext,
169
+ event: PiEvent<"tool_execution_end">,
170
+ _ctx: ExtensionContext,
166
171
  ): void {
167
172
  const session = state.session;
168
173
  if (!session) return;
@@ -202,14 +207,14 @@ function handleToolExecutionEnd(
202
207
  deleteCurrentToolCall(session, toolCallId);
203
208
  }
204
209
 
205
- function createUserBashPreExecutionHandler(state: HandlerSessionState): Handler {
210
+ function createUserBashPreExecutionHandler(state: HandlerSessionState): PiHandler<"user_bash"> {
206
211
  return handleUserBashPreExecution.bind(undefined, state);
207
212
  }
208
213
 
209
214
  function handleUserBashPreExecution(
210
215
  state: HandlerSessionState,
211
- event: unknown,
212
- _ctx: ObservMeHandlerContext,
216
+ event: UserBashEvent,
217
+ _ctx: ExtensionContext,
213
218
  ): void {
214
219
  const session = state.session;
215
220
  if (!session) return;
@@ -217,17 +222,6 @@ function handleUserBashPreExecution(
217
222
  startPendingUserBashOperation(session, event);
218
223
  }
219
224
 
220
- function createBashExecutionHandler(state: HandlerSessionState): Handler {
221
- return handleBashExecution.bind(undefined, state);
222
- }
223
-
224
- function handleBashExecution(state: HandlerSessionState, event: unknown, _ctx: ObservMeHandlerContext): void {
225
- const session = state.session;
226
- if (!session) return;
227
-
228
- recordBashExecution(session, event);
229
- }
230
-
231
225
  function dropAmbiguousToolLifecycleEvent(
232
226
  session: ObservMeTelemetrySession,
233
227
  event: unknown,
@@ -345,7 +345,7 @@ export function buildLlmRequestAttributes(
345
345
  [LLM_ATTRIBUTES.GEN_AI_REQUEST_MODEL]: model,
346
346
  [LLM_ATTRIBUTES.GEN_AI_CONVERSATION_ID]: resolveCurrentSessionId(session),
347
347
  [LLM_ATTRIBUTES.PI_LLM_API]: readString(payload, "api") ?? readString(ctx.model, "api") ?? provider,
348
- [LLM_ATTRIBUTES.PI_LLM_REQUEST_THINKING_LEVEL]: resolveSessionThinkingLevel(event, ctx, session),
348
+ [LLM_ATTRIBUTES.PI_LLM_REQUEST_THINKING_LEVEL]: resolveSessionThinkingLevel(event, session),
349
349
  [LLM_ATTRIBUTES.PI_LLM_REQUEST_MESSAGE_COUNT]: countPayloadItems(payload, ["messages", "contents", "input", "prompt"]),
350
350
  [LLM_ATTRIBUTES.PI_LLM_REQUEST_TOOL_SCHEMA_COUNT]: countPayloadItems(payload, ["tools", "toolSchemas"]),
351
351
  [LLM_ATTRIBUTES.PI_LLM_REQUEST_INPUT_CHARS]: safeJsonLength(payload),
@@ -1039,7 +1039,7 @@ export function buildModelChangeAttributes(event: unknown, ctx: ObservMeHandlerC
1039
1039
  });
1040
1040
  }
1041
1041
 
1042
- export function buildThinkingLevelChangeAttributes(event: unknown, ctx: ObservMeHandlerContext, session: ObservMeTelemetrySession): AttributeMap {
1042
+ export function buildThinkingLevelChangeAttributes(event: unknown, session: ObservMeTelemetrySession): AttributeMap {
1043
1043
  return withoutUndefinedAttributes({
1044
1044
  ...buildLineageMetricSafeLogAttributes(session),
1045
1045
  [COMMON_SPAN_ATTRIBUTES.PI_AGENT_RUN_ID]: session.currentAgentRunId,
@@ -1047,7 +1047,7 @@ export function buildThinkingLevelChangeAttributes(event: unknown, ctx: ObservMe
1047
1047
  [COMMON_SPAN_ATTRIBUTES.PI_ENTRY_ID]: readChangeEntryId(event, "thinking_level_change"),
1048
1048
  [COMMON_SPAN_ATTRIBUTES.PI_ENTRY_PARENT_ID]: readChangeEntryParentId(event, "thinking_level_change"),
1049
1049
  [COMMON_SPAN_ATTRIBUTES.PI_ENTRY_TYPE]: readChangeEntryType(event, "thinking_level_change"),
1050
- [SESSION_ATTRIBUTES.PI_THINKING_LEVEL_CURRENT]: resolveSessionThinkingLevel(event, ctx, session),
1050
+ [SESSION_ATTRIBUTES.PI_THINKING_LEVEL_CURRENT]: resolveSessionThinkingLevel(event, session),
1051
1051
  });
1052
1052
  }
1053
1053
 
@@ -1948,30 +1948,29 @@ export function extractThinkingText(value: unknown): string | undefined {
1948
1948
  }
1949
1949
 
1950
1950
  export function resolveSessionFilePath(event: unknown, ctx: ObservMeHandlerContext): string | undefined {
1951
- return readString(event, "sessionFile") ?? readString(event, "session_file") ?? readString(ctx, "sessionFile") ?? readString(ctx, "session_file");
1951
+ return ctx.sessionManager?.getSessionFile() ?? readString(event, "sessionFile") ?? readString(event, "session_file");
1952
1952
  }
1953
1953
 
1954
1954
  export function resolveSessionId(event: unknown, ctx: ObservMeHandlerContext, lineage: AgentLineageContext): string {
1955
1955
  return (
1956
+ ctx.sessionManager?.getSessionId() ??
1956
1957
  readString(event, "sessionId") ??
1957
1958
  readString(event, "session_id") ??
1958
1959
  readString(event, "id") ??
1959
- readString(ctx, "sessionId") ??
1960
- readString(ctx, "session_id") ??
1961
1960
  `session-${lineage.workflowId}`
1962
1961
  );
1963
1962
  }
1964
1963
 
1965
- export function resolveModelProvider(event: unknown, ctx: ObservMeHandlerContext): string {
1966
- return readModelProvider(event, ctx) ?? "unknown";
1964
+ export function resolveModelProvider(ctx: ObservMeHandlerContext): string {
1965
+ return readString(ctx.model, "provider") ?? "unknown";
1967
1966
  }
1968
1967
 
1969
- export function resolveModelId(event: unknown, ctx: ObservMeHandlerContext): string {
1970
- return readModelId(event, ctx) ?? "unknown";
1968
+ export function resolveModelId(ctx: ObservMeHandlerContext): string {
1969
+ return readString(ctx.model, "id") ?? "unknown";
1971
1970
  }
1972
1971
 
1973
- export function resolveThinkingLevel(event: unknown, ctx: ObservMeHandlerContext): string {
1974
- return readThinkingLevel(event, ctx) ?? "unknown";
1972
+ export function resolveThinkingLevel(thinkingLevel: unknown): string {
1973
+ return typeof thinkingLevel === "string" && thinkingLevel.trim() ? thinkingLevel.trim() : "unknown";
1975
1974
  }
1976
1975
 
1977
1976
  export function resolveSessionModelProvider(
@@ -1988,10 +1987,9 @@ export function resolveSessionModelId(event: unknown, ctx: ObservMeHandlerContex
1988
1987
 
1989
1988
  export function resolveSessionThinkingLevel(
1990
1989
  event: unknown,
1991
- ctx: ObservMeHandlerContext,
1992
1990
  session: ObservMeTelemetrySession,
1993
1991
  ): string {
1994
- return readThinkingLevel(event, ctx) ?? readString(session.sessionAttributes, SESSION_ATTRIBUTES.PI_THINKING_LEVEL_CURRENT) ?? "unknown";
1992
+ return readThinkingLevel(event) ?? readString(session.sessionAttributes, SESSION_ATTRIBUTES.PI_THINKING_LEVEL_CURRENT) ?? "unknown";
1995
1993
  }
1996
1994
 
1997
1995
  export function readModelProvider(event: unknown, ctx: ObservMeHandlerContext): string | undefined {
@@ -2004,9 +2002,7 @@ export function readModelProvider(event: unknown, ctx: ObservMeHandlerContext):
2004
2002
  readString(payload, "model_provider") ??
2005
2003
  readString(selectedModel, "provider") ??
2006
2004
  readString(selectedModel, "modelProvider") ??
2007
- readString(ctx.model, "provider") ??
2008
- readString(ctx, "modelProvider") ??
2009
- readString(ctx, "model_provider")
2005
+ readString(ctx.model, "provider")
2010
2006
  );
2011
2007
  }
2012
2008
 
@@ -2022,14 +2018,11 @@ export function readModelId(event: unknown, ctx: ObservMeHandlerContext): string
2022
2018
  readString(payload, "selection") ??
2023
2019
  readString(payload, "model") ??
2024
2020
  readModelIdFromObject(selectedModel) ??
2025
- readString(ctx.model, "id") ??
2026
- readString(ctx.model, "model") ??
2027
- readString(ctx, "modelId") ??
2028
- readString(ctx, "model_id")
2021
+ readString(ctx.model, "id")
2029
2022
  );
2030
2023
  }
2031
2024
 
2032
- export function readThinkingLevel(event: unknown, ctx: ObservMeHandlerContext): string | undefined {
2025
+ export function readThinkingLevel(event: unknown): string | undefined {
2033
2026
  const payload = readChangePayload(event, "thinking_level_change");
2034
2027
  const selectedThinking = readUnknown(payload, "thinking") ?? readUnknown(payload, "selection");
2035
2028
 
@@ -2038,10 +2031,7 @@ export function readThinkingLevel(event: unknown, ctx: ObservMeHandlerContext):
2038
2031
  readString(payload, "thinking_level") ??
2039
2032
  readString(payload, "level") ??
2040
2033
  readString(payload, "selection") ??
2041
- readString(selectedThinking, "level") ??
2042
- readString(ctx.thinking, "level") ??
2043
- readString(ctx, "thinkingLevel") ??
2044
- readString(ctx, "thinking_level")
2034
+ readString(selectedThinking, "level")
2045
2035
  );
2046
2036
  }
2047
2037