@senad-d/observme 0.1.3 → 0.1.5
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.
- package/.env.example +4 -0
- package/CHANGELOG.md +48 -0
- package/README.md +36 -11
- package/dashboards/observme-agent-node-graphs.json +5 -5
- package/dashboards/observme-agents.json +169 -4
- package/dashboards/observme-alerts.yaml +16 -3
- package/dashboards/observme-overview.json +6 -6
- package/dashboards/observme-trace-journey.json +4 -4
- package/docs/agent-subagent-observability-requirements.md +19 -10
- package/docs/compatibility-matrix.md +21 -4
- package/docs/configuration.md +7 -2
- package/docs/extension-integration.md +20 -13
- package/docs/reference/03-pi-event-and-session-model.md +6 -6
- package/docs/reference/04-telemetry-semantic-conventions.md +39 -1
- package/docs/reference/05-otel-pipeline-and-collector.md +23 -10
- package/docs/reference/06-security-privacy-redaction.md +13 -6
- package/docs/reference/08-query-grafana-integration.md +30 -7
- package/docs/reference/09-dashboards-alerts-slos.md +132 -3
- package/docs/reference/10-testing-release-operations.md +44 -14
- package/docs/reference/11-deployment-runbooks.md +82 -5
- package/docs/reference/12-configuration-reference.md +48 -5
- package/docs/review-validation.md +17 -0
- package/examples/README.md +7 -2
- package/examples/collector.yaml +8 -8
- package/examples/integrations/subagent-runner.ts +8 -5
- package/examples/observme.yaml +3 -2
- package/package.json +14 -4
- package/src/commands/obs-agents.ts +17 -12
- package/src/commands/obs-backfill.ts +2 -2
- package/src/commands/obs-command-support.ts +2 -1
- package/src/commands/obs-cost.ts +15 -7
- package/src/commands/obs-health.ts +22 -2
- package/src/commands/obs-loki-summary.ts +4 -8
- package/src/commands/obs-session.ts +35 -28
- package/src/commands/obs-status.ts +56 -13
- package/src/commands/obs-tools.ts +19 -8
- package/src/commands/obs-trace.ts +9 -0
- package/src/commands/obs.ts +6 -1
- package/src/config/bootstrap-project-config.ts +50 -32
- package/src/config/defaults.ts +1 -2
- package/src/config/load-config.ts +270 -81
- package/src/config/project-paths.ts +318 -6
- package/src/config/query-limits.ts +10 -0
- package/src/config/schema.ts +18 -8
- package/src/config/transport-security.ts +107 -0
- package/src/config/validate.ts +88 -12
- package/src/extension.ts +2 -12
- package/src/integration.ts +6 -2
- package/src/otel/logs.ts +24 -13
- package/src/otel/metrics.ts +24 -13
- package/src/otel/otlp-endpoint.ts +41 -2
- package/src/otel/otlp-http-options.ts +11 -0
- package/src/otel/sdk.ts +155 -9
- package/src/otel/shutdown.ts +39 -11
- package/src/otel/traces.ts +34 -16
- package/src/pi/active-agent-lease.ts +101 -0
- package/src/pi/agent-tree-tracker.ts +14 -1
- package/src/pi/compatibility.ts +121 -0
- package/src/pi/event-handlers/agent-turn.ts +16 -9
- package/src/pi/event-handlers/lifecycle.ts +312 -106
- package/src/pi/event-handlers/llm.ts +17 -9
- package/src/pi/event-handlers/session-events.ts +53 -19
- package/src/pi/event-handlers/tool-bash.ts +21 -27
- package/src/pi/handler-internals.ts +16 -26
- package/src/pi/handler-runtime.ts +159 -54
- package/src/pi/handler-types.ts +40 -17
- package/src/pi/handlers.ts +12 -1
- package/src/pi/integration-api.ts +27 -15
- package/src/pi/session-correlation.ts +167 -0
- package/src/pi/subagent-spawn.ts +164 -31
- package/src/privacy/redact.ts +574 -68
- package/src/privacy/secret-patterns.ts +6 -1
- package/src/query/grafana-readiness.ts +18 -2
- package/src/query/grafana-transport.ts +150 -27
- package/src/query/grafana-url.ts +36 -0
- package/src/query/grafana.ts +4 -136
- package/src/query/loki.ts +2 -4
- package/src/query/prometheus.ts +8 -10
- package/src/query/tempo.ts +2 -4
- package/src/query/trace-link.ts +186 -0
- package/src/safety/display-bounds.ts +84 -0
- package/src/semconv/metrics.ts +7 -0
- package/src/semconv/values.ts +2 -0
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
"type": "prometheus",
|
|
145
145
|
"uid": "prometheus"
|
|
146
146
|
},
|
|
147
|
-
"description": "
|
|
147
|
+
"description": "Canonical leased-active fleet total; Prometheus summary values are aggregate and are not filtered to the selected workflow. Clean shutdown reaches zero after export/scrape propagation; after a crash, SIGKILL, cancelled job, or runner loss, the leased total converges to zero within the default 60-second lease plus one Prometheus scrape even if raw claims stay cached. Zero can also mean healthy idle or a failed-closed lease, so check Export Health. /obs agents is current-process child state, not fleet liveness.",
|
|
148
148
|
"fieldConfig": {
|
|
149
149
|
"defaults": {
|
|
150
150
|
"color": {
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
"uid": "prometheus"
|
|
196
196
|
},
|
|
197
197
|
"editorMode": "code",
|
|
198
|
-
"expr": "
|
|
198
|
+
"expr": "sum(max by (observme_instance_id) ((observme_active_agents > 0) and on (observme_instance_id) ((observme_agent_lease_expires_unixtime_seconds > time()) and (observme_agent_lease_expires_unixtime_seconds <= time() + 305)))) or vector(0)",
|
|
199
199
|
"instant": true,
|
|
200
200
|
"range": false,
|
|
201
201
|
"refId": "A"
|
|
@@ -1005,7 +1005,7 @@
|
|
|
1005
1005
|
"type": "prometheus",
|
|
1006
1006
|
"uid": "prometheus"
|
|
1007
1007
|
},
|
|
1008
|
-
"description": "
|
|
1008
|
+
"description": "Aggregate fleet shape over time, not per-workflow truth: the leased-active role series is canonical current activity, while tree histograms summarize all matching telemetry without workflow or agent IDs as metric labels. Clean shutdown removes leased activity after export/scrape propagation; after an ungraceful exit, leased activity converges to zero within the default 60-second lease plus one Prometheus scrape while raw claims may remain cached. /obs agents is local current-process child state. Constant lines mirror the depth=5 and fan-out=20 alert thresholds.",
|
|
1009
1009
|
"fieldConfig": {
|
|
1010
1010
|
"defaults": {
|
|
1011
1011
|
"color": {
|
|
@@ -1083,7 +1083,7 @@
|
|
|
1083
1083
|
"uid": "prometheus"
|
|
1084
1084
|
},
|
|
1085
1085
|
"editorMode": "code",
|
|
1086
|
-
"expr": "sum(
|
|
1086
|
+
"expr": "sum by (agent_role) (max by (observme_instance_id, agent_role) ((observme_active_agents > 0) and on (observme_instance_id) ((observme_agent_lease_expires_unixtime_seconds > time()) and (observme_agent_lease_expires_unixtime_seconds <= time() + 305)))) or on() vector(0)",
|
|
1087
1087
|
"legendFormat": "active {{agent_role}}",
|
|
1088
1088
|
"range": true,
|
|
1089
1089
|
"refId": "A"
|
|
@@ -565,6 +565,7 @@ agent.run.failed
|
|
|
565
565
|
agent.spawn.started
|
|
566
566
|
agent.spawn.completed
|
|
567
567
|
agent.spawn.failed
|
|
568
|
+
agent.spawn.cancelled
|
|
568
569
|
agent.wait.started
|
|
569
570
|
agent.wait.completed
|
|
570
571
|
agent.join.started
|
|
@@ -647,27 +648,35 @@ histogram_quantile(0.95, sum(rate(observme_subagent_spawn_duration_ms_bucket[$__
|
|
|
647
648
|
|
|
648
649
|
```text
|
|
649
650
|
observme_active_agents
|
|
651
|
+
observme_agent_lease_expires_unixtime_seconds
|
|
650
652
|
observme_agent_tree_depth
|
|
651
653
|
observme_agent_tree_width
|
|
652
654
|
observme_agent_fanout_count
|
|
653
655
|
```
|
|
654
656
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
```text
|
|
658
|
-
agent_role
|
|
659
|
-
subagent_depth
|
|
660
|
-
```
|
|
657
|
+
The active claim and lease carry the bounded `agent_role` and `environment` dimensions plus generated resource-instance identity normalized by the Collector to `observme_instance_id`. They do not currently carry `subagent_depth`; never derive a replacement depth label from workflow, session, logical-agent, trace, span, or job-run IDs. The tree histograms retain their documented bounded tree dimensions.
|
|
661
658
|
|
|
662
659
|
Dashboard examples:
|
|
663
660
|
|
|
664
661
|
```promql
|
|
665
|
-
sum
|
|
662
|
+
sum by (agent_role) (
|
|
663
|
+
max by (observme_instance_id, agent_role) (
|
|
664
|
+
(observme_active_agents > 0)
|
|
665
|
+
and on (observme_instance_id)
|
|
666
|
+
(
|
|
667
|
+
(observme_agent_lease_expires_unixtime_seconds > time())
|
|
668
|
+
and
|
|
669
|
+
(observme_agent_lease_expires_unixtime_seconds <= time() + 305)
|
|
670
|
+
)
|
|
671
|
+
)
|
|
672
|
+
) or on() vector(0)
|
|
666
673
|
histogram_quantile(0.95, sum(rate(observme_agent_tree_depth_bucket[$__rate_interval])) by (subagent_depth, le))
|
|
667
674
|
histogram_quantile(0.95, sum(rate(observme_agent_tree_width_bucket[$__rate_interval])) by (subagent_depth, le))
|
|
668
675
|
histogram_quantile(0.95, sum(rate(observme_agent_fanout_count_bucket[$__rate_interval])) by (subagent_depth, le))
|
|
669
676
|
```
|
|
670
677
|
|
|
678
|
+
Use the canonical total, role, environment, diagnostics, and migration guidance in [`reference/09-dashboards-alerts-slos.md` §1.3](reference/09-dashboards-alerts-slos.md#13-active-agent-leased-state-contract); raw active-claim sums are not current-liveness queries after ungraceful termination.
|
|
679
|
+
|
|
671
680
|
### Lineage health metrics
|
|
672
681
|
|
|
673
682
|
```text
|
|
@@ -825,9 +834,9 @@ Minimum adapter behavior:
|
|
|
825
834
|
1. Request the API with `requestObservMeIntegration(pi)`.
|
|
826
835
|
2. Before spawning child Pi, call `startSubagent` with spawn type/reason and safe command metadata.
|
|
827
836
|
3. Pass the returned `env` into `child_process.spawn`.
|
|
828
|
-
4. On launcher
|
|
829
|
-
5.
|
|
830
|
-
6.
|
|
837
|
+
4. On launcher error before the child runs, call `failSubagent`.
|
|
838
|
+
5. Around blocking waits, call `startWait` and `endWait`.
|
|
839
|
+
6. When the child reaches a terminal state, call `completeSubagent` once with matching `completed`, `failed`, or `cancelled` status/outcome fields.
|
|
831
840
|
7. When child output is collected, call `startJoin` and `endJoin` with child status.
|
|
832
841
|
8. Ensure the child command loads ObservMe as an extension/package.
|
|
833
842
|
9. Ensure the child ObservMe runtime receives the complete propagated parent context.
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# ObservMe Compatibility Matrix
|
|
2
2
|
|
|
3
|
-
Last updated: 2026-07-
|
|
3
|
+
Last updated: 2026-07-14
|
|
4
4
|
|
|
5
|
-
This matrix records the ObservMe runtime and observability-stack versions
|
|
5
|
+
This matrix records the ObservMe runtime and observability-stack versions exercised or pinned for the current release line. A component is marked **validated** only when the named command actually exercised it; configured CI targets remain distinct from completed local or workflow evidence.
|
|
6
|
+
|
|
7
|
+
## Pi version policy
|
|
8
|
+
|
|
9
|
+
ObservMe 0.1.x supports `@earendil-works/pi-coding-agent` `>=0.80.5 <0.81.0`. The Pi-mandated core package peers remain in `peerDependencies` with `"*"`, while release development dependencies are pinned exactly to 0.80.6 and CI installs both the 0.80.5 minimum and the 0.80.6 release-resolved target explicitly. Extension startup checks Pi's exported `VERSION` and required APIs before registering any handler or command.
|
|
10
|
+
|
|
11
|
+
The 0.80.5 minimum is evidence-based: its exported extension contract includes `session_info_changed`, which ObservMe requires for live rename metadata; 0.80.2 does not. Pi's current extension overloads do not include `bashExecution`, `model_change`, or `thinking_level_change`. `bashExecution` is an `AgentMessage` role observed through `message_end`, while model and thinking changes are session entry types represented at runtime by `model_select` and `thinking_level_select`; ObservMe no longer registers those three non-event names.
|
|
6
12
|
|
|
7
13
|
## Validation record
|
|
8
14
|
|
|
@@ -12,12 +18,23 @@ This matrix records the ObservMe runtime and observability-stack versions that h
|
|
|
12
18
|
| 2026-07-07 | Local Docker 29.5.2 with Collector Contrib 0.104.0 | `npm run test:integration:collector` | Starts a local debug-exporter Collector container, exports representative ObservMe traces/metrics/logs, and asserts default-disabled content capture is absent. |
|
|
13
19
|
| 2026-07-07 | Local Docker 29.5.2 with the pinned Grafana stack | `npm run test:integration:grafana-stack` | Starts `observability-stack/` services, exports representative ObservMe telemetry, queries Tempo by trace ID and lineage attributes, queries Loki by session ID, queries Prometheus token totals, and validates Grafana dashboard provisioning imports. |
|
|
14
20
|
| Continuous integration target | GitHub Actions `ubuntu-latest`, Node.js 22.19.0 | `npm ci --ignore-scripts` then `npm run validate` | CI target from `.github/workflows/ci.yml`; record the workflow run URL here after each release validation. |
|
|
21
|
+
| Pi compatibility CI matrix (configured 2026-07-14) | GitHub Actions `ubuntu-latest`, Node.js 22.19.0; exact Pi 0.80.5 and 0.80.6 matrix targets | Exact Pi install, version assertion, then `npm run validate:pi-compatibility` | Compile-time event fixtures, compatibility diagnostics, package install, handler/lifecycle smoke, and a real Pi RPC runtime are exercised separately at the declared minimum and release-resolved versions. |
|
|
22
|
+
| 2026-07-14 | Local Node.js v26.0.0, npm 11.12.1; exact `@earendil-works/pi-coding-agent` and `@earendil-works/pi-ai` 0.80.5 | Exact version assertion then `npm run validate:pi-compatibility` | Passed typecheck, typed event contracts, compatibility diagnostics, packaged install, handler/lifecycle smoke, and real Pi RPC runtime at the declared minimum. |
|
|
23
|
+
| 2026-07-14 | Local Node.js v26.0.0, npm 11.12.1; exact `@earendil-works/pi-coding-agent` and `@earendil-works/pi-ai` 0.80.6 | Exact version assertion then `npm run validate:pi-compatibility` | Passed the same compatibility suite at the release-resolved version. |
|
|
24
|
+
| Lease cancellation CI profile (configured 2026-07-14) | GitHub-hosted Linux `ubuntu-latest` (exact `ImageOS`/`ImageVersion` recorded in the Actions runner log); Node.js 22.19.0; Collector Contrib 0.104.0; Prometheus 2.53.1; Grafana 11.1.0 | `npm run test:active-agent-lease:contracts` then `npm run test:integration:active-agent-lease` | The bounded `lease-cancellation` job validates clean shutdown, `SIGTERM`, and cancellation-oriented `SIGKILL` convergence against the pinned Collector and Prometheus without secrets or a Grafana dependency. Grafana 11.1.0 is the recorded dashboard compatibility baseline validated separately by `npm run test:integration:grafana-stack`. `if: always()` cleanup is best effort; lease expiry is the asserted correctness mechanism. |
|
|
25
|
+
| 2026-07-14 | Local Node.js v26.0.0, npm 11.12.1 | `npm run validate` | Passed 493 unit/contract tests plus typecheck, ESLint, formatting, script, package-content, packaged-install, handler, Pi lifecycle, and Pi runtime checks for the lease release candidate. |
|
|
26
|
+
| 2026-07-14 | Local Docker 29.5.2, Compose 5.1.4, Collector Contrib 0.104.0 | `npm run test:integration:collector`; Compose config; pinned Collector `validate` | Passed representative OTLP export, Compose interpolation, and the shipped Collector config validation. |
|
|
27
|
+
| 2026-07-14 | Local Docker 29.5.2, Collector Contrib 0.104.0, Prometheus 2.53.1 | `npm run test:integration:active-agent-lease` | Passed clean shutdown, `SIGTERM`, and cancellation-oriented `SIGKILL`; the raw claim remained cached while leased activity converged without Collector restart. Labeled containers and the network were absent after cleanup. |
|
|
28
|
+
| 2026-07-14 | Local Docker 29.5.2 with the pinned Grafana stack | `npm run test:integration:grafana-stack` | Unrelated pre-existing broad-stack blocker remains: this run timed out with an empty Prometheus total-token vector. That token path does not validate active-agent leases. Cleanup removed all integration containers and networks; focused lease evidence is unaffected. |
|
|
29
|
+
| 2026-07-14 | npm package dry run for `@senad-d/observme` 0.1.3 | `npm run pack:dry-run` | Passed with 124 files; metric source, dashboards, alerts, examples, target docs, and `skills/observme-docs/SKILL.md` were present. |
|
|
30
|
+
|
|
31
|
+
Active-agent lease interpretation additionally requires producer and Prometheus wall clocks to remain within 5 seconds. GitHub-hosted runners satisfy this expectation. Self-hosted runners are supported only with reliable NTP or equivalent synchronization; clock health must be monitored separately from the ObservMe version matrix.
|
|
15
32
|
|
|
16
33
|
## Runtime and library matrix
|
|
17
34
|
|
|
18
35
|
| Component | Version tested or pinned | Source of truth | Status | Evidence and notes |
|
|
19
36
|
| --- | --- | --- | --- | --- |
|
|
20
|
-
| Pi package API | `@earendil-works/pi-coding-agent` 0.80.
|
|
37
|
+
| Pi package API | Supported: `@earendil-works/pi-coding-agent` >=0.80.5 <0.81.0; release-resolved `@earendil-works/pi-coding-agent` 0.80.6 and `@earendil-works/pi-ai` 0.80.6 | `package.json` `observmeCompatibility`, exact dev dependencies, `package-lock.json`, `.github/workflows/ci.yml` | Validated locally at minimum and release-resolved versions; exact CI matrix configured | `npm run validate:pi-compatibility` typechecks exported event contracts, tests pre-registration diagnostics, installs the package, runs handler/lifecycle smoke, and launches the real Pi RPC runtime. |
|
|
21
38
|
| Node.js | Local: 26.0.0; CI target/minimum: 22.19.0 | `node --version`, `package.json` `engines.node`, `.github/workflows/ci.yml` | Validated locally for 26.0.0; CI target pinned at 22.19.0 | Local validation ran on 26.0.0. CI is configured to validate on 22.19.0, which is also the minimum supported Node.js version. |
|
|
22
39
|
| OpenTelemetry JS package set | `@opentelemetry/api` 1.9.1; `@opentelemetry/api-logs` 0.220.0; OTLP proto exporters 0.220.0; `@opentelemetry/resources` 2.9.0; `@opentelemetry/sdk-logs` 0.220.0; `@opentelemetry/sdk-metrics` 2.9.0; `@opentelemetry/sdk-trace-base` 2.9.0; `@opentelemetry/sdk-trace-node` 2.9.0 | `package.json`, `package-lock.json` | Validated locally | `npm run validate` includes the OTEL lifecycle/exporter unit tests under `test/otel-*.test.mjs`. |
|
|
23
40
|
|
|
@@ -33,7 +50,7 @@ This matrix records the ObservMe runtime and observability-stack versions that h
|
|
|
33
50
|
|
|
34
51
|
## Update rules
|
|
35
52
|
|
|
36
|
-
- Update this file whenever `package-lock.json`, `.github/workflows/ci.yml`, or `observability-stack/docker-compose.yml` changes a tracked version.
|
|
53
|
+
- Update this file whenever `package.json` `observmeCompatibility`, `package-lock.json`, `.github/workflows/ci.yml`, or `observability-stack/docker-compose.yml` changes a tracked version.
|
|
37
54
|
- Add a new validation-record row whenever a release candidate is validated locally, in CI, or against the Docker Compose stack.
|
|
38
55
|
- Do not mark backend components as validated unless a command actually started or queried that component.
|
|
39
56
|
- Keep workflow IDs, session IDs, agent IDs, trace IDs, and other high-cardinality values out of this document.
|
package/docs/configuration.md
CHANGED
|
@@ -18,12 +18,15 @@ The standard Pi distribution currently resolves this to `.pi/observme.yaml`. Obs
|
|
|
18
18
|
|
|
19
19
|
Edit the project config file (`.pi/observme.yaml` in the standard distribution) where you run Pi:
|
|
20
20
|
|
|
21
|
-
- `otlp.endpoint` and `otlp.signalEndpoints` —
|
|
21
|
+
- `otlp.endpoint` and `otlp.signalEndpoints` — absolute HTTP(S) OpenTelemetry Collector URLs. Base endpoints may include an intentional path; ObservMe appends one `/v1/{signal}` suffix with URL pathname semantics. Signal-specific endpoints must already end in the matching signal path. Keep credentials in `otlp.headers`; endpoint userinfo, unresolved placeholders, queries, and fragments are rejected.
|
|
22
22
|
- `resource.attributes` — service name, project name, tenant, and deployment environment labels.
|
|
23
23
|
- `capture` — whether prompts, responses, thinking, tool data, bash data, and file paths are exported. For local debugging, set only the specific fields you need to `true`.
|
|
24
24
|
- `privacy` — redaction, unsafe-capture acknowledgement, insecure transport, hash salt env var, and path handling. Keep `redactionEnabled: true`; set `allowUnsafeCapture: true` only when you intentionally accept unredacted sensitive-content export from this trusted project. Live telemetry and `/obs backfill` use the same policy: disabled capture omits content, enabled redaction redacts then truncates, redaction failures drop content, and `redactionEnabled: false` with `allowUnsafeCapture: true` exports raw truncated content.
|
|
25
25
|
- `query.grafana` — Grafana URL, datasource UIDs, TLS, and IPv4 transport settings for `/obs` query commands.
|
|
26
|
-
- `query.links.traceUrlTemplate` — the Grafana Explore trace-link template used by `/obs trace
|
|
26
|
+
- `query.links.traceUrlTemplate` — the canonical Grafana Explore trace-link template used by `/obs session`, `/obs trace`, and `/obs link`; use `{traceId}`, `{{traceId}}`, `${traceId}`, or `%TRACE_ID%`, or keep the generated `...` structured fallback.
|
|
27
|
+
- `metrics.activeAgentLeaseDurationMillis` — how long the last exported active-agent lease remains valid. The default is `60000` ms, the supported range is `10000`–`300000` ms, and the value must be at least `(2 * metrics.exportIntervalMillis) + 5000` ms. `OBSERVME_ACTIVE_AGENT_LEASE_DURATION_MS` overrides YAML through the normal precedence rules.
|
|
28
|
+
|
|
29
|
+
The shipped dashboards and alerts combine a positive `observme_active_agents` lifecycle claim with a current lease; raw active-claim sums are diagnostic only after an ungraceful exit. Clean shutdown reaches zero after normal export/scrape propagation. Crash, `SIGKILL`, forced GitHub Actions cancellation, or runner loss converges within the lease plus up to 5 seconds of supported clock skew and one Prometheus scrape/evaluation interval, without restarting the Collector. Keep producer and Prometheus clocks synchronized within 5 seconds; GitHub-hosted runners meet this expectation, while self-hosted runners require reliable NTP or an equivalent time service.
|
|
27
30
|
|
|
28
31
|
Keep credentials out of YAML. Reference environment variables such as `OBSERVME_OTLP_TOKEN`, `OBSERVME_GRAFANA_TOKEN`, `OBSERVME_GRAFANA_PASSWORD`, and `OBSERVME_HASH_SALT`, then set those values in the shell or a trusted project `.env` file. When any content capture flag is enabled with `privacy.redactionEnabled: true`, `OBSERVME_HASH_SALT` must be set before the event occurs; otherwise content capture fails closed and emits `redaction.failed` diagnostics instead of conversation rows.
|
|
29
32
|
|
|
@@ -45,3 +48,5 @@ Invalid or unsafe merged configuration falls back to safe defaults. `/obs status
|
|
|
45
48
|
- [Example configurations](../examples/README.md)
|
|
46
49
|
- [Security, privacy, and redaction](reference/06-security-privacy-redaction.md)
|
|
47
50
|
- [Grafana and `/obs` validation flow](validation-flow.md)
|
|
51
|
+
- [Canonical active-agent queries and raw-query migration](reference/09-dashboards-alerts-slos.md#131-canonical-active-agent-promql)
|
|
52
|
+
- [Active-lease deployment and troubleshooting runbook](reference/11-deployment-runbooks.md)
|
|
@@ -58,9 +58,9 @@ Use this order for each child process:
|
|
|
58
58
|
1. Request the ObservMe integration API.
|
|
59
59
|
2. Call `startSubagent()` immediately before launching the child.
|
|
60
60
|
3. Pass the returned `env` as the child process environment without logging it.
|
|
61
|
-
4. Call `
|
|
62
|
-
5. Call `
|
|
63
|
-
6. Call `
|
|
61
|
+
4. Call `failSubagent()` only when the launcher fails before the child is running.
|
|
62
|
+
5. Call `startWait()`/`endWait()` around time spent waiting for child completion.
|
|
63
|
+
6. Call `completeSubagent()` once with the matching terminal `childStatus` and `outcome` (`completed`, `failed`, or `cancelled`).
|
|
64
64
|
7. Call `startJoin()`/`endJoin()` when collecting the terminal child status or result.
|
|
65
65
|
|
|
66
66
|
```typescript
|
|
@@ -78,12 +78,9 @@ if (!started?.ok) {
|
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
let child;
|
|
81
82
|
try {
|
|
82
|
-
await launchChildPi({ env: started.env });
|
|
83
|
-
observme.completeSubagent(started.spawnId, {
|
|
84
|
-
childAgentId: started.childAgentId,
|
|
85
|
-
childStatus: "active",
|
|
86
|
-
});
|
|
83
|
+
child = await launchChildPi({ env: started.env });
|
|
87
84
|
} catch (error) {
|
|
88
85
|
observme.failSubagent(started.spawnId, {
|
|
89
86
|
childAgentId: started.childAgentId,
|
|
@@ -91,6 +88,13 @@ try {
|
|
|
91
88
|
});
|
|
92
89
|
throw error;
|
|
93
90
|
}
|
|
91
|
+
|
|
92
|
+
const result = await waitForChildPi(child);
|
|
93
|
+
observme.completeSubagent(started.spawnId, {
|
|
94
|
+
childAgentId: started.childAgentId,
|
|
95
|
+
childStatus: result.status,
|
|
96
|
+
outcome: result.status,
|
|
97
|
+
});
|
|
94
98
|
```
|
|
95
99
|
|
|
96
100
|
Do not put raw tasks, prompts, command lines, environment values, child output, or private paths in `errorClass`, `spawnReason`, or other bounded fields.
|
|
@@ -101,7 +105,7 @@ Do not put raw tasks, prompts, command lines, environment values, child output,
|
|
|
101
105
|
| --- | --- |
|
|
102
106
|
| `getContext()` | Read the current workflow, root/parent/current agent, role, depth, session, and trace identifiers for local orchestration correlation. These are high-cardinality values and must not become metric labels. |
|
|
103
107
|
| `startSubagent(options)` | Starts `pi.agent.spawn`, records spawn metrics/logs, creates bounded parent tree state, and returns a sanitized propagation environment. |
|
|
104
|
-
| `completeSubagent(spawnId, options)` | Ends
|
|
108
|
+
| `completeSubagent(spawnId, options)` | Ends the active child lifecycle with one coherent `completed`, `failed`, or `cancelled` status/outcome pair. |
|
|
105
109
|
| `failSubagent(spawnId, options)` | Ends a launcher failure and records bounded failure telemetry. |
|
|
106
110
|
| `startWait(options)` / `endWait(id, options)` | Measures time the parent is blocked on a child or dependency. |
|
|
107
111
|
| `startJoin(options)` / `endJoin(id, options)` | Measures result collection and records child failure propagation or confirmed parent recovery. |
|
|
@@ -111,16 +115,16 @@ Do not put raw tasks, prompts, command lines, environment values, child output,
|
|
|
111
115
|
| Option | Values and meaning |
|
|
112
116
|
| --- | --- |
|
|
113
117
|
| `spawnId` | Optional caller-generated safe ID; omit to let ObservMe generate it. |
|
|
114
|
-
| `childAgentId` | Optional bounded parent-side placeholder; this is not propagated as the child's real agent ID. |
|
|
118
|
+
| `childAgentId` | Optional bounded parent-side placeholder; this is not propagated as the child's real agent ID and must remain unique while its active or terminal node is retained. |
|
|
115
119
|
| `command` / `args` | Used only to create a salted command fingerprint when configured; raw values are not exported. Omit them if the launcher cannot safely provide them. |
|
|
116
120
|
| `spawnType` | `command`, `tool`, `extension`, or `unknown`. |
|
|
117
121
|
| `spawnReason` | `delegated_task`, `parallel_search`, `review`, `tool_wrapper`, or `unknown`. |
|
|
118
122
|
| `toolCallId` | Optional high-cardinality trace/log correlation when a tool initiated the spawn. |
|
|
119
123
|
| `env` | Base child environment. ObservMe removes stale lineage/W3C keys and returns the replacement environment. |
|
|
120
124
|
|
|
121
|
-
Runtime callers are validated even when JavaScript bypasses the TypeScript types. Caller-provided lifecycle identifiers must match `[A-Za-z0-9._:-]{1,128}`. Commands and individual arguments are capped at 4096 characters, argument lists at 256 items, and environment objects at 4096 entries. Durations must be finite, non-negative milliseconds. Invalid or duplicate active operations return a failure without replacing an existing span.
|
|
125
|
+
Runtime callers are validated even when JavaScript bypasses the TypeScript types. Caller-provided lifecycle identifiers must match `[A-Za-z0-9._:-]{1,128}`. Commands and individual arguments are capped at 4096 characters, argument lists at 256 items, and environment objects at 4096 entries. Durations must be finite, non-negative milliseconds. Invalid or duplicate active operations return a failure without replacing an existing span. Child placeholders, including generated placeholders, are collision-checked before span, tree, metric, or propagation state is created; do not reuse a terminal placeholder while its bounded tree node remains retained.
|
|
122
126
|
|
|
123
|
-
Completion and
|
|
127
|
+
Completion accepts only terminal child states (`completed`, `failed`, `cancelled`), and any supplied outcome must match. Wait/join methods use bounded child states (`starting`, `active`, `completed`, `failed`, `cancelled`, `orphaned`), join states (`waiting`, `completed`, `failed`, `cancelled`, `timeout`, `unknown`), and wait reasons (`dependency`, `rate_limit`, `child_running`, `unknown`). `failurePropagated=false` on a completed join confirms that the parent recovered from a failed child.
|
|
124
128
|
|
|
125
129
|
All mutation methods return a discriminated result. Handle these reasons without crashing Pi:
|
|
126
130
|
|
|
@@ -129,7 +133,10 @@ All mutation methods return a discriminated result. Handle these reasons without
|
|
|
129
133
|
| `session_unavailable` | ObservMe is loaded but no telemetry session is active. |
|
|
130
134
|
| `invalid_request` | An identifier, enum, duration, command/argument field, or environment shape is invalid or oversized. |
|
|
131
135
|
| `spawn_already_exists` / `wait_already_exists` / `join_already_exists` | The requested lifecycle identifier is already active. Generate a unique identifier or finish the active operation; do not overwrite it. |
|
|
136
|
+
| `child_agent_already_exists` | The requested or generated child placeholder belongs to an active spawn or retained tree node. Generate a unique child identifier; do not reuse terminal placeholders. |
|
|
132
137
|
| `spawn_not_found` / `wait_not_found` / `join_not_found` | The lifecycle handle is absent or has already ended. |
|
|
138
|
+
| `child_agent_mismatch` | The supplied child ID does not match the child stored for the active spawn. |
|
|
139
|
+
| `invalid_terminal_transition` | Terminal status/outcome fields contradict each other or would rewrite an existing terminal tree state. |
|
|
133
140
|
| `operation_failed` | ObservMe could not safely complete the operation. |
|
|
134
141
|
|
|
135
142
|
Do not retry a completed lifecycle handle blindly; repeated completion can otherwise hide an orchestration-state bug.
|
|
@@ -183,7 +190,7 @@ The integration API is transport-agnostic. A launcher can use a local subprocess
|
|
|
183
190
|
|
|
184
191
|
- passes the returned environment unchanged to the child Pi process;
|
|
185
192
|
- does not serialize the envelope or raw task into telemetry or captured logs;
|
|
186
|
-
- reports launcher
|
|
193
|
+
- reports launcher failure separately from child completion, failure, or cancellation;
|
|
187
194
|
- records wait and join around the transport's actual blocking and result-collection boundaries;
|
|
188
195
|
- guarantees the child loads a compatible ObservMe extension;
|
|
189
196
|
- cleans up temporary files, pipes, sessions, containers, or remote resources deterministically.
|
|
@@ -105,7 +105,7 @@ Optional telemetry:
|
|
|
105
105
|
|
|
106
106
|
### `custom` entry
|
|
107
107
|
|
|
108
|
-
Extension state entry created through `pi.appendEntry()`. It does not participate in LLM context. ObservMe
|
|
108
|
+
Extension state entry created through `pi.appendEntry()`. It does not participate in LLM context. When `agent.writeCorrelationEntry` is explicitly enabled, ObservMe appends at most one `observme.correlation` entry per active branch after telemetry startup succeeds. The versioned data contains only bounded workflow/agent lineage identifiers and depth. Startup scans `ctx.sessionManager.getBranch()` from newest to oldest, restores the latest valid entry, ignores corrupt entries and entries on abandoned branches, and skips an idempotent reload write. By default ObservMe does not append custom entries.
|
|
109
109
|
|
|
110
110
|
### `custom_message` entry
|
|
111
111
|
|
|
@@ -244,12 +244,12 @@ The derived id must be scoped by session id and agent run id and must not be use
|
|
|
244
244
|
|
|
245
245
|
On extension startup in an existing session:
|
|
246
246
|
|
|
247
|
-
1. Read current session header if available.
|
|
247
|
+
1. Read the current session header from `ctx.sessionManager` if available.
|
|
248
248
|
2. Set `pi.session.id`, `pi.workflow.id`, `pi.agent.id`, and resource attributes.
|
|
249
|
-
3.
|
|
250
|
-
4.
|
|
251
|
-
5.
|
|
252
|
-
6.
|
|
249
|
+
3. When `agent.writeCorrelationEntry` is enabled, reconstruct optional workflow/agent lineage only from the latest validated `observme.correlation` custom entry on `ctx.sessionManager.getBranch()`.
|
|
250
|
+
4. Ignore malformed entries and entries outside the active branch without exporting their values or changing LLM context.
|
|
251
|
+
5. After successful startup, append the minimal custom entry only when the active branch does not already contain the same validated correlation.
|
|
252
|
+
6. Never replay historical telemetry automatically. Historical export requires the explicit, confirmed `/obs backfill` command, which marks each emitted record with `observme.replayed=true`.
|
|
253
253
|
|
|
254
254
|
## 11. Local Session Reading Rules
|
|
255
255
|
|
|
@@ -503,9 +503,46 @@ observme_handler_duration_ms
|
|
|
503
503
|
|
|
504
504
|
```text
|
|
505
505
|
observme_active_spans
|
|
506
|
-
observme_active_agents
|
|
506
|
+
observme_active_agents # compatibility lifecycle claim
|
|
507
|
+
observme_agent_lease_expires_unixtime_seconds # asynchronous gauge, unit s
|
|
507
508
|
```
|
|
508
509
|
|
|
510
|
+
#### 12.4.1 Active-agent lease and clock contract
|
|
511
|
+
|
|
512
|
+
This section is the single source of truth for active-agent lease constants and semantics. Runtime configuration, metric metadata, PromQL, dashboards, alerts, examples, and tests must reference these values rather than define different local windows.
|
|
513
|
+
|
|
514
|
+
| Contract item | Required value |
|
|
515
|
+
|---|---|
|
|
516
|
+
| Lease metric | `observme_agent_lease_expires_unixtime_seconds` |
|
|
517
|
+
| OTel instrument and unit | Asynchronous gauge, unit `s` |
|
|
518
|
+
| Observation value | `(wallClockUnixMillis + metrics.activeAgentLeaseDurationMillis) / 1000`; fractional Unix seconds are valid |
|
|
519
|
+
| Configuration | `metrics.activeAgentLeaseDurationMillis`; `OBSERVME_ACTIVE_AGENT_LEASE_DURATION_MS` is its environment override |
|
|
520
|
+
| Default lease | `60000` ms (60 seconds) |
|
|
521
|
+
| Inclusive duration bounds | `10000` ms through `300000` ms (10 seconds through 5 minutes) |
|
|
522
|
+
| Export relationship | Lease duration must be at least `(2 * metrics.exportIntervalMillis) + 5000` ms |
|
|
523
|
+
| Supported wall-clock skew | Producer and Prometheus clocks must differ by no more than 5 seconds |
|
|
524
|
+
| Future-timestamp horizon | A lease later than `time() + 305` seconds is pathological and must not count active |
|
|
525
|
+
|
|
526
|
+
`observme_active_agents` remains the clean lifecycle claim. An agent is operationally active only when its claim is positive and the matching lease is finite, later than Prometheus `time()`, and no later than the future-timestamp horizon. Missing, expired, malformed, non-finite, or pathologically future leases fail closed. Lease renewal occurs once per SDK metric collection while the session is active, including otherwise idle sessions; it does not depend on turns, tool calls, or a background heartbeat timer.
|
|
527
|
+
|
|
528
|
+
On clean shutdown, deactivate the lease before the final metric force-flush and record the matching `observme_active_agents` decrement. This makes the leased result zero after normal export/scrape propagation without waiting for expiry. If shutdown telemetry cannot run, the last exported lease is allowed to expire. With synchronized clocks, an ungraceful exit converges to zero within the configured lease plus one Prometheus scrape/evaluation interval. Across the full supported positive-skew budget, the measurable worst-case bound is the configured lease plus 5 seconds plus one scrape/evaluation interval; for the defaults and a 15-second scrape interval, that is at most 80 seconds.
|
|
529
|
+
|
|
530
|
+
The only per-runtime Prometheus join and deduplication key is the Collector-normalized `observme_instance_id` resource label. ObservMe generates its UUID independently for each telemetry session and never derives it from prompts, responses, content, paths, commands, GitHub run IDs, sessions, workflows, or logical agents. `service.instance.id` may carry the same resource value for OTel interoperability, but active-agent PromQL joins on `observme_instance_id` and deduplicates repeated exporter/scrape representations with `max by (observme_instance_id)`. The lease observation itself may carry only existing bounded metric labels; the instance identity comes from resource-to-metric conversion and must not become a dashboard variable or legend.
|
|
531
|
+
|
|
532
|
+
Lifecycle and failure behavior is fixed as follows:
|
|
533
|
+
|
|
534
|
+
| Condition | Required leased-active behavior |
|
|
535
|
+
|---|---|
|
|
536
|
+
| Clean exit | Deactivate before final flush; the active claim becomes zero without waiting for lease expiry. |
|
|
537
|
+
| `SIGTERM` | Use the clean path when Pi delivers shutdown; otherwise use the same expiry behavior as an abrupt exit. |
|
|
538
|
+
| Crash, `SIGKILL`, cancelled GitHub job, or runner/power loss | No cleanup is required for correctness; the last lease expires within the documented convergence bound. |
|
|
539
|
+
| Duplicate session start, reload, or resume | Deactivate and flush the replaced session before activating its replacement. If replacement cleanup is interrupted, the old instance lease expires independently. |
|
|
540
|
+
| Startup failure or partial runtime construction | Never leave an activated observable callback; dispose any registered callback. |
|
|
541
|
+
| Collector or OTLP outage | Cached positive claims stop counting when their last delivered leases expire. A still-running producer becomes active again after export recovers and a fresh lease arrives. Backend unavailability itself remains an unknown/no-query state. |
|
|
542
|
+
| Clock skew or clock movement | A producer clock ahead can delay expiry and one behind can fail closed early. Clocks outside the supported skew are unsupported; non-finite observations are omitted and values beyond the future horizon do not count. GitHub-hosted runners satisfy the synchronized-clock expectation; self-hosted runners must use reliable time synchronization. |
|
|
543
|
+
|
|
544
|
+
A renewable lease is a bounded liveness signal, not proof that the operating-system process is reachable at the exact query instant. `observme_active_agents` remains emitted with its existing name and clean-lifecycle behavior for compatibility, so the change is additive under the semantic-convention versioning policy and does not require a major convention-version increment. Consumers that need crash-safe live counts must migrate from raw active-claim sums to the leased definition.
|
|
545
|
+
|
|
509
546
|
### 12.5 Lifecycle recording points
|
|
510
547
|
|
|
511
548
|
- Record `observme_tool_result_size_chars` once from the finalized `tool_execution_end` result, using the same bounded tool labels as tool-call metrics.
|
|
@@ -665,6 +702,7 @@ agent.run.failed
|
|
|
665
702
|
agent.spawn.started
|
|
666
703
|
agent.spawn.completed
|
|
667
704
|
agent.spawn.failed
|
|
705
|
+
agent.spawn.cancelled
|
|
668
706
|
agent.wait.started
|
|
669
707
|
agent.wait.completed
|
|
670
708
|
agent.join.started
|
|
@@ -47,6 +47,7 @@ metrics:
|
|
|
47
47
|
enabled: true
|
|
48
48
|
exportIntervalMillis: 15000
|
|
49
49
|
exportTimeoutMillis: 3000
|
|
50
|
+
activeAgentLeaseDurationMillis: 60000
|
|
50
51
|
|
|
51
52
|
logs:
|
|
52
53
|
enabled: true
|
|
@@ -58,6 +59,8 @@ logs:
|
|
|
58
59
|
|
|
59
60
|
The OpenTelemetry JS OTLP HTTP exporters default to signal-specific paths such as `http://localhost:4318/v1/traces`, `http://localhost:4318/v1/metrics`, and `/v1/logs`. Treat `otlp.endpoint` as a base endpoint in ObservMe config, but pass signal-specific URLs to SDK exporters that require an explicit `url`.
|
|
60
61
|
|
|
62
|
+
The default active-agent lease is 60 seconds and renews on each 15-second metric collection. Supported lease values are 10 seconds through 5 minutes and must be at least twice the export interval plus 5 seconds. Keep the producer and Prometheus clocks within 5 seconds; otherwise a lease may fail closed early or remain valid longer than intended. The exact contract is frozen in [`04-telemetry-semantic-conventions.md` §12.4.1](04-telemetry-semantic-conventions.md#1241-active-agent-lease-and-clock-contract).
|
|
63
|
+
|
|
61
64
|
## 4. Trace Context and Workflow/Agent-Lineage Propagation
|
|
62
65
|
|
|
63
66
|
When ObservMe launches or wraps a subagent process, the parent runtime should propagate both standard trace context and ObservMe-specific lineage:
|
|
@@ -118,10 +121,10 @@ service:
|
|
|
118
121
|
|
|
119
122
|
## 6. Production Collector for Grafana Stack
|
|
120
123
|
|
|
121
|
-
This configuration routes traces to Tempo, logs to Loki, and metrics to a Prometheus
|
|
124
|
+
This configuration routes traces to Tempo, logs to Loki, and metrics to a Prometheus scrape endpoint. Use a Collector distribution that contains every configured component. The minimal debug example works with the core Collector, while `prometheus`, `prometheusremotewrite`, `probabilistic_sampler`, and `tail_sampling` are commonly deployed from the Collector Contrib distribution or a vendor distribution such as Grafana Alloy.
|
|
122
125
|
|
|
123
126
|
```yaml
|
|
124
|
-
# Production-oriented Collector reference for Tempo, Loki, and
|
|
127
|
+
# Production-oriented Collector reference for Tempo, Loki, and Prometheus.
|
|
125
128
|
# Verify that your Collector distribution contains every configured component,
|
|
126
129
|
# replace backend endpoints/security for your deployment, and never add raw
|
|
127
130
|
# content or high-cardinality execution identifiers to metric labels.
|
|
@@ -220,14 +223,14 @@ exporters:
|
|
|
220
223
|
retry_on_failure:
|
|
221
224
|
enabled: true
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
endpoint:
|
|
226
|
+
prometheus:
|
|
227
|
+
endpoint: 0.0.0.0:8889
|
|
228
|
+
# Exporter-wide stale-series/cardinality cleanup for gauges, counters, and
|
|
229
|
+
# histograms. Five minutes exceeds ObservMe's default one-minute active-agent
|
|
230
|
+
# lease; lease-aware PromQL, not expiration, determines liveness.
|
|
231
|
+
metric_expiration: 5m
|
|
225
232
|
resource_to_telemetry_conversion:
|
|
226
233
|
enabled: true
|
|
227
|
-
sending_queue:
|
|
228
|
-
enabled: true
|
|
229
|
-
retry_on_failure:
|
|
230
|
-
enabled: true
|
|
231
234
|
|
|
232
235
|
debug:
|
|
233
236
|
verbosity: basic
|
|
@@ -243,7 +246,7 @@ service:
|
|
|
243
246
|
metrics:
|
|
244
247
|
receivers: [otlp]
|
|
245
248
|
processors: [memory_limiter, resource/observme, resource/drop_high_cardinality_metric_attrs, batch]
|
|
246
|
-
exporters: [
|
|
249
|
+
exporters: [prometheus]
|
|
247
250
|
|
|
248
251
|
logs:
|
|
249
252
|
receivers: [otlp]
|
|
@@ -251,9 +254,19 @@ service:
|
|
|
251
254
|
exporters: [otlphttp/loki]
|
|
252
255
|
```
|
|
253
256
|
|
|
257
|
+
### 6.1 Prometheus exporter expiration policy
|
|
258
|
+
|
|
259
|
+
The shipped local and production scrape-exporter examples set `metric_expiration: 5m`. This exporter-level setting applies uniformly to every Prometheus-exported gauge, counter, and histogram; it cannot be scoped only to active-agent metrics. Its only purpose is stale-series and cardinality cleanup: it bounds the Collector memory used by abandoned metric streams and eventually lets Prometheus mark an expired series stale after the exporter stops exposing it. Lease-aware PromQL remains the active-agent correctness mechanism, even while the Collector still exposes a cached positive `observme_active_agents` value.
|
|
260
|
+
|
|
261
|
+
Five minutes comfortably exceeds the default 60-second active-agent lease and 15-second SDK metric export interval. If either interval is customized, keep `metric_expiration` longer than the lease and allow enough additional time for expected export delays and scrape gaps. A shorter value cleans abandoned streams sooner but can evict gauges, counters, and histograms during a temporary producer outage; a longer value consumes Collector memory and preserves abandoned cardinality for longer. It must never be shortened merely to make active-agent totals converge.
|
|
262
|
+
|
|
263
|
+
Apply a Collector configuration change through the deployment's supported reload mechanism or restart the Collector. A restart clears the Prometheus exporter's in-memory series cache, but neither reload nor restart deletes samples already stored in Prometheus; historical samples remain subject to Prometheus retention and deletion policy. Restart is never required merely to make an expired active-agent lease stop counting. `metric_expiration` belongs only to the metrics scrape exporter, so traces and logs are unaffected. Remote-write deployments do not use this scrape-exporter cache setting and must apply their backend's series-retention policy, while continuing to use the same lease-aware active-agent queries.
|
|
264
|
+
|
|
265
|
+
When migrating, deploy a version that emits `observme_agent_lease_expires_unixtime_seconds`, verify the Collector preserves the generated `observme_instance_id`, then replace every current-activity raw sum in dashboards, recording rules, and alerts with the canonical queries in [`09-dashboards-alerts-slos.md` §1.3.1](09-dashboards-alerts-slos.md#131-canonical-active-agent-promql). Keep raw and expired claims only as clearly labeled diagnostics. Missing leases intentionally fail closed, so do not switch a mixed-version fleet until the lease metric is visible for the producers that should count.
|
|
266
|
+
|
|
254
267
|
The Collector attribute processor is defense in depth for log attributes only. It must not be the only redaction layer, and it does not sanitize arbitrary log bodies; ObservMe must redact or drop sensitive content before export. The traces pipeline intentionally keeps `pi.llm.prompt.redacted`, `pi.llm.response.redacted`, and `pi.llm.thinking.redacted` so Tempo can display redacted content after explicit capture is enabled. Old telemetry that an earlier Collector dropped cannot be recovered; generate new LLM events after updating the Collector.
|
|
255
268
|
|
|
256
|
-
Keep high-cardinality lineage attributes on traces/logs, but remove them from the metrics pipeline unless the organization explicitly accepts the cardinality. The bundled local Loki pipeline promotes `pi.agent.id` and `pi.agent.run.id` as log labels so the LLM Conversations dashboard can filter captured chat content by agent and run. Prometheus resource-to-telemetry conversion is safe only after the metrics pipeline drops `pi.workflow.id`, `pi.agent.id`, `pi.session.id`, trace IDs, and spawn IDs
|
|
269
|
+
Keep high-cardinality lineage attributes on traces/logs, but remove them from the metrics pipeline unless the organization explicitly accepts the cardinality. The bundled local Loki pipeline promotes `pi.agent.id` and `pi.agent.run.id` as log labels so the LLM Conversations dashboard can filter captured chat content by agent and run. Prometheus resource-to-telemetry conversion is safe only after the metrics pipeline drops `pi.workflow.id`, `pi.agent.id`, `pi.session.id`, trace IDs, and spawn IDs. Preserve the generated `service.instance.id`/`observme.instance.id` resource identity in the metrics pipeline so lease-aware queries can join and deduplicate concurrent ObservMe metric streams without exposing workflow, session, logical-agent, trace, span, or job-run identifiers as labels.
|
|
257
270
|
|
|
258
271
|
## 7. Direct-to-Backend Development Mode
|
|
259
272
|
|
|
@@ -105,7 +105,7 @@ GitHub token: (gh[pousr]_[A-Za-z0-9_]{36,}|github_pat_[A-Za-z0-9_
|
|
|
105
105
|
OpenAI-like key: sk-[A-Za-z0-9_-]{20,}
|
|
106
106
|
Anthropic-like key: sk-ant-[A-Za-z0-9_-]{20,}
|
|
107
107
|
Slack token: xox[baprs]-[A-Za-z0-9-]{10,}
|
|
108
|
-
Private key block:
|
|
108
|
+
Private key block: complete uppercase labels ending in PRIVATE KEY (including PKCS#8, encrypted, RSA, and EC)
|
|
109
109
|
Password assignment: (?i)(password|passwd|pwd)\s*[:=]\s*[^\s]+
|
|
110
110
|
API key assignment: (?i)(api[_-]?key|token|secret|client[_-]?secret)\s*[:=]\s*[^\s]+
|
|
111
111
|
URL credentials: [a-z][a-z0-9+.-]*://[^\s:/?#]+:[^\s@/]+@
|
|
@@ -113,6 +113,8 @@ URL credentials: [a-z][a-z0-9+.-]*://[^\s:/?#]+:[^\s@/]+@
|
|
|
113
113
|
|
|
114
114
|
These patterns are a minimum safety net, not a complete secret scanner. Keep them covered by tests and allow organizations to add custom regexes for proprietary credential formats.
|
|
115
115
|
|
|
116
|
+
Private-key matching consumes a `PRIVATE KEY` label with an optional, bounded uppercase prefix, its body, and a same-label `END` marker with a 1,000,000-character body scan bound. This covers PKCS#8 (`PRIVATE KEY`), `ENCRYPTED PRIVATE KEY`, `RSA PRIVATE KEY`, `EC PRIVATE KEY`, and other uppercase private-key labels without accepting public-key labels. The shared input-size guard drops larger captured values before secret matching. If a supported private-key marker has no matching footer because it is missing, truncated, or mislabeled, redaction fails closed by replacing everything from that marker through the end of the captured value. `PUBLIC KEY` and `RSA PUBLIC KEY` blocks are not private-key matches.
|
|
117
|
+
|
|
116
118
|
Matched values are replaced with:
|
|
117
119
|
|
|
118
120
|
```text
|
|
@@ -228,7 +230,7 @@ Example:
|
|
|
228
230
|
|
|
229
231
|
The Collector cannot recover content dropped by an older configuration; generate new LLM events after updating the Collector and dashboards. Do not use raw chat text in Loki or Tempo query strings.
|
|
230
232
|
|
|
231
|
-
## 11. Authentication
|
|
233
|
+
## 11. Authentication and Credential-Free Endpoint URLs
|
|
232
234
|
|
|
233
235
|
ObservMe supports OTLP headers:
|
|
234
236
|
|
|
@@ -238,7 +240,9 @@ otlp:
|
|
|
238
240
|
Authorization: "Bearer ${OBSERVME_OTLP_TOKEN}"
|
|
239
241
|
```
|
|
240
242
|
|
|
241
|
-
|
|
243
|
+
Grafana base URLs must be absolute HTTP(S) URLs without embedded username or password components. Configure Grafana authentication only through `query.grafana.token` or the complete `query.grafana.username` and `query.grafana.password` pair. A credential-bearing base URL is rejected during configuration validation and query readiness, before either Grafana transport can perform network I/O. Diagnostics report only the safe `embedded_credentials` failure class and dedicated setting names; they do not render the rejected URL or credential values.
|
|
244
|
+
|
|
245
|
+
Secrets must be read from environment variables or secure runtime config, not hardcoded in extension source or embedded in endpoint URLs.
|
|
242
246
|
|
|
243
247
|
## 12. TLS
|
|
244
248
|
|
|
@@ -246,12 +250,12 @@ Production default:
|
|
|
246
250
|
|
|
247
251
|
```yaml
|
|
248
252
|
otlp:
|
|
253
|
+
endpoint: https://otel-collector.example.com:4318
|
|
249
254
|
tls:
|
|
250
|
-
enabled: true
|
|
251
255
|
insecureSkipVerify: false
|
|
252
256
|
```
|
|
253
257
|
|
|
254
|
-
Development may use insecure
|
|
258
|
+
The endpoint URL scheme selects TLS. The OTLP exporters explicitly keep certificate verification enabled unless `otlp.tls.insecureSkipVerify: true` is configured. Grafana query transport follows the same rule through `query.grafana.tls.insecureSkipVerify`. Production rejects either verification bypass, and any plain HTTP endpoint, unless `privacy.allowInsecureTransport: true` records the explicit insecure-transport acknowledgement. Development may use local HTTP or self-signed certificates, but the insecure setting remains visible in `/obs status` and `/obs health`.
|
|
255
259
|
|
|
256
260
|
## 13. Tenant Isolation
|
|
257
261
|
|
|
@@ -281,10 +285,13 @@ Do not log secrets while reporting security failures.
|
|
|
281
285
|
|
|
282
286
|
## 15. Safe Config Validation
|
|
283
287
|
|
|
288
|
+
Trusted project config, project `.env`, and starter-config creation share one filesystem boundary. Their lexical paths and canonical targets must remain inside the stable canonical project root. Existing symlinks are supported only when they resolve to files or directories inside that root; out-of-root, dangling, replaced, or unverifiable paths fail closed. Reads and starter writes use identity-verified file handles and recheck containment so a concurrent file, ancestor, or root change cannot substitute an external target. A rejected project source is not loaded, starter creation does not overwrite an existing file, and diagnostics expose only a bounded failure class rather than canonical or external path details.
|
|
289
|
+
|
|
284
290
|
Reject config if:
|
|
285
291
|
|
|
286
292
|
- capture is enabled but redaction is disabled, unless `allowUnsafeCapture: true`
|
|
287
|
-
- OTLP endpoint is plain HTTP in production, unless `allowInsecureTransport: true`
|
|
293
|
+
- an OTLP or Grafana endpoint is plain HTTP in production, unless `allowInsecureTransport: true`
|
|
294
|
+
- OTLP or Grafana certificate verification is bypassed in production, unless `allowInsecureTransport: true`
|
|
288
295
|
- metric labels include forbidden high-cardinality fields such as workflow IDs, session IDs, or agent IDs
|
|
289
296
|
- propagated workflow or agent lineage values are malformed, too long, or contain unsafe characters
|
|
290
297
|
- queue sizes exceed configured memory limits
|
|
@@ -43,9 +43,10 @@ query:
|
|
|
43
43
|
|
|
44
44
|
Authentication and local transport behavior:
|
|
45
45
|
|
|
46
|
-
-
|
|
46
|
+
- Keep `query.grafana.url` credential-free: it must be an absolute HTTP(S) base URL with no embedded username or password component.
|
|
47
|
+
- Configure authentication only with `query.grafana.token` or the dedicated `query.grafana.username` and `query.grafana.password` settings. Prefer the token for Grafana service-account or bearer authentication.
|
|
47
48
|
- If the token is blank or an unresolved environment placeholder, ObservMe may use `query.grafana.username` plus `query.grafana.password` as a local-development Basic auth fallback.
|
|
48
|
-
- Query-backed commands fail fast before backend calls when Grafana auth is unresolved/missing/incomplete, `query.grafana.url` is invalid, or a required datasource UID is blank.
|
|
49
|
+
- Query-backed commands fail fast before backend calls when Grafana auth is unresolved/missing/incomplete, `query.grafana.url` is invalid or contains credentials, or a required datasource UID is blank. Credential-bearing URLs report the safe `embedded_credentials` class without rendering the URL or credential values.
|
|
49
50
|
- `401` and `403` responses are surfaced as Grafana authentication failures without printing token or password values.
|
|
50
51
|
- Browser login cookies are irrelevant because `/obs` commands call the Grafana API directly from the Pi process.
|
|
51
52
|
|
|
@@ -71,7 +72,7 @@ query:
|
|
|
71
72
|
preferIPv4: true
|
|
72
73
|
```
|
|
73
74
|
|
|
74
|
-
For the bundled `observability-stack/`, the supported command path is nginx HTTPS at `https://observability.local`; the default stack does not publish Grafana on `localhost:3000`. Create a Grafana service-account token with Viewer access for read-only datasource queries and export it as `OBSERVME_GRAFANA_TOKEN`, or export/set `OBSERVME_GRAFANA_PASSWORD` from `observability-stack/secrets/grafana_admin_password` for local Basic auth. Extension environment values can come from system environment variables before Pi starts, or from a trusted project `.env` copied from `.env.example`; system variables override `.env`. The local self-signed certificate requires `query.grafana.tls.insecureSkipVerify=true`, and `query.grafana.transport.preferIPv4=true` avoids DNS stalls when `observability.local` resolves to IPv6 first. Provisioned datasource UIDs are `tempo`, `loki`, and `prometheus`; Loki selectors use normalized labels such as `service_name`, `pi_session_id`, `event_name`, and `event_category`.
|
|
75
|
+
For the bundled `observability-stack/`, the supported command path is nginx HTTPS at `https://observability.local`; the default stack does not publish Grafana on `localhost:3000`. Create a Grafana service-account token with Viewer access for read-only datasource queries and export it as `OBSERVME_GRAFANA_TOKEN`, or export/set `OBSERVME_GRAFANA_PASSWORD` from `observability-stack/secrets/grafana_admin_password` for local Basic auth. Extension environment values can come from system environment variables before Pi starts, or from a trusted project `.env` copied from `.env.example`; system variables override `.env`. The local self-signed certificate requires `query.grafana.tls.insecureSkipVerify=true`, and `query.grafana.transport.preferIPv4=true` avoids DNS stalls when `observability.local` resolves to IPv6 first. Production configurations using the TLS bypass must also set `privacy.allowInsecureTransport=true` as an explicit acknowledgement; trusting the production CA remains preferred. Provisioned datasource UIDs are `tempo`, `loki`, and `prometheus`; Loki selectors use normalized labels such as `service_name`, `pi_session_id`, `event_name`, and `event_category`.
|
|
75
76
|
|
|
76
77
|
## 4. Commands
|
|
77
78
|
|
|
@@ -84,6 +85,9 @@ Output:
|
|
|
84
85
|
```text
|
|
85
86
|
ObservMe: enabled
|
|
86
87
|
OTLP endpoint: https://otel.example.com:4318
|
|
88
|
+
OTLP transport security: TLS certificate verification enabled
|
|
89
|
+
Grafana URL: https://grafana.example.com/
|
|
90
|
+
Grafana transport security: TLS certificate verification enabled
|
|
87
91
|
Traces: enabled
|
|
88
92
|
Metrics: enabled
|
|
89
93
|
Logs: enabled
|
|
@@ -97,6 +101,8 @@ Last export error: none
|
|
|
97
101
|
Checks Collector and Grafana reachability, and reports Grafana query auth/config readiness before datasource calls.
|
|
98
102
|
|
|
99
103
|
```text
|
|
104
|
+
Collector transport security: TLS certificate verification enabled
|
|
105
|
+
Grafana transport security: TLS certificate verification enabled
|
|
100
106
|
Collector: reachable
|
|
101
107
|
Grafana: reachable
|
|
102
108
|
Tempo datasource: ok
|
|
@@ -216,13 +222,22 @@ Only available if Loki receives `pi.session.id` as structured metadata or label.
|
|
|
216
222
|
|
|
217
223
|
## 5. Trace Links
|
|
218
224
|
|
|
219
|
-
|
|
225
|
+
`/obs session`, `/obs trace`, and `/obs link` use the same pure trace-link builder, so one effective config and trace ID always produce one canonical URL. Custom absolute HTTP(S) templates support these trace-ID placeholders:
|
|
220
226
|
|
|
221
|
-
|
|
222
|
-
|
|
227
|
+
- `{traceId}`
|
|
228
|
+
- `{{traceId}}` or `{{ traceId }}`
|
|
229
|
+
- `${traceId}`
|
|
230
|
+
- `%TRACE_ID%`
|
|
231
|
+
|
|
232
|
+
The matching optional Tempo datasource placeholders are `{tempoDatasourceUid}`, `{{tempoDatasourceUid}}` (whitespace is allowed), `${tempoDatasourceUid}`, and `%TEMPO_DATASOURCE_UID%`. Trace IDs and datasource UIDs are URL-encoded before substitution.
|
|
233
|
+
|
|
234
|
+
An empty template or a template containing `...` selects the structured Grafana Explore fallback. The fallback uses `query.grafana.url`, preserves any path prefix, and constructs the current `schemaVersion=1&panes=...` Tempo query with `query.grafana.datasourceUids.tempo`. This is the behavior used by the generated starter:
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
traceUrlTemplate: https://observability.local/explore?left=...
|
|
223
238
|
```
|
|
224
239
|
|
|
225
|
-
|
|
240
|
+
Unsupported placeholders, non-HTTP(S) URLs, credential-bearing URLs, and malformed or oversized templates produce one bounded actionable diagnostic; commands do not silently omit a link or apply different substitutions.
|
|
226
241
|
|
|
227
242
|
## 6. Grafana Query Abstraction
|
|
228
243
|
|
|
@@ -258,6 +273,14 @@ query:
|
|
|
258
273
|
maxAgents: 20
|
|
259
274
|
```
|
|
260
275
|
|
|
276
|
+
Each query result-count setting accepts integers from 1 through 100. Runtime query clients clamp programmatically supplied values to the same ceiling before adding a backend request limit or selecting parsed results.
|
|
277
|
+
|
|
278
|
+
### Bounded command display
|
|
279
|
+
|
|
280
|
+
Prometheus label keys and values used for `/obs` display are normalized before they enter command snapshots. Control and line-separator characters become spaces, repeated whitespace collapses, and each label is limited to 96 UTF-16 code units. Truncated labels retain a valid Unicode prefix and end with `…`.
|
|
281
|
+
|
|
282
|
+
`/obs cost` renders at most 20 rows. `/obs tools` renders at most 20 call rows and 20 failure rows. Omitted rows are reported explicitly. `/obs agents` retains its 10-row recent-child display limit. Every final `/obs` notification is normalized to remove terminal controls other than intended line feeds and is limited to 64 rows and 8,192 UTF-16 code units. A notification that reaches either hard limit ends with `… output truncated`. These display bounds do not change backend queries, metric identity, or emitted telemetry.
|
|
283
|
+
|
|
261
284
|
## 8. Dependency Direction
|
|
262
285
|
|
|
263
286
|
ObservMe telemetry emission must not depend on Grafana query availability.
|