@senad-d/observme 0.1.4 → 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/CHANGELOG.md +35 -0
- package/README.md +24 -8
- package/docs/agent-subagent-observability-requirements.md +4 -3
- package/docs/compatibility-matrix.md +11 -2
- package/docs/configuration.md +2 -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 +1 -0
- package/docs/reference/06-security-privacy-redaction.md +13 -6
- package/docs/reference/08-query-grafana-integration.md +30 -7
- package/docs/reference/11-deployment-runbooks.md +21 -0
- package/docs/reference/12-configuration-reference.md +30 -4
- package/examples/integrations/subagent-runner.ts +8 -5
- package/examples/observme.yaml +2 -2
- package/package.json +12 -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 +49 -32
- package/src/config/defaults.ts +0 -2
- package/src/config/load-config.ts +270 -92
- package/src/config/project-paths.ts +318 -6
- package/src/config/query-limits.ts +10 -0
- package/src/config/schema.ts +9 -8
- package/src/config/transport-security.ts +107 -0
- package/src/config/validate.ts +68 -11
- 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/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 +207 -75
- 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 +138 -51
- package/src/pi/handler-types.ts +30 -15
- 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 +1 -0
- package/src/semconv/values.ts +2 -0
package/src/otel/traces.ts
CHANGED
|
@@ -3,15 +3,24 @@ import type { Resource } from "@opentelemetry/resources";
|
|
|
3
3
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
4
4
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
5
5
|
import type { Sampler, SpanExporter, SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
AlwaysOffSampler,
|
|
8
|
+
BasicTracerProvider,
|
|
9
|
+
BatchSpanProcessor,
|
|
10
|
+
ParentBasedSampler,
|
|
11
|
+
TraceIdRatioBasedSampler,
|
|
12
|
+
} from "@opentelemetry/sdk-trace-base";
|
|
7
13
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
8
14
|
import type { ObservMeConfig, TraceBatchConfig } from "../config/schema.ts";
|
|
9
15
|
import { appendOtlpSignalPath } from "./otlp-endpoint.ts";
|
|
16
|
+
import { buildOtlpHttpAgentOptions, type OtlpHttpAgentOptions } from "./otlp-http-options.ts";
|
|
10
17
|
import type { StartOtelSdkFactoryOptions } from "./sdk.ts";
|
|
11
18
|
|
|
12
19
|
export const OBSERVME_TRACER_NAME = "@senad-d/observme";
|
|
13
20
|
export const OTLP_TRACE_SIGNAL_PATH = "/v1/traces";
|
|
14
21
|
|
|
22
|
+
const noopTracer = new BasicTracerProvider({ sampler: new AlwaysOffSampler() }).getTracer(OBSERVME_TRACER_NAME);
|
|
23
|
+
|
|
15
24
|
export const DOCUMENTED_TRACE_BATCH_DEFAULTS = {
|
|
16
25
|
maxQueueSize: 2048,
|
|
17
26
|
maxExportBatchSize: 512,
|
|
@@ -25,6 +34,7 @@ export interface OtlpTraceExporterOptions {
|
|
|
25
34
|
readonly url: string;
|
|
26
35
|
readonly headers: Record<string, string>;
|
|
27
36
|
readonly timeoutMillis: number;
|
|
37
|
+
readonly httpAgentOptions: OtlpHttpAgentOptions;
|
|
28
38
|
}
|
|
29
39
|
|
|
30
40
|
export interface TraceProviderOptions {
|
|
@@ -35,7 +45,6 @@ export interface TraceProviderOptions {
|
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
export interface TraceProviderLike {
|
|
38
|
-
register?: () => void;
|
|
39
48
|
getTracer: (name: string) => Tracer;
|
|
40
49
|
forceFlush?: () => Promise<void> | void;
|
|
41
50
|
shutdown?: () => Promise<void> | void;
|
|
@@ -48,7 +57,6 @@ export type TraceProviderFactory = (options: TraceProviderOptions) => TraceProvi
|
|
|
48
57
|
export interface ObservMeTraceSdkOptions {
|
|
49
58
|
readonly config: ObservMeConfig;
|
|
50
59
|
readonly tracerName?: string;
|
|
51
|
-
readonly registerGlobal?: boolean;
|
|
52
60
|
readonly exporterFactory?: TraceExporterFactory;
|
|
53
61
|
readonly spanProcessorFactory?: TraceSpanProcessorFactory;
|
|
54
62
|
readonly tracerProviderFactory?: TraceProviderFactory;
|
|
@@ -63,18 +71,18 @@ export interface TraceExporterWiring {
|
|
|
63
71
|
export class ObservMeTraceSdk {
|
|
64
72
|
readonly #config: ObservMeConfig;
|
|
65
73
|
readonly #tracerName: string;
|
|
66
|
-
readonly #registerGlobal: boolean;
|
|
67
74
|
readonly #exporterFactory: TraceExporterFactory;
|
|
68
75
|
readonly #spanProcessorFactory: TraceSpanProcessorFactory;
|
|
69
76
|
readonly #tracerProviderFactory: TraceProviderFactory;
|
|
77
|
+
#exporter?: SpanExporter;
|
|
78
|
+
#processor?: SpanProcessor;
|
|
70
79
|
#provider?: TraceProviderLike;
|
|
71
|
-
#tracer
|
|
80
|
+
#tracer: Tracer = noopTracer;
|
|
72
81
|
#state: TracePipelineState = "idle";
|
|
73
82
|
|
|
74
83
|
constructor(options: ObservMeTraceSdkOptions) {
|
|
75
84
|
this.#config = options.config;
|
|
76
85
|
this.#tracerName = options.tracerName ?? OBSERVME_TRACER_NAME;
|
|
77
|
-
this.#registerGlobal = options.registerGlobal ?? true;
|
|
78
86
|
this.#exporterFactory = options.exporterFactory ?? createOtlpTraceExporter;
|
|
79
87
|
this.#spanProcessorFactory = options.spanProcessorFactory ?? createBatchSpanProcessor;
|
|
80
88
|
this.#tracerProviderFactory = options.tracerProviderFactory ?? createNodeTraceProvider;
|
|
@@ -84,28 +92,27 @@ export class ObservMeTraceSdk {
|
|
|
84
92
|
return this.#state;
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
get tracer(): Tracer
|
|
95
|
+
get tracer(): Tracer {
|
|
88
96
|
return this.#tracer;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
start(): void {
|
|
92
100
|
if (this.#state === "started" || this.#state === "disabled") return;
|
|
93
|
-
if (!this.#config.traces.enabled) {
|
|
101
|
+
if (!this.#config.enabled || !this.#config.traces.enabled) {
|
|
94
102
|
this.#state = "disabled";
|
|
95
103
|
return;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
const wiring = buildTraceExporterWiring(this.#config);
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
this.#exporter = this.#exporterFactory(wiring.exporter);
|
|
108
|
+
this.#processor = this.#spanProcessorFactory(this.#exporter, wiring.batch);
|
|
101
109
|
this.#provider = this.#tracerProviderFactory({
|
|
102
110
|
resource: createTraceResource(this.#config),
|
|
103
111
|
sampler: createTraceSampler(this.#config),
|
|
104
|
-
spanProcessors: [processor],
|
|
112
|
+
spanProcessors: [this.#processor],
|
|
105
113
|
forceFlushTimeoutMillis: this.#config.shutdown.flushTimeoutMs,
|
|
106
114
|
});
|
|
107
115
|
|
|
108
|
-
if (this.#registerGlobal) this.#provider.register?.();
|
|
109
116
|
this.#tracer = this.#provider.getTracer(this.#tracerName);
|
|
110
117
|
this.#state = "started";
|
|
111
118
|
}
|
|
@@ -115,9 +122,19 @@ export class ObservMeTraceSdk {
|
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
async shutdown(): Promise<void> {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
if (this.#state === "shutdown") return;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
if (this.#provider?.shutdown) await this.#provider.shutdown();
|
|
129
|
+
else if (this.#processor) await this.#processor.shutdown();
|
|
130
|
+
else await this.#exporter?.shutdown();
|
|
131
|
+
} finally {
|
|
132
|
+
this.#provider = undefined;
|
|
133
|
+
this.#processor = undefined;
|
|
134
|
+
this.#exporter = undefined;
|
|
135
|
+
this.#state = "shutdown";
|
|
136
|
+
this.#tracer = noopTracer;
|
|
137
|
+
}
|
|
121
138
|
}
|
|
122
139
|
}
|
|
123
140
|
|
|
@@ -127,7 +144,7 @@ export function createTraceSessionScopedOtelSdk(options: StartOtelSdkFactoryOpti
|
|
|
127
144
|
|
|
128
145
|
export function buildTraceExporterWiring(config: ObservMeConfig): TraceExporterWiring {
|
|
129
146
|
return {
|
|
130
|
-
enabled: config.traces.enabled,
|
|
147
|
+
enabled: config.enabled && config.traces.enabled,
|
|
131
148
|
exporter: buildOtlpTraceExporterOptions(config),
|
|
132
149
|
batch: { ...config.traces.batch },
|
|
133
150
|
};
|
|
@@ -138,6 +155,7 @@ export function buildOtlpTraceExporterOptions(config: ObservMeConfig): OtlpTrace
|
|
|
138
155
|
url: resolveTraceEndpoint(config),
|
|
139
156
|
headers: { ...config.otlp.headers },
|
|
140
157
|
timeoutMillis: config.otlp.timeoutMs,
|
|
158
|
+
httpAgentOptions: buildOtlpHttpAgentOptions(config),
|
|
141
159
|
};
|
|
142
160
|
}
|
|
143
161
|
|
|
@@ -48,6 +48,7 @@ interface MutableAgentTreeNode {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const activeChildStatuses = new Set<AgentChildStatus>(["starting", "active"]);
|
|
51
|
+
const terminalChildStatuses = new Set<AgentChildStatus>(["completed", "failed", "cancelled", "orphaned"]);
|
|
51
52
|
const agentStatusOrder: AgentChildStatus[] = ["starting", "active", "completed", "failed", "cancelled", "orphaned"];
|
|
52
53
|
|
|
53
54
|
export class AgentTreeTracker {
|
|
@@ -68,6 +69,11 @@ export class AgentTreeTracker {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
registerAgent(lineage: AgentLineageContext, status: AgentChildStatus = "active"): AgentTreeNode {
|
|
72
|
+
const existingNode = this.#nodes.get(lineage.agentId);
|
|
73
|
+
if (existingNode && !isAgentStatusTransitionAllowed(existingNode.status, status)) {
|
|
74
|
+
return snapshotNode(existingNode, this.#nodes);
|
|
75
|
+
}
|
|
76
|
+
|
|
71
77
|
const node = createMutableNode(lineage, status, this.isOrphan(lineage));
|
|
72
78
|
this.retainParentForInsertion(node.parentAgentId);
|
|
73
79
|
this.#nodes.set(lineage.agentId, node);
|
|
@@ -77,7 +83,7 @@ export class AgentTreeTracker {
|
|
|
77
83
|
|
|
78
84
|
updateStatus(agentId: string, status: AgentChildStatus): AgentTreeNode | undefined {
|
|
79
85
|
const node = this.#nodes.get(agentId);
|
|
80
|
-
if (!node) return undefined;
|
|
86
|
+
if (!node || !isAgentStatusTransitionAllowed(node.status, status)) return undefined;
|
|
81
87
|
|
|
82
88
|
node.status = status;
|
|
83
89
|
if (status === "orphaned") node.orphaned = true;
|
|
@@ -177,6 +183,13 @@ export class AgentTreeTracker {
|
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
185
|
|
|
186
|
+
export function isAgentStatusTransitionAllowed(current: AgentChildStatus, next: AgentChildStatus): boolean {
|
|
187
|
+
if (current === next) return true;
|
|
188
|
+
if (terminalChildStatuses.has(current)) return false;
|
|
189
|
+
if (current === "starting") return true;
|
|
190
|
+
return next !== "starting";
|
|
191
|
+
}
|
|
192
|
+
|
|
180
193
|
export function assertNoHighCardinalityMetricLabels(labels: Record<string, string>): void {
|
|
181
194
|
const forbiddenKey = Object.keys(labels).find(isHighCardinalityLineageKey);
|
|
182
195
|
if (forbiddenKey) throw new Error(`High-cardinality lineage value must not be used as a metric label: ${forbiddenKey}`);
|
|
@@ -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
|
|