@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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { VERSION as PI_VERSION } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
|
|
4
|
+
export const MINIMUM_SUPPORTED_PI_VERSION = "0.80.5";
|
|
5
|
+
export const RELEASE_TESTED_PI_VERSION = "0.80.6";
|
|
6
|
+
const FIRST_UNSUPPORTED_PI_VERSION = "0.81.0";
|
|
7
|
+
export const SUPPORTED_PI_VERSION_RANGE =
|
|
8
|
+
`>=${MINIMUM_SUPPORTED_PI_VERSION} <${FIRST_UNSUPPORTED_PI_VERSION}`;
|
|
9
|
+
|
|
10
|
+
const compatibilityErrorPrefix = "ObservMe/Pi API compatibility error";
|
|
11
|
+
const MAX_DETECTED_PI_VERSION_LENGTH = 128;
|
|
12
|
+
const piVersionCorePattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
|
|
13
|
+
const semanticVersionIdentifierPattern = /^[0-9A-Za-z-]+$/u;
|
|
14
|
+
const numericIdentifierPattern = /^\d+$/u;
|
|
15
|
+
const requiredPiMethods = ["on", "registerCommand", "appendEntry", "getThinkingLevel"] as const;
|
|
16
|
+
|
|
17
|
+
export function assertObservMePiCompatibility(
|
|
18
|
+
pi: unknown,
|
|
19
|
+
piVersion = PI_VERSION,
|
|
20
|
+
): asserts pi is ExtensionAPI {
|
|
21
|
+
const missingMethods = findMissingPiMethods(pi);
|
|
22
|
+
const detectedVersion = normalizeDetectedVersion(piVersion);
|
|
23
|
+
const versionSupported = isSupportedPiVersion(detectedVersion);
|
|
24
|
+
if (versionSupported && missingMethods.length === 0) return;
|
|
25
|
+
|
|
26
|
+
const reasons: string[] = [];
|
|
27
|
+
if (!versionSupported) reasons.push(`detected Pi ${detectedVersion}`);
|
|
28
|
+
if (missingMethods.length > 0) reasons.push(`missing required API method(s): ${missingMethods.join(", ")}`);
|
|
29
|
+
|
|
30
|
+
throw new TypeError(
|
|
31
|
+
`${compatibilityErrorPrefix}: ObservMe requires @earendil-works/pi-coding-agent ${SUPPORTED_PI_VERSION_RANGE}; ${reasons.join("; ")}. No ObservMe event handlers or commands were registered.`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findMissingPiMethods(pi: unknown): string[] {
|
|
36
|
+
if (!isRecord(pi)) return [...requiredPiMethods];
|
|
37
|
+
|
|
38
|
+
const missing: string[] = [];
|
|
39
|
+
for (const method of requiredPiMethods) {
|
|
40
|
+
if (typeof pi[method] !== "function") missing.push(method);
|
|
41
|
+
}
|
|
42
|
+
return missing;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
46
|
+
return typeof value === "object" && value !== null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeDetectedVersion(value: unknown): string {
|
|
50
|
+
if (typeof value !== "string") return "unknown";
|
|
51
|
+
const trimmed = value.trim();
|
|
52
|
+
if (trimmed.length === 0 || trimmed.length > MAX_DETECTED_PI_VERSION_LENGTH) return "unknown";
|
|
53
|
+
return parsePiVersion(trimmed) ? trimmed : "unknown";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isSupportedPiVersion(version: string): boolean {
|
|
57
|
+
const detected = parsePiVersion(version);
|
|
58
|
+
const minimum = parsePiVersion(MINIMUM_SUPPORTED_PI_VERSION);
|
|
59
|
+
const maximum = parsePiVersion(FIRST_UNSUPPORTED_PI_VERSION);
|
|
60
|
+
if (!detected || detected.prerelease || !minimum || !maximum) return false;
|
|
61
|
+
|
|
62
|
+
return comparePiVersions(detected, minimum) >= 0 && comparePiVersions(detected, maximum) < 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface PiVersion {
|
|
66
|
+
major: number;
|
|
67
|
+
minor: number;
|
|
68
|
+
patch: number;
|
|
69
|
+
prerelease?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parsePiVersion(version: string): PiVersion | undefined {
|
|
73
|
+
const buildSeparatorIndex = version.indexOf("+");
|
|
74
|
+
const versionAndPrerelease = buildSeparatorIndex === -1 ? version : version.slice(0, buildSeparatorIndex);
|
|
75
|
+
const build = buildSeparatorIndex === -1 ? undefined : version.slice(buildSeparatorIndex + 1);
|
|
76
|
+
if (build !== undefined && !hasValidSemanticVersionIdentifiers(build, true)) return undefined;
|
|
77
|
+
|
|
78
|
+
const prereleaseSeparatorIndex = versionAndPrerelease.indexOf("-");
|
|
79
|
+
const coreVersion = prereleaseSeparatorIndex === -1
|
|
80
|
+
? versionAndPrerelease
|
|
81
|
+
: versionAndPrerelease.slice(0, prereleaseSeparatorIndex);
|
|
82
|
+
const prerelease = prereleaseSeparatorIndex === -1
|
|
83
|
+
? undefined
|
|
84
|
+
: versionAndPrerelease.slice(prereleaseSeparatorIndex + 1);
|
|
85
|
+
if (prerelease !== undefined && !hasValidSemanticVersionIdentifiers(prerelease, false)) return undefined;
|
|
86
|
+
|
|
87
|
+
const match = piVersionCorePattern.exec(coreVersion);
|
|
88
|
+
if (!match) return undefined;
|
|
89
|
+
|
|
90
|
+
const major = Number(match[1]);
|
|
91
|
+
const minor = Number(match[2]);
|
|
92
|
+
const patch = Number(match[3]);
|
|
93
|
+
if (!Number.isSafeInteger(major) || !Number.isSafeInteger(minor) || !Number.isSafeInteger(patch)) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
major,
|
|
99
|
+
minor,
|
|
100
|
+
patch,
|
|
101
|
+
...(prerelease === undefined ? {} : { prerelease }),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function hasValidSemanticVersionIdentifiers(value: string, allowLeadingZeros: boolean): boolean {
|
|
106
|
+
for (const identifier of value.split(".")) {
|
|
107
|
+
if (!semanticVersionIdentifierPattern.test(identifier)) return false;
|
|
108
|
+
if (!allowLeadingZeros && hasInvalidNumericIdentifier(identifier)) return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function hasInvalidNumericIdentifier(identifier: string): boolean {
|
|
114
|
+
return identifier.length > 1 && identifier.startsWith("0") && numericIdentifierPattern.test(identifier);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function comparePiVersions(left: PiVersion, right: PiVersion): number {
|
|
118
|
+
if (left.major !== right.major) return left.major - right.major;
|
|
119
|
+
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
120
|
+
return left.patch - right.patch;
|
|
121
|
+
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
2
|
+
import type {
|
|
3
|
+
AgentEndEvent,
|
|
4
|
+
AgentStartEvent,
|
|
5
|
+
ExtensionContext,
|
|
6
|
+
TurnEndEvent,
|
|
7
|
+
TurnStartEvent,
|
|
8
|
+
} from "@earendil-works/pi-coding-agent";
|
|
2
9
|
import { recordObsSessionTurn } from "../../commands/obs-session.ts";
|
|
3
10
|
import { AGENT_RUN_ATTRIBUTES, TURN_ATTRIBUTES } from "../../semconv/attributes.ts";
|
|
4
11
|
import { LOG_EVENT_NAMES } from "../../semconv/metrics.ts";
|
|
@@ -21,7 +28,7 @@ import {
|
|
|
21
28
|
} from "../handler-internals.ts";
|
|
22
29
|
import { workflowFailed } from "../handler-runtime.ts";
|
|
23
30
|
import type { HandlerRegistrar } from "../handler-runtime.ts";
|
|
24
|
-
import type {
|
|
31
|
+
import type { HandlerSessionState, PiHandler } from "../handler-types.ts";
|
|
25
32
|
|
|
26
33
|
export function registerAgentTurnHandlers(registrar: HandlerRegistrar, state: HandlerSessionState): void {
|
|
27
34
|
registrar.add("agent_start", createAgentStartHandler(state));
|
|
@@ -30,11 +37,11 @@ export function registerAgentTurnHandlers(registrar: HandlerRegistrar, state: Ha
|
|
|
30
37
|
registrar.add("agent_end", createAgentEndHandler(state));
|
|
31
38
|
}
|
|
32
39
|
|
|
33
|
-
function createAgentStartHandler(state: HandlerSessionState):
|
|
40
|
+
function createAgentStartHandler(state: HandlerSessionState): PiHandler<"agent_start"> {
|
|
34
41
|
return handleAgentStart.bind(undefined, state);
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
function handleAgentStart(state: HandlerSessionState, event:
|
|
44
|
+
function handleAgentStart(state: HandlerSessionState, event: AgentStartEvent, _ctx: ExtensionContext): void {
|
|
38
45
|
const session = state.session;
|
|
39
46
|
if (!session) return;
|
|
40
47
|
|
|
@@ -48,11 +55,11 @@ function handleAgentStart(state: HandlerSessionState, event: unknown, _ctx: Obse
|
|
|
48
55
|
emitLifecycleLog(session.logger, LOG_EVENT_NAMES.AGENT_RUN_STARTED, attributes);
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
function createAgentEndHandler(state: HandlerSessionState):
|
|
58
|
+
function createAgentEndHandler(state: HandlerSessionState): PiHandler<"agent_end"> {
|
|
52
59
|
return handleAgentEnd.bind(undefined, state);
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
function handleAgentEnd(state: HandlerSessionState, event:
|
|
62
|
+
function handleAgentEnd(state: HandlerSessionState, event: AgentEndEvent, _ctx: ExtensionContext): void {
|
|
56
63
|
const session = state.session;
|
|
57
64
|
if (!session) return;
|
|
58
65
|
|
|
@@ -75,11 +82,11 @@ function handleAgentEnd(state: HandlerSessionState, event: unknown, _ctx: Observ
|
|
|
75
82
|
});
|
|
76
83
|
}
|
|
77
84
|
|
|
78
|
-
function createTurnStartHandler(state: HandlerSessionState):
|
|
85
|
+
function createTurnStartHandler(state: HandlerSessionState): PiHandler<"turn_start"> {
|
|
79
86
|
return handleTurnStart.bind(undefined, state);
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
function handleTurnStart(state: HandlerSessionState, event:
|
|
89
|
+
function handleTurnStart(state: HandlerSessionState, event: TurnStartEvent, _ctx: ExtensionContext): void {
|
|
83
90
|
const session = state.session;
|
|
84
91
|
if (!session) return;
|
|
85
92
|
|
|
@@ -99,11 +106,11 @@ function handleTurnStart(state: HandlerSessionState, event: unknown, _ctx: Obser
|
|
|
99
106
|
emitLifecycleLog(session.logger, LOG_EVENT_NAMES.TURN_STARTED, attributes);
|
|
100
107
|
}
|
|
101
108
|
|
|
102
|
-
function createTurnEndHandler(state: HandlerSessionState):
|
|
109
|
+
function createTurnEndHandler(state: HandlerSessionState): PiHandler<"turn_end"> {
|
|
103
110
|
return handleTurnEnd.bind(undefined, state);
|
|
104
111
|
}
|
|
105
112
|
|
|
106
|
-
function handleTurnEnd(state: HandlerSessionState, event:
|
|
113
|
+
function handleTurnEnd(state: HandlerSessionState, event: TurnEndEvent, _ctx: ExtensionContext): void {
|
|
107
114
|
const session = state.session;
|
|
108
115
|
if (!session) return;
|
|
109
116
|
|