@senad-d/observme 0.1.0 → 0.1.2

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 (43) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +103 -13
  3. package/SECURITY.md +1 -1
  4. package/dashboards/observme-slos.yaml +1 -1
  5. package/docs/README.md +54 -0
  6. package/docs/agent-subagent-observability-requirements.md +34 -38
  7. package/docs/configuration.md +9 -0
  8. package/docs/extension-integration.md +222 -0
  9. package/docs/reference/00-README.md +81 -0
  10. package/{ObservMe-Production-Docs → docs/reference}/05-otel-pipeline-and-collector.md +5 -0
  11. package/{ObservMe-Production-Docs → docs/reference}/07-extension-implementation-blueprint.md +18 -7
  12. package/{ObservMe-Production-Docs → docs/reference}/pi-session-format.md +1 -1
  13. package/docs/review-validation.md +1 -1
  14. package/docs/validation-flow.md +8 -1
  15. package/examples/README.md +52 -0
  16. package/examples/collector.yaml +5 -0
  17. package/examples/integrations/subagent-runner.ts +262 -0
  18. package/examples/observme.yaml +4 -0
  19. package/img/demo.gif +0 -0
  20. package/img/observme-command.png +0 -0
  21. package/img/observme-conversations.png +0 -0
  22. package/img/observme-dashboards.png +0 -0
  23. package/img/observme-overview.png +0 -0
  24. package/img/observme-trace.png +0 -0
  25. package/package.json +11 -2
  26. package/skills/observme-docs/SKILL.md +65 -0
  27. package/src/integration.ts +134 -0
  28. package/src/pi/handlers.ts +11 -0
  29. package/src/pi/integration-api.ts +224 -0
  30. package/src/pi/subagent-spawn.ts +1 -1
  31. package/tsconfig.json +1 -1
  32. package/ObservMe-Production-Docs/00-README.md +0 -79
  33. /package/{ObservMe-Production-Docs → docs/reference}/01-requirements-and-scope.md +0 -0
  34. /package/{ObservMe-Production-Docs → docs/reference}/02-reference-architecture.md +0 -0
  35. /package/{ObservMe-Production-Docs → docs/reference}/03-pi-event-and-session-model.md +0 -0
  36. /package/{ObservMe-Production-Docs → docs/reference}/04-telemetry-semantic-conventions.md +0 -0
  37. /package/{ObservMe-Production-Docs → docs/reference}/06-security-privacy-redaction.md +0 -0
  38. /package/{ObservMe-Production-Docs → docs/reference}/08-query-grafana-integration.md +0 -0
  39. /package/{ObservMe-Production-Docs → docs/reference}/09-dashboards-alerts-slos.md +0 -0
  40. /package/{ObservMe-Production-Docs → docs/reference}/10-testing-release-operations.md +0 -0
  41. /package/{ObservMe-Production-Docs → docs/reference}/11-deployment-runbooks.md +0 -0
  42. /package/{ObservMe-Production-Docs → docs/reference}/12-configuration-reference.md +0 -0
  43. /package/{ObservMe-Production-Docs → docs/reference}/13-source-notes.md +0 -0
@@ -0,0 +1,224 @@
1
+ import {
2
+ OBSERVME_INTEGRATION_CHANNEL,
3
+ OBSERVME_INTEGRATION_VERSION,
4
+ type ObservMeCompleteSubagentOptions,
5
+ type ObservMeFailSubagentOptions,
6
+ type ObservMeIntegrationApi,
7
+ type ObservMeIntegrationContextSuccess,
8
+ type ObservMeIntegrationFailure,
9
+ type ObservMeIntegrationRequest,
10
+ type ObservMeIntegrationSuccess,
11
+ type ObservMeStartedSubagent,
12
+ type ObservMeStartedWaitJoin,
13
+ type ObservMeStartSubagentOptions,
14
+ type ObservMeWaitJoinOptions,
15
+ } from "../integration.ts";
16
+ import { SESSION_ATTRIBUTES } from "../semconv/attributes.ts";
17
+ import {
18
+ completeSubagentSpawn,
19
+ endAgentJoin,
20
+ endAgentWait,
21
+ failSubagentSpawn,
22
+ startAgentJoin,
23
+ startAgentWait,
24
+ startSubagentSpawn,
25
+ } from "./subagent-spawn.ts";
26
+ import type { HandlerSessionState, ObservMeTelemetrySession } from "./handler-types.ts";
27
+
28
+ interface IntegrationEventBus {
29
+ on(channel: string, handler: (data: unknown) => void): () => void;
30
+ }
31
+
32
+ interface IntegrationPiApi {
33
+ readonly events?: IntegrationEventBus;
34
+ }
35
+
36
+ export function registerObservMeIntegration(pi: unknown, state: HandlerSessionState): (() => void) | undefined {
37
+ const events = resolveIntegrationEventBus(pi);
38
+ if (!events) return undefined;
39
+
40
+ const api = new SessionBackedObservMeIntegrationApi(state);
41
+ return events.on(OBSERVME_INTEGRATION_CHANNEL, api.handleRequest.bind(api));
42
+ }
43
+
44
+ export class SessionBackedObservMeIntegrationApi implements ObservMeIntegrationApi {
45
+ readonly version = OBSERVME_INTEGRATION_VERSION;
46
+ readonly #state: HandlerSessionState;
47
+
48
+ constructor(state: HandlerSessionState) {
49
+ this.#state = state;
50
+ }
51
+
52
+ handleRequest(value: unknown): void {
53
+ if (!isCompatibleIntegrationRequest(value)) return;
54
+ try {
55
+ value.respond(this);
56
+ } catch {
57
+ return;
58
+ }
59
+ }
60
+
61
+ getContext(): ObservMeIntegrationContextSuccess | ObservMeIntegrationFailure {
62
+ const session = this.#state.session;
63
+ if (!session) return integrationFailure("session_unavailable");
64
+
65
+ return {
66
+ ok: true,
67
+ context: {
68
+ workflowId: session.lineage.workflowId,
69
+ workflowRootAgentId: session.lineage.workflowRootAgentId,
70
+ agentId: session.lineage.agentId,
71
+ parentAgentId: session.lineage.parentAgentId,
72
+ rootAgentId: session.lineage.rootAgentId,
73
+ depth: session.lineage.depth,
74
+ role: session.lineage.role,
75
+ capability: session.lineage.capability,
76
+ sessionId: readSessionId(session),
77
+ traceId: readSessionTraceId(session),
78
+ },
79
+ };
80
+ }
81
+
82
+ startSubagent(options: ObservMeStartSubagentOptions = {}): ObservMeStartedSubagent | ObservMeIntegrationFailure {
83
+ const session = this.#state.session;
84
+ if (!session) return integrationFailure("session_unavailable");
85
+
86
+ try {
87
+ const started = startSubagentSpawn(session, options);
88
+ return {
89
+ ok: true,
90
+ spawnId: started.spawnId,
91
+ childAgentId: started.childAgentId,
92
+ env: started.env,
93
+ traceContextPropagated: started.traceContextPropagated,
94
+ };
95
+ } catch {
96
+ return integrationFailure("operation_failed");
97
+ }
98
+ }
99
+
100
+ completeSubagent(
101
+ spawnId: string,
102
+ options: ObservMeCompleteSubagentOptions = {},
103
+ ): ObservMeIntegrationSuccess | ObservMeIntegrationFailure {
104
+ const session = this.#state.session;
105
+ if (!session) return integrationFailure("session_unavailable");
106
+ if (!session.spans.activeSubagentSpawns.get(spawnId)) return integrationFailure("spawn_not_found");
107
+
108
+ try {
109
+ completeSubagentSpawn(session, spawnId, options);
110
+ return integrationSuccess();
111
+ } catch {
112
+ return integrationFailure("operation_failed");
113
+ }
114
+ }
115
+
116
+ failSubagent(
117
+ spawnId: string,
118
+ options: ObservMeFailSubagentOptions = {},
119
+ ): ObservMeIntegrationSuccess | ObservMeIntegrationFailure {
120
+ const session = this.#state.session;
121
+ if (!session) return integrationFailure("session_unavailable");
122
+ if (!session.spans.activeSubagentSpawns.get(spawnId)) return integrationFailure("spawn_not_found");
123
+
124
+ try {
125
+ failSubagentSpawn(session, spawnId, options);
126
+ return integrationSuccess();
127
+ } catch {
128
+ return integrationFailure("operation_failed");
129
+ }
130
+ }
131
+
132
+ startWait(options: ObservMeWaitJoinOptions = {}): ObservMeStartedWaitJoin | ObservMeIntegrationFailure {
133
+ return this.startWaitJoin(options, "wait");
134
+ }
135
+
136
+ endWait(
137
+ waitId: string,
138
+ options: ObservMeWaitJoinOptions = {},
139
+ ): ObservMeIntegrationSuccess | ObservMeIntegrationFailure {
140
+ return this.endWaitJoin(waitId, options, "wait");
141
+ }
142
+
143
+ startJoin(options: ObservMeWaitJoinOptions = {}): ObservMeStartedWaitJoin | ObservMeIntegrationFailure {
144
+ return this.startWaitJoin(options, "join");
145
+ }
146
+
147
+ endJoin(
148
+ joinId: string,
149
+ options: ObservMeWaitJoinOptions = {},
150
+ ): ObservMeIntegrationSuccess | ObservMeIntegrationFailure {
151
+ return this.endWaitJoin(joinId, options, "join");
152
+ }
153
+
154
+ private startWaitJoin(
155
+ options: ObservMeWaitJoinOptions,
156
+ kind: "wait" | "join",
157
+ ): ObservMeStartedWaitJoin | ObservMeIntegrationFailure {
158
+ const session = this.#state.session;
159
+ if (!session) return integrationFailure("session_unavailable");
160
+
161
+ try {
162
+ const started = kind === "wait" ? startAgentWait(session, options) : startAgentJoin(session, options);
163
+ return { ok: true, id: started.id };
164
+ } catch {
165
+ return integrationFailure("operation_failed");
166
+ }
167
+ }
168
+
169
+ private endWaitJoin(
170
+ id: string,
171
+ options: ObservMeWaitJoinOptions,
172
+ kind: "wait" | "join",
173
+ ): ObservMeIntegrationSuccess | ObservMeIntegrationFailure {
174
+ const session = this.#state.session;
175
+ if (!session) return integrationFailure("session_unavailable");
176
+ const registry = kind === "wait" ? session.spans.activeAgentWaits : session.spans.activeAgentJoins;
177
+ if (!registry.get(id)) return integrationFailure(kind === "wait" ? "wait_not_found" : "join_not_found");
178
+
179
+ try {
180
+ if (kind === "wait") endAgentWait(session, id, options);
181
+ else endAgentJoin(session, id, options);
182
+ return integrationSuccess();
183
+ } catch {
184
+ return integrationFailure("operation_failed");
185
+ }
186
+ }
187
+ }
188
+
189
+ function resolveIntegrationEventBus(pi: unknown): IntegrationEventBus | undefined {
190
+ if (!pi || typeof pi !== "object") return undefined;
191
+ const events = (pi as IntegrationPiApi).events;
192
+ return events && typeof events.on === "function" ? events : undefined;
193
+ }
194
+
195
+ function isCompatibleIntegrationRequest(value: unknown): value is ObservMeIntegrationRequest {
196
+ if (!value || typeof value !== "object") return false;
197
+ const request = value as Partial<ObservMeIntegrationRequest>;
198
+ return (
199
+ Array.isArray(request.supportedVersions) &&
200
+ request.supportedVersions.includes(OBSERVME_INTEGRATION_VERSION) &&
201
+ typeof request.respond === "function"
202
+ );
203
+ }
204
+
205
+ function readSessionId(session: ObservMeTelemetrySession): string | undefined {
206
+ const value = session.sessionAttributes?.[SESSION_ATTRIBUTES.PI_SESSION_ID];
207
+ return typeof value === "string" ? value : undefined;
208
+ }
209
+
210
+ function readSessionTraceId(session: ObservMeTelemetrySession): string | undefined {
211
+ try {
212
+ return session.sessionSpan?.spanContext().traceId;
213
+ } catch {
214
+ return undefined;
215
+ }
216
+ }
217
+
218
+ function integrationSuccess(): ObservMeIntegrationSuccess {
219
+ return { ok: true };
220
+ }
221
+
222
+ function integrationFailure(reason: ObservMeIntegrationFailure["reason"]): ObservMeIntegrationFailure {
223
+ return { ok: false, reason };
224
+ }
@@ -44,7 +44,7 @@ export type {
44
44
 
45
45
  export type SubagentSpawnType = "command" | "tool" | "extension" | "unknown";
46
46
  export type AgentJoinStatus = "completed" | "failed" | "cancelled" | "timeout" | "unknown" | "waiting";
47
- export type AttributePrimitive = boolean | number | string;
47
+ export type AttributePrimitive = boolean | number | string | string[];
48
48
  export type AttributeMap = Record<string, AttributePrimitive>;
49
49
 
50
50
  export interface SubagentSpanRegistry {
package/tsconfig.json CHANGED
@@ -11,5 +11,5 @@
11
11
  "allowImportingTsExtensions": true,
12
12
  "types": ["node"]
13
13
  },
14
- "include": ["src/**/*.ts"]
14
+ "include": ["src/**/*.ts", "examples/**/*.ts"]
15
15
  }
@@ -1,79 +0,0 @@
1
- # ObservMe Production Documentation
2
-
3
- **ObservMe** is a production-grade Pi extension for observability. It captures Pi session, turn, tool, LLM, branch, compaction, and error events, translates them into OpenTelemetry telemetry, and exports them to a durable external observability stack such as OpenTelemetry Collector, Grafana Tempo, Grafana Loki, Prometheus, Mimir, or Grafana Cloud.
4
-
5
- ObservMe is intentionally **not** a local analytics database. Pi agents are ephemeral. Observability data must leave the Pi agent process quickly, safely, and reliably. When multiple Pi agents or subagents are running, ObservMe must also preserve workflow and agent-tree lineage so operators can see which parent agent spawned each child agent and how the full orchestrated workload behaved.
6
-
7
- ## Core Design
8
-
9
- ```text
10
- Pi Agent
11
- └── ObservMe Pi Extension
12
- ├── OTEL Traces ──► OTEL Collector ──► Tempo
13
- ├── OTEL Metrics ──► OTEL Collector ──► Prometheus / Mimir
14
- └── OTEL Logs ──► OTEL Collector ──► Loki
15
-
16
- Grafana
17
- ├── Tempo datasource
18
- ├── Loki datasource
19
- └── Prometheus/Mimir datasource
20
- ```
21
-
22
- ## Document Set
23
-
24
- | File | Purpose |
25
- |---|---|
26
- | `01-requirements-and-scope.md` | Product goals, non-goals, personas, requirements, rollout phases |
27
- | `02-reference-architecture.md` | Full system architecture and component responsibilities |
28
- | `03-pi-event-and-session-model.md` | Pi session/event sources and how ObservMe interprets them |
29
- | `04-telemetry-semantic-conventions.md` | ObservMe attribute, metric, log, and span naming specification |
30
- | `05-otel-pipeline-and-collector.md` | OTLP exporter strategy and production Collector configurations |
31
- | `06-security-privacy-redaction.md` | Prompt safety, secret redaction, PII handling, tenant isolation |
32
- | `07-extension-implementation-blueprint.md` | TypeScript implementation architecture, modules, lifecycle, code patterns |
33
- | `08-query-grafana-integration.md` | `/obs` commands, Grafana/Tempo/Loki/Prometheus query integration |
34
- | `09-dashboards-alerts-slos.md` | Dashboard pack, PromQL, LogQL, TraceQL, alerts, SLOs |
35
- | `10-testing-release-operations.md` | Test strategy, chaos testing, performance validation, release process |
36
- | `11-deployment-runbooks.md` | Local, Docker Compose, Kubernetes, and production runbooks |
37
- | `12-configuration-reference.md` | Complete ObservMe configuration schema and examples |
38
- | `13-source-notes.md` | Official documentation cross-check notes and external assumptions |
39
- | `pi-session-format.md` | Pi session JSONL reference copied and adapted from Pi's official session-format documentation |
40
-
41
- ## Production Principles
42
-
43
- 1. **OTLP-first.** ObservMe emits OpenTelemetry traces, metrics, and logs.
44
- 2. **No durable local telemetry storage.** Only bounded in-memory queues are allowed.
45
- 3. **Fail open.** If observability fails, Pi must continue operating.
46
- 4. **Privacy by default.** Prompt and response content capture is disabled by default.
47
- 5. **Pi-native and OTEL-compatible semantics.** ObservMe uses `observme.*` and `pi.*` for Pi-specific concepts and official OpenTelemetry `gen_ai.*` attributes where they fit; it does not depend on OpenInference.
48
- 6. **Agent lineage by design.** Agent and subagent telemetry carries `pi.agent.*` lineage attributes and, where possible, W3C trace context so parent/child relationships survive process boundaries.
49
- 7. **Workflow/tree observability.** Orchestrator workloads are modeled as agent trees with `pi.workflow.*` correlation, depth, fan-out, wait/join, orphan, and critical-path signals so operators can understand both single-agent and multi-agent execution.
50
- 8. **Collector recommended.** Direct-to-backend export is allowed only for development or constrained environments.
51
- 9. **Backend-neutral.** Tempo/Loki/Prometheus are reference backends, not hard dependencies.
52
-
53
- ## Minimum Viable Production Release
54
-
55
- A production-ready `1.0.0` release must include:
56
-
57
- - Extension load and health checks
58
- - Session, workflow, agent-run, turn, LLM, tool, subagent-spawn, agent wait/join, compaction, branch, model-change telemetry
59
- - Multi-agent tree metrics for active agents, depth, fan-out, orphan agents, trace-context propagation failures, child failures, and workflow duration
60
- - OTLP trace exporter
61
- - OTLP metric exporter
62
- - OTLP log exporter
63
- - Configurable redaction and capture controls
64
- - Bounded queues and exporter timeouts
65
- - Grafana dashboard JSON
66
- - Collector reference configs
67
- - Tests for event mapping, redaction, exporter failure, and cardinality
68
- - `/obs status`, `/obs health`, `/obs session`, `/obs cost`, `/obs trace`, `/obs agents` commands
69
-
70
- ## Source References
71
-
72
- This document set assumes:
73
-
74
- - Pi extensions provide documented lifecycle hooks, commands, tools, custom entries, session events, and queryable session state.
75
- - Pi can run multiple extension instances or spawn additional Pi processes through tools/subagent patterns; cross-process workflow and parent/child lineage requires explicit ObservMe correlation and trace-context propagation.
76
- - Pi sessions are JSONL files with typed entries such as `message`, `compaction`, `branch_summary`, `model_change`, `thinking_level_change`, `custom`, `custom_message`, `label`, and `session_info`.
77
- - OpenTelemetry Collector receives, processes, and exports telemetry via pipelines.
78
- - OpenTelemetry GenAI semantic conventions are the official OTEL namespace for GenAI client/agent spans, attributes, and metrics, but many GenAI fields are still experimental/evolving in SDK packages; ObservMe extends them for Pi-specific workflow concepts.
79
- - Grafana Tempo, Loki, and Prometheus/Mimir are reference storage systems for traces, logs, and metrics.