@senad-d/observme 0.1.0

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 (111) hide show
  1. package/.env.example +57 -0
  2. package/CHANGELOG.md +52 -0
  3. package/LICENSE +21 -0
  4. package/ObservMe-Production-Docs/00-README.md +79 -0
  5. package/ObservMe-Production-Docs/01-requirements-and-scope.md +207 -0
  6. package/ObservMe-Production-Docs/02-reference-architecture.md +306 -0
  7. package/ObservMe-Production-Docs/03-pi-event-and-session-model.md +266 -0
  8. package/ObservMe-Production-Docs/04-telemetry-semantic-conventions.md +722 -0
  9. package/ObservMe-Production-Docs/05-otel-pipeline-and-collector.md +355 -0
  10. package/ObservMe-Production-Docs/06-security-privacy-redaction.md +294 -0
  11. package/ObservMe-Production-Docs/07-extension-implementation-blueprint.md +447 -0
  12. package/ObservMe-Production-Docs/08-query-grafana-integration.md +276 -0
  13. package/ObservMe-Production-Docs/09-dashboards-alerts-slos.md +640 -0
  14. package/ObservMe-Production-Docs/10-testing-release-operations.md +319 -0
  15. package/ObservMe-Production-Docs/11-deployment-runbooks.md +203 -0
  16. package/ObservMe-Production-Docs/12-configuration-reference.md +337 -0
  17. package/ObservMe-Production-Docs/13-source-notes.md +33 -0
  18. package/ObservMe-Production-Docs/pi-session-format.md +427 -0
  19. package/README.md +356 -0
  20. package/SECURITY.md +45 -0
  21. package/dashboards/observme-agent-node-graphs.json +250 -0
  22. package/dashboards/observme-agents.json +1880 -0
  23. package/dashboards/observme-alerts.yaml +113 -0
  24. package/dashboards/observme-branches-compactions.json +1042 -0
  25. package/dashboards/observme-cost.json +1254 -0
  26. package/dashboards/observme-errors.json +1659 -0
  27. package/dashboards/observme-export-health.json +1802 -0
  28. package/dashboards/observme-latency.json +1494 -0
  29. package/dashboards/observme-llm-conversations.json +730 -0
  30. package/dashboards/observme-logs-llm.json +644 -0
  31. package/dashboards/observme-models.json +933 -0
  32. package/dashboards/observme-overview.json +2129 -0
  33. package/dashboards/observme-slo-health.json +737 -0
  34. package/dashboards/observme-slos.yaml +56 -0
  35. package/dashboards/observme-tools.json +902 -0
  36. package/dashboards/observme-trace-journey.json +1793 -0
  37. package/docs/STRUCTURE.md +49 -0
  38. package/docs/agent-subagent-observability-requirements.md +997 -0
  39. package/docs/compatibility-matrix.md +39 -0
  40. package/docs/configuration-tui-design-standard.md +767 -0
  41. package/docs/configuration.md +38 -0
  42. package/docs/review-validation.md +115 -0
  43. package/docs/validation-flow.md +117 -0
  44. package/examples/collector.yaml +123 -0
  45. package/examples/observme.yaml +131 -0
  46. package/img/demo.gif +0 -0
  47. package/img/icon.svg +47 -0
  48. package/package.json +103 -0
  49. package/src/commands/obs-agents-runtime.ts +150 -0
  50. package/src/commands/obs-agents.ts +491 -0
  51. package/src/commands/obs-args.ts +63 -0
  52. package/src/commands/obs-backfill.ts +1334 -0
  53. package/src/commands/obs-command-support.ts +43 -0
  54. package/src/commands/obs-cost.ts +228 -0
  55. package/src/commands/obs-diagnostics.ts +22 -0
  56. package/src/commands/obs-errors.ts +156 -0
  57. package/src/commands/obs-health.ts +301 -0
  58. package/src/commands/obs-link.ts +90 -0
  59. package/src/commands/obs-logs.ts +194 -0
  60. package/src/commands/obs-loki-summary.ts +184 -0
  61. package/src/commands/obs-session.ts +259 -0
  62. package/src/commands/obs-status.ts +359 -0
  63. package/src/commands/obs-tools.ts +274 -0
  64. package/src/commands/obs-trace.ts +411 -0
  65. package/src/commands/obs.ts +211 -0
  66. package/src/config/bootstrap-project-config.ts +300 -0
  67. package/src/config/defaults.ts +143 -0
  68. package/src/config/load-config.ts +631 -0
  69. package/src/config/project-paths.ts +61 -0
  70. package/src/config/schema.ts +405 -0
  71. package/src/config/validate.ts +456 -0
  72. package/src/constants.ts +4 -0
  73. package/src/diagnostics/sanitize.ts +6 -0
  74. package/src/extension.ts +38 -0
  75. package/src/otel/logs.ts +160 -0
  76. package/src/otel/metrics.ts +165 -0
  77. package/src/otel/otlp-endpoint.ts +10 -0
  78. package/src/otel/sdk.ts +114 -0
  79. package/src/otel/shutdown.ts +102 -0
  80. package/src/otel/traces.ts +166 -0
  81. package/src/pi/agent-lineage.ts +378 -0
  82. package/src/pi/agent-tree-tracker.ts +258 -0
  83. package/src/pi/event-handlers/agent-turn.ts +155 -0
  84. package/src/pi/event-handlers/lifecycle.ts +642 -0
  85. package/src/pi/event-handlers/llm.ts +115 -0
  86. package/src/pi/event-handlers/session-events.ts +159 -0
  87. package/src/pi/event-handlers/tool-bash.ts +275 -0
  88. package/src/pi/handler-internals.ts +2154 -0
  89. package/src/pi/handler-runtime.ts +633 -0
  90. package/src/pi/handler-types.ts +261 -0
  91. package/src/pi/handlers.ts +75 -0
  92. package/src/pi/subagent-spawn.ts +975 -0
  93. package/src/pi/subagent-types.ts +29 -0
  94. package/src/privacy/content-capture.ts +104 -0
  95. package/src/privacy/hash.ts +93 -0
  96. package/src/privacy/redact.ts +619 -0
  97. package/src/privacy/secret-patterns.ts +185 -0
  98. package/src/privacy/truncate.ts +69 -0
  99. package/src/query/grafana-readiness.ts +164 -0
  100. package/src/query/grafana-transport.ts +481 -0
  101. package/src/query/grafana.ts +371 -0
  102. package/src/query/loki.ts +332 -0
  103. package/src/query/prometheus.ts +388 -0
  104. package/src/query/tempo.ts +332 -0
  105. package/src/safety/sensitive-input.ts +208 -0
  106. package/src/semconv/attributes.ts +279 -0
  107. package/src/semconv/metrics.ts +146 -0
  108. package/src/semconv/spans.ts +19 -0
  109. package/src/semconv/values.ts +13 -0
  110. package/src/util/bounded-map.ts +97 -0
  111. package/tsconfig.json +15 -0
@@ -0,0 +1,722 @@
1
+ # ObservMe Telemetry Semantic Conventions
2
+
3
+ ## 1. Naming Strategy
4
+
5
+ ObservMe does not depend on OpenInference. It uses official OpenTelemetry GenAI semantic-convention attributes where they apply and its own namespaces for Pi-specific concepts.
6
+
7
+ Namespaces:
8
+
9
+ ```text
10
+ observme.* Extension/runtime attributes
11
+ pi.* Pi domain attributes, including `pi.workflow.*` and `pi.agent.*` for Pi workflow/agent/subagent lineage
12
+ gen_ai.* Official OpenTelemetry GenAI client/agent attributes and metrics where applicable
13
+ llm.* Legacy ObservMe aliases only; do not use as the sole representation for new telemetry
14
+ ```
15
+
16
+ Do not introduce a bare `agent.*` namespace for new telemetry. Use `pi.agent.*` for Pi runtime lineage and `gen_ai.agent.*` only where OpenTelemetry's GenAI agent attributes fit.
17
+
18
+ ObservMe-owned metric names use snake_case and are prefixed with `observme_`. Official GenAI metrics such as `gen_ai.client.token.usage` may also be emitted when the selected OpenTelemetry SDK/backend supports them. Because OTEL GenAI conventions are still evolving, ObservMe's `observme_*` metrics remain the stable product contract.
19
+
20
+ Span names use dotted lowercase operation names:
21
+
22
+ ```text
23
+ pi.session
24
+ pi.agent.run
25
+ pi.agent.spawn
26
+ pi.agent.wait
27
+ pi.agent.join
28
+ pi.turn
29
+ pi.llm.request
30
+ pi.tool.call
31
+ pi.bash.execution
32
+ pi.compaction
33
+ pi.branch
34
+ pi.model.change
35
+ pi.thinking.change
36
+ ```
37
+
38
+ ## 2. Resource Attributes
39
+
40
+ Set once at SDK initialization.
41
+
42
+ ```text
43
+ service.name = observme-pi-extension
44
+ service.namespace = pi
45
+ service.version = <extension version>
46
+ service.instance.id = <uuid per ObservMe telemetry session/process startup>
47
+ telemetry.sdk.name = opentelemetry # normally set by SDK
48
+ observme.version = <extension version>
49
+ observme.instance.id = <uuid per ObservMe telemetry session/process startup>
50
+ pi.agent.id = <uuid or trusted propagated id per logical agent runtime>
51
+ pi.agent.parent_id = <parent agent id>, optional high-cardinality resource attribute
52
+ pi.agent.root_id = <root agent id>, optional high-cardinality resource attribute
53
+ pi.agent.role = root|subagent
54
+ pi.agent.depth = 0 for root, 1+ for subagents
55
+ pi.agent.capability = optional trusted safe capability name
56
+ pi.cwd.hash = sha256(cwd)
57
+ pi.cwd.basename = basename(cwd), optional
58
+ deployment.environment.name = development|test|staging|production|custom
59
+ observme.environment = dev|ci|prod|unknown, optional compatibility alias
60
+ observme.tenant.id = optional tenant/routing id
61
+ pi.workflow.id = generated workflow/root execution id; high-cardinality resource/span/log attribute
62
+ pi.workflow.root_agent_id = root agent id for the workflow tree
63
+ pi.user.hash = optional stable user hash
64
+ pi.project.name = optional safe project name
65
+ ```
66
+
67
+ Never set raw `cwd` by default because paths often include usernames, project codenames, or customer names.
68
+
69
+ ## 3. Common Span Attributes
70
+
71
+ Every ObservMe span should include:
72
+
73
+ ```text
74
+ pi.session.id
75
+ pi.workflow.id
76
+ pi.workflow.root_agent_id
77
+ pi.agent.id
78
+ pi.agent.parent_id # if this runtime is a subagent
79
+ pi.agent.root_id
80
+ pi.agent.run.id # if inside an agent_start/agent_end lifecycle
81
+ pi.entry.id # if linked to an entry
82
+ pi.entry.parent_id # if linked to an entry
83
+ pi.entry.type # if linked to an entry
84
+ observme.capture.prompts # boolean
85
+ observme.capture.responses # boolean
86
+ observme.capture.tool_arguments # boolean
87
+ observme.redaction.enabled # boolean
88
+ observme.semconv.version # ObservMe convention version
89
+ observme.replayed # true for explicit replay/backfill telemetry
90
+ observme.evicted # true when span was closed by bounded-registry eviction
91
+ observme.truncated # true when content was truncated before export
92
+ observme.original_length # original character length when truncation occurred
93
+ ```
94
+
95
+ ## 4. Session Span
96
+
97
+ Span name:
98
+
99
+ ```text
100
+ pi.session
101
+ ```
102
+
103
+ Attributes:
104
+
105
+ ```text
106
+ pi.session.id
107
+ pi.session.name
108
+ pi.session.cwd_hash
109
+ pi.session.parent_session_hash
110
+ pi.session.persisted
111
+ pi.session.file_hash
112
+ pi.session.version
113
+ pi.model.provider.current
114
+ pi.model.id.current
115
+ pi.thinking.level.current
116
+ ```
117
+
118
+ Events:
119
+
120
+ ```text
121
+ session.started
122
+ session.named
123
+ session.shutdown
124
+ session.error
125
+ ```
126
+
127
+ Shutdown event/log attributes:
128
+
129
+ ```text
130
+ pi.workflow.duration_ms
131
+ pi.workflow.status # ok|error
132
+ ```
133
+
134
+ ## 5. Workflow, Agent, and Subagent Spans
135
+
136
+ Additional lineage attributes where known:
137
+
138
+ ```text
139
+ pi.agent.capability # optional trusted safe capability name
140
+ pi.agent.orphaned # true when parent lineage is incomplete
141
+ ```
142
+
143
+ ### 5.1 Agent Run Span
144
+
145
+ Pi emits `agent_start` and `agent_end` once per user prompt lifecycle. Represent that lifecycle with a child span under `pi.session`.
146
+
147
+ Span name:
148
+
149
+ ```text
150
+ pi.agent.run
151
+ ```
152
+
153
+ Attributes:
154
+
155
+ ```text
156
+ pi.agent.id
157
+ pi.agent.parent_id # optional
158
+ pi.agent.root_id
159
+ pi.agent.role # root|subagent
160
+ pi.agent.depth
161
+ pi.agent.run.id
162
+ pi.agent.run.index
163
+ pi.agent.run.source # user|rpc|extension|unknown when known
164
+ pi.agent.run.prompt.hash
165
+ pi.agent.run.prompt.length
166
+ ```
167
+
168
+ Official GenAI attributes may be mirrored when they describe the Pi agent rather than a provider-side assistant:
169
+
170
+ ```text
171
+ gen_ai.agent.id # same as pi.agent.id, span/log attribute only
172
+ gen_ai.agent.name # optional configured safe name
173
+ gen_ai.agent.version # ObservMe/Pi agent version if meaningful
174
+ ```
175
+
176
+ ### 5.2 Subagent Spawn Span
177
+
178
+ When a tool or extension launches another Pi agent, emit a spawn span around the parent operation.
179
+
180
+ Span name:
181
+
182
+ ```text
183
+ pi.agent.spawn
184
+ ```
185
+
186
+ Attributes:
187
+
188
+ ```text
189
+ pi.agent.spawn.id
190
+ pi.agent.spawn.type # tool|command|extension|unknown
191
+ pi.agent.spawn.reason # delegated_task|parallel_search|review|tool_wrapper|unknown
192
+ pi.agent.spawn.outcome # started|failed|cancelled|timeout
193
+ pi.agent.spawn.tool_call_id # if spawned from a tool call
194
+ pi.agent.spawn.command.hash # if spawned by a command; never raw by default
195
+ pi.agent.child.id # if known after spawn
196
+ pi.agent.child.count # current/observed child count for the parent operation
197
+ pi.agent.children.active # active children at observation time
198
+ pi.agent.parent_id # current pi.agent.id
199
+ pi.agent.root_id
200
+ pi.workflow.id
201
+ pi.workflow.root_agent_id
202
+ pi.agent.depth
203
+ pi.session.id
204
+ pi.agent.spawn.trace_context_propagated
205
+ ```
206
+
207
+ If W3C trace context is propagated to the child, the child `pi.session` span should continue the trace. If not, link the child trace back using span links/log attributes and the lineage fields above.
208
+
209
+ ### 5.3 Agent Wait and Join Spans
210
+
211
+ When a parent agent waits for a child or collects child results, emit wait/join spans or span events so the multi-agent critical path is visible.
212
+
213
+ Span names:
214
+
215
+ ```text
216
+ pi.agent.wait
217
+ pi.agent.join
218
+ ```
219
+
220
+ Attributes:
221
+
222
+ ```text
223
+ pi.workflow.id
224
+ pi.agent.id
225
+ pi.agent.child.id # high-cardinality span/log attribute only
226
+ pi.agent.spawn.id # high-cardinality span/log attribute only
227
+ pi.agent.wait.reason # dependency|rate_limit|child_running|unknown
228
+ pi.agent.join.status # ok|error|cancelled|timeout|partial|unknown
229
+ pi.agent.child.status # ok|error|cancelled|timeout|unknown
230
+ pi.agent.failure.propagated # true if child failure failed the parent operation
231
+ pi.agent.children.active
232
+ pi.agent.child.count
233
+ ```
234
+
235
+ ## 6. Turn Span
236
+
237
+ Span name:
238
+
239
+ ```text
240
+ pi.turn
241
+ ```
242
+
243
+ Attributes:
244
+
245
+ ```text
246
+ pi.turn.id
247
+ pi.turn.index
248
+ pi.turn.branch_path_hash
249
+ pi.turn.user_message.hash
250
+ pi.turn.user_message.length
251
+ pi.turn.user_message.image_count
252
+ pi.model.provider.current
253
+ pi.model.id.current
254
+ ```
255
+
256
+ Status:
257
+
258
+ - OK if completed without error
259
+ - ERROR if LLM/tool fatal error ends the turn
260
+ - UNSET if interrupted or unknown
261
+
262
+ ## 7. LLM / GenAI Request Span
263
+
264
+ Span name:
265
+
266
+ ```text
267
+ pi.llm.request
268
+ ```
269
+
270
+ For backend interoperability, also follow the OpenTelemetry GenAI client-span guidance where practical: the provider call span may be named `{gen_ai.operation.name} {gen_ai.request.model}` (for example `chat claude-sonnet-4-5`) or kept as `pi.llm.request` with the official `gen_ai.*` attributes below. If both an internal Pi wrapper span and a provider client span are emitted, make the provider span a child of the turn span and avoid double-counting metrics.
271
+
272
+ Required or recommended official GenAI attributes:
273
+
274
+ ```text
275
+ gen_ai.operation.name # chat|generate_content|text_completion
276
+ gen_ai.provider.name
277
+ gen_ai.request.model
278
+ gen_ai.response.model # if provider reports a different response model
279
+ gen_ai.response.id # if available; high-cardinality span/log attribute only
280
+ gen_ai.response.finish_reasons # map Pi stopReason to array where possible
281
+ gen_ai.request.temperature
282
+ gen_ai.request.max_tokens
283
+ gen_ai.usage.input_tokens
284
+ gen_ai.usage.output_tokens
285
+ gen_ai.usage.cache_read.input_tokens
286
+ gen_ai.usage.cache_creation.input_tokens
287
+ gen_ai.usage.reasoning.output_tokens # if provider reports reasoning/thinking tokens
288
+ gen_ai.conversation.id # pi.session.id when safe to expose in spans/logs
289
+ error.type # error class when the operation fails
290
+ ```
291
+
292
+ ObservMe/Pi-specific LLM attributes:
293
+
294
+ ```text
295
+ pi.llm.api
296
+ pi.llm.request.thinking_level
297
+ pi.llm.request.message_count
298
+ pi.llm.request.tool_schema_count
299
+ pi.llm.request.input_chars
300
+ pi.llm.request.id # in-process provider request correlation
301
+ pi.llm.stop_reason
302
+ pi.llm.error_message_hash
303
+ pi.llm.usage.total_tokens
304
+ pi.llm.usage.cache_write_1h_tokens # Anthropic-specific when available
305
+ pi.llm.cost.input_usd
306
+ pi.llm.cost.output_usd
307
+ pi.llm.cost.cache_read_usd
308
+ pi.llm.cost.cache_write_usd
309
+ pi.llm.cost.total_usd
310
+ pi.llm.tool_call_count # replayed assistant-message tool-call count
311
+ ```
312
+
313
+ Optional content fields when enabled and redacted:
314
+
315
+ ```text
316
+ gen_ai.input.messages # only if explicit content capture is enabled
317
+ gen_ai.output.messages # only if explicit content capture is enabled
318
+ pi.llm.prompt.redacted
319
+ pi.llm.response.redacted
320
+ pi.llm.thinking.redacted
321
+ pi.llm.content.kind # log attribute: prompt|response|thinking
322
+ ```
323
+
324
+ When prompt, response, or thinking capture is explicitly enabled, ObservMe exports already-redacted content to both the LLM span attributes above and correlated OTEL logs. Keep capture disabled by default; for high-volume production deployments, prefer dashboards and retention policies that treat these opt-in attributes and log bodies as sensitive even after redaction.
325
+
326
+ ## 8. Tool Call Span
327
+
328
+ Span name:
329
+
330
+ ```text
331
+ pi.tool.call
332
+ ```
333
+
334
+ Attributes:
335
+
336
+ ```text
337
+ pi.tool.call.id
338
+ pi.tool.name
339
+ pi.tool.category # shell|filesystem|network|custom|unknown
340
+ pi.tool.arguments.hash
341
+ pi.tool.arguments.size
342
+ pi.tool.result.size
343
+ pi.tool.result.hash
344
+ pi.tool.success
345
+ pi.tool.error
346
+ pi.tool.error_class
347
+ gen_ai.tool.call.id # optional official alias for GenAI tool-call correlation
348
+ gen_ai.tool.name # optional official alias
349
+ gen_ai.tool.type # function|extension|datastore when applicable
350
+ ```
351
+
352
+ Optional:
353
+
354
+ ```text
355
+ pi.tool.arguments.redacted
356
+ pi.tool.result.redacted
357
+ gen_ai.tool.call.arguments # only when explicit content capture is enabled and redacted
358
+ gen_ai.tool.call.result # only when explicit content capture is enabled and redacted
359
+ ```
360
+
361
+ ## 9. Bash Execution Span
362
+
363
+ Span name:
364
+
365
+ ```text
366
+ pi.bash.execution
367
+ ```
368
+
369
+ Attributes:
370
+
371
+ ```text
372
+ pi.bash.command.hash
373
+ pi.bash.command.redacted # optional
374
+ pi.bash.exit_code
375
+ pi.bash.cancelled
376
+ pi.bash.truncated
377
+ pi.bash.output.size
378
+ pi.bash.output.hash
379
+ pi.bash.output.redacted # optional
380
+ pi.bash.full_output_path_present
381
+ pi.bash.exclude_from_context
382
+ ```
383
+
384
+ ## 10. Branch Span
385
+
386
+ Span name:
387
+
388
+ ```text
389
+ pi.branch
390
+ ```
391
+
392
+ Attributes:
393
+
394
+ ```text
395
+ pi.branch.from_id
396
+ pi.branch.to_id
397
+ pi.branch.common_ancestor_id # if available
398
+ pi.branch.path_hash
399
+ pi.leaf.id
400
+ pi.branch.summary.hash
401
+ pi.branch.summary.length
402
+ pi.branch.from_hook
403
+ pi.branch.read_files_count
404
+ pi.branch.modified_files_count
405
+ ```
406
+
407
+ ## 11. Compaction Span
408
+
409
+ Span name:
410
+
411
+ ```text
412
+ pi.compaction
413
+ ```
414
+
415
+ Attributes:
416
+
417
+ ```text
418
+ pi.compaction.first_kept_entry_id
419
+ pi.compaction.tokens_before
420
+ pi.compaction.summary.hash
421
+ pi.compaction.summary.length
422
+ pi.compaction.from_hook
423
+ pi.compaction.reason
424
+ pi.compaction.will_retry
425
+ pi.compaction.read_files_count
426
+ pi.compaction.modified_files_count
427
+ ```
428
+
429
+ ## 12. Metrics
430
+
431
+ ### 12.1 Counters
432
+
433
+ ```text
434
+ observme_sessions_started_total
435
+ observme_sessions_shutdown_total
436
+ observme_workflows_started_total
437
+ observme_workflows_completed_total
438
+ observme_workflow_errors_total
439
+ observme_agent_runs_total
440
+ observme_agent_run_errors_total
441
+ observme_subagents_spawned_total
442
+ observme_subagent_spawn_failures_total
443
+ observme_orphan_agents_total
444
+ observme_trace_context_propagation_failures_total
445
+ observme_child_agent_failures_total
446
+ observme_parent_recovered_from_child_failure_total
447
+ observme_turns_started_total
448
+ observme_turns_completed_total
449
+ observme_llm_requests_total
450
+ observme_llm_errors_total
451
+ observme_tool_calls_total
452
+ observme_tool_failures_total
453
+ observme_bash_executions_total
454
+ observme_bash_failures_total
455
+ observme_model_changes_total
456
+ observme_thinking_level_changes_total
457
+ observme_compactions_total
458
+ observme_branches_total
459
+ observme_telemetry_dropped_total
460
+ observme_export_errors_total
461
+ observme_redaction_failures_total
462
+ observme_events_observed_total
463
+ observme_handler_errors_total
464
+ ```
465
+
466
+ ### 12.2 Token and Cost Counters
467
+
468
+ ```text
469
+ observme_llm_input_tokens_total
470
+ observme_llm_output_tokens_total
471
+ observme_llm_cache_read_tokens_total
472
+ observme_llm_cache_write_tokens_total
473
+ observme_llm_cache_write_1h_tokens_total
474
+ observme_llm_reasoning_tokens_total
475
+ observme_llm_total_tokens_total
476
+ observme_llm_cost_usd_total
477
+ ```
478
+
479
+ ### 12.3 Histograms
480
+
481
+ ```text
482
+ observme_workflow_duration_ms
483
+ observme_agent_run_duration_ms
484
+ observme_agent_lifetime_duration_ms
485
+ observme_subagent_spawn_duration_ms
486
+ observme_agent_wait_duration_ms
487
+ observme_agent_join_duration_ms
488
+ observme_agent_tree_depth
489
+ observme_agent_tree_width
490
+ observme_agent_fanout_count
491
+ observme_turn_duration_ms
492
+ observme_llm_request_duration_ms
493
+ observme_tool_duration_ms
494
+ observme_bash_duration_ms
495
+ observme_compaction_tokens_before
496
+ observme_prompt_size_chars
497
+ observme_response_size_chars
498
+ observme_tool_result_size_chars
499
+ observme_handler_duration_ms
500
+ ```
501
+
502
+ ### 12.4 Gauges
503
+
504
+ ```text
505
+ observme_active_spans
506
+ observme_active_agents
507
+ ```
508
+
509
+ ### 12.5 Lifecycle recording points
510
+
511
+ - Record `observme_tool_result_size_chars` once from the finalized `tool_execution_end` result, using the same bounded tool labels as tool-call metrics.
512
+ - Increment `observme_agent_run_errors_total` only when an ended agent run is classified as failed.
513
+ - Record `observme_workflow_duration_ms` once for a root workflow at shutdown from its stored start time, with bounded `status=ok|error`.
514
+ - Record `observme_subagent_spawn_duration_ms` from spawn start through either launcher completion or launcher failure.
515
+ - Increment `observme_child_agent_failures_total` when an actual child completion/join reports failed status; a launcher failure before child creation is only a spawn failure.
516
+ - Increment `observme_parent_recovered_from_child_failure_total` once when a failed child is joined with `failurePropagated=false` and parent recovery is confirmed.
517
+ - Repeated child completion/join observations use bounded deduplication state and cannot increment the same failure or recovery transition twice.
518
+
519
+ ### 12.6 Export Health self-observability contract
520
+
521
+ The `ObservMe Export Health` dashboard is a self-observability contract for the ObservMe runtime, not a replacement for `/obs status` or `/obs health`. A healthy local session should still produce positive liveness signals while failure-only signals remain at zero or no matching log rows.
522
+
523
+ Dashboard-driving signals:
524
+
525
+ ```text
526
+ observme_events_observed_total # counter; one handled Pi event observed by a live telemetry session
527
+ observme_handler_duration_ms # histogram; handler lifecycle latency
528
+ observme_handler_errors_total # counter; safe-handler failures
529
+ observme_active_spans # gauge; active SDK spans by bounded operation
530
+ observme_telemetry_dropped_total # counter; local queue/registry drops by bounded reason
531
+ observme_export_errors_total # counter; exporter failures by bounded reason/error class
532
+ observme_redaction_failures_total # counter; dropped fields caused by redaction exceptions
533
+ ```
534
+
535
+ Export Health Loki events:
536
+
537
+ ```text
538
+ telemetry.dropped # emitted with telemetry-drop counter updates
539
+ redaction.failed # emitted with redaction-failure counter updates
540
+ export.failed # emitted with export-error counter updates
541
+ trace_context.propagation_failed # emitted when subagent trace propagation fails
542
+ handler.failed # emitted when safe handler isolation catches an exception
543
+ ```
544
+
545
+ Healthy-state semantics:
546
+
547
+ - `observme_events_observed_total` is the primary liveness denominator for the dashboard and Observability Export SLO.
548
+ - Failure counters should be queryable as zero when no matching failures happened in the selected range.
549
+ - Failure log tables can be empty in a healthy range; empty `telemetry.dropped`, `redaction.failed`, `export.failed`, and `trace_context.propagation_failed` tables mean no matching failures were observed, not that ingestion is broken.
550
+ - Collector/export health is considered healthy when events are observed and local drop/export-error rates remain zero.
551
+
552
+ Metric labels for these signals are restricted to the low-cardinality allowlist below. In practice, Export Health metrics may use only `operation`, `reason`, `error_class`, and `status` as needed. High-cardinality identifiers such as session, workflow, agent, trace, span, entry, prompt, raw path, raw command, and raw error-message values must stay out of Prometheus labels.
553
+
554
+ This telemetry contract must not require changing project trust behavior, `/obs status`, `/obs health`, the configured local OTLP endpoint, Grafana authentication/profile, or intentionally enabled local debug capture settings.
555
+
556
+ ### 12.7 Optional official GenAI metrics
557
+
558
+ Emit these in addition to ObservMe metrics only when the SDK/backend path handles dotted OTEL metric names correctly:
559
+
560
+ ```text
561
+ gen_ai.client.token.usage # histogram, unit {token}, attribute gen_ai.token.type=input|output
562
+ gen_ai.client.operation.duration # histogram, unit s
563
+ ```
564
+
565
+ ## 13. Metric Labels
566
+
567
+ Allowed low-cardinality labels:
568
+
569
+ ```text
570
+ provider
571
+ model
572
+ tool_name
573
+ tool_category
574
+ environment
575
+ operation
576
+ status
577
+ error_class
578
+ reason # bounded enum only, e.g. span_registry_full|export_timeout
579
+ agent_role # root|subagent|orchestrator|worker|reviewer|unknown
580
+ agent_capability # bounded configured capability enum/string
581
+ subagent_depth # bounded bucket or small integer
582
+ spawn_type # tool|command|extension|unknown
583
+ spawn_reason # delegated_task|parallel_search|review|tool_wrapper|unknown
584
+ pi_version
585
+ observme_version
586
+ token_type # Prometheus-normalized alias for gen_ai.token.type when applicable
587
+ ```
588
+
589
+ Forbidden high-cardinality labels:
590
+
591
+ ```text
592
+ session_id
593
+ workflow_id
594
+ workflow_root_agent_id
595
+ agent_id
596
+ parent_agent_id
597
+ child_agent_id
598
+ agent_run_id
599
+ spawn_id
600
+ spawn_tool_call_id
601
+ trace_id
602
+ span_id
603
+ entry_id
604
+ tool_call_id
605
+ raw_command
606
+ raw_prompt
607
+ raw_path
608
+ raw_error_message
609
+ ```
610
+
611
+ High-cardinality values belong in span attributes or logs, not metrics. If a backend/exporter converts resource attributes into metric labels, drop `pi.workflow.*`, `pi.agent.*`, `pi.session.id`, trace IDs, span IDs, and spawn IDs from the metrics pipeline first. Generated `service.instance.id` / `observme.instance.id` may remain as backend labels when needed to keep concurrent metric streams distinct; dashboard PromQL must aggregate over them instead of grouping by them.
612
+
613
+ ## 14. Logs
614
+
615
+ Log body should be a short event summary. Detailed data goes into structured OTEL log attributes.
616
+
617
+ Common log attributes:
618
+
619
+ ```text
620
+ event.name
621
+ event.category
622
+ pi.session.id
623
+ pi.workflow.id
624
+ pi.workflow.root_agent_id
625
+ pi.agent.id
626
+ pi.agent.parent_id
627
+ pi.agent.root_id
628
+ pi.agent.run.id
629
+ pi.turn.id
630
+ trace_id
631
+ span_id
632
+ severity
633
+ error.type
634
+ observme.config.source
635
+ observme.config.rejection.issue_codes
636
+ observme.config.rejection.issue_count
637
+ ```
638
+
639
+ Configuration rejection diagnostics use only the bounded effective source, normalized issue codes, and aggregate issue count. They never include rejected values, raw validation messages, paths, headers, environment values, or custom regular expressions.
640
+
641
+ Operational `tool.call.completed` and `tool.call.failed` logs include every available common session/workflow/agent/run/turn correlation field plus `pi.tool.call.id`, `pi.tool.name`, `pi.tool.category`, matching `trace_id`/`span_id`, success, and bounded `error.type` for failures. These operational logs never copy raw tool arguments, results, prompts, commands, paths, or error messages. When `capture.toolResults` is explicitly enabled and the capture policy succeeds, failed-tool output is emitted separately as the `tool.error.captured` log body with `event.category="tool_content"`; redaction remains enabled by default and capture failures emit no content.
642
+
643
+ Explicit replay logs may also include:
644
+
645
+ ```text
646
+ pi.message.role
647
+ pi.message.content_length
648
+ ```
649
+
650
+ Event names:
651
+
652
+ ```text
653
+ config.rejected
654
+ session.started
655
+ session.named
656
+ session.shutdown
657
+ session.error
658
+ session.duplicate_start
659
+ workflow.started
660
+ workflow.completed
661
+ workflow.failed
662
+ agent.run.started
663
+ agent.run.completed
664
+ agent.run.failed
665
+ agent.spawn.started
666
+ agent.spawn.completed
667
+ agent.spawn.failed
668
+ agent.wait.started
669
+ agent.wait.completed
670
+ agent.join.started
671
+ agent.join.completed
672
+ agent.orphaned
673
+ trace_context.propagation_failed
674
+ turn.started
675
+ turn.completed
676
+ llm.request.started
677
+ llm.request.completed
678
+ llm.request.failed
679
+ llm.prompt.captured
680
+ llm.response.captured
681
+ llm.thinking.captured
682
+ message.replayed
683
+ tool.call.started
684
+ tool.call.completed
685
+ tool.call.failed
686
+ tool.error.captured
687
+ bash.completed
688
+ model.changed
689
+ thinking.changed
690
+ branch.created
691
+ compaction.created
692
+ redaction.failed
693
+ export.failed
694
+ handler.failed
695
+ telemetry.dropped
696
+ ```
697
+
698
+ ## 15. Exemplars
699
+
700
+ Metrics that correspond to traces should include exemplars where supported:
701
+
702
+ - `observme_workflow_duration_ms`
703
+ - `observme_agent_run_duration_ms`
704
+ - `observme_agent_wait_duration_ms`
705
+ - `observme_agent_join_duration_ms`
706
+ - `observme_llm_request_duration_ms`
707
+ - `observme_tool_duration_ms`
708
+ - `observme_turn_duration_ms`
709
+ - `observme_llm_cost_usd_total`
710
+ - `gen_ai.client.operation.duration`
711
+
712
+ For Loki OTLP ingestion, remember that attribute names containing dots are normalized with underscores in Loki queries (for example `event.name` becomes `event_name`, and `pi.session.id` becomes `pi_session_id`).
713
+
714
+ ## 16. Versioning
715
+
716
+ Semantic conventions are versioned independently:
717
+
718
+ ```text
719
+ observme.semconv.version = 1.0.0
720
+ ```
721
+
722
+ Breaking changes require major version increments.