@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
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
LoadSessionConfigOptions,
|
|
4
|
+
SessionConfigDiagnostics,
|
|
5
|
+
SessionConfigEffectiveSource,
|
|
6
|
+
SessionConfigEnvFileStatus,
|
|
7
|
+
SessionConfigEnvironmentStatus,
|
|
8
|
+
SessionConfigProjectStatus,
|
|
9
|
+
} from "../config/load-config.ts";
|
|
3
10
|
import { loadSessionConfigWithDiagnostics } from "../config/load-config.ts";
|
|
4
11
|
import type { CaptureConfig, ObservMeConfig } from "../config/schema.ts";
|
|
12
|
+
import {
|
|
13
|
+
describeGrafanaTransportSecurity,
|
|
14
|
+
describeOtlpTransportSecurity,
|
|
15
|
+
} from "../config/transport-security.ts";
|
|
5
16
|
import { getGrafanaQueryReadiness } from "../query/grafana-readiness.ts";
|
|
6
17
|
import { completeObsSubcommand, isExactObsSubcommandRequest } from "./obs-args.ts";
|
|
18
|
+
import { notifyObsCommand } from "./obs-command-support.ts";
|
|
7
19
|
import { sanitizeObsDiagnosticText } from "./obs-diagnostics.ts";
|
|
8
20
|
|
|
9
21
|
export interface ObsStatusCommandContext {
|
|
@@ -93,15 +105,15 @@ export async function handleObsStatusCommand(
|
|
|
93
105
|
options: RegisterObsStatusCommandOptions = {},
|
|
94
106
|
): Promise<void> {
|
|
95
107
|
if (!isObsStatusRequest(args)) {
|
|
96
|
-
await
|
|
108
|
+
await notifyObsCommand(ctx, "Usage: /obs status", "warning");
|
|
97
109
|
return;
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
try {
|
|
101
113
|
const snapshot = await resolveObsStatusSnapshot(ctx, options);
|
|
102
|
-
await
|
|
114
|
+
await notifyObsCommand(ctx, renderObsStatus(snapshot), "info");
|
|
103
115
|
} catch (error) {
|
|
104
|
-
await
|
|
116
|
+
await notifyObsCommand(ctx, `ObservMe status unavailable: ${sanitizeObsDiagnosticText(formatError(error))}`, "error");
|
|
105
117
|
}
|
|
106
118
|
}
|
|
107
119
|
|
|
@@ -129,8 +141,10 @@ export function renderObsStatus(snapshot: ObsStatusSnapshot): string {
|
|
|
129
141
|
const lines = [
|
|
130
142
|
`ObservMe: ${formatEnabled(config.enabled)}`,
|
|
131
143
|
`OTLP endpoint: ${formatSafeConfiguredEndpoint(config.otlp.endpoint)}`,
|
|
144
|
+
`OTLP transport security: ${describeOtlpTransportSecurity(config)}`,
|
|
132
145
|
...formatConfigDiagnosticsLines(snapshot.configDiagnostics),
|
|
133
146
|
`Grafana URL: ${formatSafeConfiguredUrl(config.query.grafana.url)}`,
|
|
147
|
+
`Grafana transport security: ${describeGrafanaTransportSecurity(config)}`,
|
|
134
148
|
`Grafana query readiness: ${formatGrafanaQueryReadiness(config)}`,
|
|
135
149
|
`Traces: ${formatEnabled(signalEnabled(config, config.traces.enabled))}`,
|
|
136
150
|
`Metrics: ${formatEnabled(signalEnabled(config, config.metrics.enabled))}`,
|
|
@@ -237,10 +251,6 @@ function isObsStatusRequest(args: string): boolean {
|
|
|
237
251
|
return isExactObsSubcommandRequest(args, OBS_STATUS_SUBCOMMAND, { allowEmpty: true });
|
|
238
252
|
}
|
|
239
253
|
|
|
240
|
-
async function notifyStatus(ctx: ObsStatusCommandContext, message: string, type: "info" | "warning" | "error"): Promise<void> {
|
|
241
|
-
await ctx.ui?.notify?.(message, type);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
254
|
function formatCaptureLines(capture: CaptureConfig): string[] {
|
|
245
255
|
return [
|
|
246
256
|
`Prompt capture: ${formatEnabled(capture.prompts)}`,
|
|
@@ -259,7 +269,10 @@ function formatConfigDiagnosticsLines(diagnostics: SessionConfigDiagnostics | un
|
|
|
259
269
|
|
|
260
270
|
return [
|
|
261
271
|
`Config source: ${formatConfigEffectiveSource(diagnostics.effectiveSource)}`,
|
|
262
|
-
|
|
272
|
+
...formatGlobalConfigStatus(diagnostics),
|
|
273
|
+
`Project config: ${formatProjectConfigStatus(diagnostics.projectConfigStatus)}`,
|
|
274
|
+
...formatEnvFileStatus(diagnostics.envFileStatus),
|
|
275
|
+
...formatEnvironmentStatus(diagnostics.environmentStatus),
|
|
263
276
|
...formatConfigRejectionLines(diagnostics),
|
|
264
277
|
];
|
|
265
278
|
}
|
|
@@ -281,13 +294,43 @@ function formatConfigEffectiveSource(source: SessionConfigEffectiveSource): stri
|
|
|
281
294
|
return "defaults";
|
|
282
295
|
}
|
|
283
296
|
|
|
284
|
-
function
|
|
285
|
-
if (diagnostics.
|
|
286
|
-
|
|
297
|
+
function formatGlobalConfigStatus(diagnostics: SessionConfigDiagnostics): string[] {
|
|
298
|
+
if (!diagnostics.globalConfigStatus) return [];
|
|
299
|
+
return [`Global config: ${formatFileSourceStatus(diagnostics.globalConfigStatus, "global config")}`];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function formatProjectConfigStatus(status: SessionConfigProjectStatus): string {
|
|
303
|
+
if (status === "loaded") return "loaded (trusted .pi/observme.yaml)";
|
|
304
|
+
if (status === "skipped_untrusted") {
|
|
287
305
|
return "skipped (project is untrusted; safe defaults/global/env only)";
|
|
288
306
|
}
|
|
307
|
+
if (status === "missing") return "missing (trusted project has no .pi/observme.yaml)";
|
|
308
|
+
return formatFileSourceStatus(status, "trusted .pi/observme.yaml");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function formatEnvFileStatus(status: SessionConfigEnvFileStatus | undefined): string[] {
|
|
312
|
+
if (!status) return [];
|
|
313
|
+
if (status === "skipped_untrusted") return ["Project .env: skipped (project is untrusted)"];
|
|
314
|
+
if (status === "skipped_disabled") return ["Project .env: skipped (loading is disabled)"];
|
|
315
|
+
return [`Project .env: ${formatFileSourceStatus(status, "trusted project .env")}`];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function formatEnvironmentStatus(status: SessionConfigEnvironmentStatus | undefined): string[] {
|
|
319
|
+
if (!status) return [];
|
|
320
|
+
if (status === "loaded") return ["Process environment: loaded"];
|
|
321
|
+
if (status === "rejected") return ["Process environment: rejected (malformed supported override)"];
|
|
322
|
+
return ["Process environment: no ObservMe values"];
|
|
323
|
+
}
|
|
289
324
|
|
|
290
|
-
|
|
325
|
+
function formatFileSourceStatus(
|
|
326
|
+
status: Exclude<SessionConfigProjectStatus, "skipped_untrusted">,
|
|
327
|
+
label: string,
|
|
328
|
+
): string {
|
|
329
|
+
if (status === "loaded") return "loaded";
|
|
330
|
+
if (status === "missing") return "missing";
|
|
331
|
+
if (status === "malformed") return `ignored (${label} is malformed)`;
|
|
332
|
+
if (status === "unreadable") return `ignored (${label} is unreadable)`;
|
|
333
|
+
return `ignored (${label} was structurally rejected)`;
|
|
291
334
|
}
|
|
292
335
|
|
|
293
336
|
function formatGrafanaQueryReadiness(config: ObservMeConfig): string {
|
|
@@ -3,6 +3,11 @@ import type { LoadSessionConfigOptions } from "../config/load-config.ts";
|
|
|
3
3
|
import type { ObservMeConfig } from "../config/schema.ts";
|
|
4
4
|
import type { PrometheusFetch, PrometheusMetricSeries, QueryResult } from "../query/prometheus.ts";
|
|
5
5
|
import { createPrometheusQueryClient } from "../query/prometheus.ts";
|
|
6
|
+
import {
|
|
7
|
+
boundObsCommandOutput,
|
|
8
|
+
normalizeObsBackendLabel,
|
|
9
|
+
selectObsCommandRows,
|
|
10
|
+
} from "../safety/display-bounds.ts";
|
|
6
11
|
import { completeObsSubcommand, isExactObsSubcommandRequest } from "./obs-args.ts";
|
|
7
12
|
import { loadObsCommandConfig, notifyObsCommand } from "./obs-command-support.ts";
|
|
8
13
|
import { appendObsRecoveryHint, formatObsCommandFailure } from "./obs-diagnostics.ts";
|
|
@@ -126,22 +131,29 @@ export async function getObsToolsSnapshot(
|
|
|
126
131
|
export function renderObsTools(snapshot: ObsToolsSnapshot): string {
|
|
127
132
|
const calls = snapshot.calls.map(normalizeObsToolCallRow).filter(isObsToolCallRow);
|
|
128
133
|
const failures = snapshot.failures.map(normalizeObsToolFailureRow).filter(isObsToolFailureRow);
|
|
129
|
-
const
|
|
134
|
+
const callSelection = selectObsCommandRows(calls);
|
|
135
|
+
const failureSelection = selectObsCommandRows(failures);
|
|
136
|
+
const window = normalizeObsBackendLabel(snapshot.window) ?? OBS_TOOLS_WINDOW;
|
|
137
|
+
const lines = [`Tool calls by tool (last ${window})`];
|
|
130
138
|
|
|
131
139
|
if (calls.length === 0) {
|
|
132
140
|
lines.push(appendObsRecoveryHint("No tool call metrics found.", OBS_TOOLS_NO_CALLS_NEXT_ACTION));
|
|
133
141
|
} else {
|
|
134
|
-
lines.push(...
|
|
142
|
+
lines.push(...callSelection.rows.map(renderObsToolCallRow));
|
|
143
|
+
if (callSelection.omittedCount > 0) lines.push(`… ${callSelection.omittedCount} tool call row(s) omitted`);
|
|
135
144
|
}
|
|
136
145
|
|
|
137
|
-
lines.push(`Tool failures by tool/error (last ${
|
|
146
|
+
lines.push(`Tool failures by tool/error (last ${window})`);
|
|
138
147
|
if (failures.length === 0) {
|
|
139
148
|
lines.push(appendObsRecoveryHint("No tool failure metrics found.", OBS_TOOLS_NO_FAILURES_NEXT_ACTION));
|
|
140
149
|
} else {
|
|
141
|
-
lines.push(...
|
|
150
|
+
lines.push(...failureSelection.rows.map(renderObsToolFailureRow));
|
|
151
|
+
if (failureSelection.omittedCount > 0) {
|
|
152
|
+
lines.push(`… ${failureSelection.omittedCount} tool failure row(s) omitted`);
|
|
153
|
+
}
|
|
142
154
|
}
|
|
143
155
|
|
|
144
|
-
return lines.join("\n");
|
|
156
|
+
return boundObsCommandOutput(lines.join("\n"));
|
|
145
157
|
}
|
|
146
158
|
|
|
147
159
|
class ObsToolsCommand {
|
|
@@ -231,12 +243,11 @@ function parseRatePerSecond(value: string | number | undefined): number | undefi
|
|
|
231
243
|
}
|
|
232
244
|
|
|
233
245
|
function normalizeMetricLabel(value: string | undefined): string {
|
|
234
|
-
return
|
|
246
|
+
return normalizeObsBackendLabel(value) ?? "unknown";
|
|
235
247
|
}
|
|
236
248
|
|
|
237
249
|
function normalizeOptionalString(value: string | undefined): string | undefined {
|
|
238
|
-
|
|
239
|
-
return trimmed || undefined;
|
|
250
|
+
return normalizeObsBackendLabel(value);
|
|
240
251
|
}
|
|
241
252
|
|
|
242
253
|
function renderObsToolCallRow(row: ObsToolCallRow): string {
|
|
@@ -92,6 +92,8 @@ const OBS_COMMAND_NAME = "obs";
|
|
|
92
92
|
const OBS_TRACE_SUBCOMMAND = "trace";
|
|
93
93
|
const OBS_TRACE_USAGE = "Usage: /obs trace [--last-turn|--session <session-id>]";
|
|
94
94
|
const OBS_TRACE_TEMPO_ERROR_NEXT_ACTION = "run /obs health and verify query.grafana.url, Grafana credentials, and the Tempo datasource UID.";
|
|
95
|
+
const OBS_TRACE_LINK_CONFIG_ERROR_NEXT_ACTION =
|
|
96
|
+
"set query.links.traceUrlTemplate to a supported trace-id placeholder or the ellipsis fallback, then verify query.grafana.url.";
|
|
95
97
|
const OBS_TRACE_SESSION_ERROR_NEXT_ACTION = "wait for a Pi turn or query a generated session id with /obs trace --session <session-id>.";
|
|
96
98
|
const OBS_TRACE_NOT_FOUND_NEXT_ACTION = "check the session id, wait for trace export, then verify Tempo datasource with /obs health.";
|
|
97
99
|
const OBS_TRACE_ACTIVE_SESSION_NOTE = "Trace visibility: active sessions may show ended child spans before the root pi.session span; the root is exported after session_shutdown.";
|
|
@@ -398,6 +400,9 @@ export function resolveObsTraceDiagnostic(error: unknown): ObsCommandRecoveryHin
|
|
|
398
400
|
const message = readObsDiagnosticMessage(error);
|
|
399
401
|
|
|
400
402
|
if (isObsTraceSessionErrorMessage(message)) return { subsystem: "Session trace", nextAction: OBS_TRACE_SESSION_ERROR_NEXT_ACTION };
|
|
403
|
+
if (isObsTraceLinkConfigErrorMessage(message)) {
|
|
404
|
+
return { subsystem: "Grafana trace link", nextAction: OBS_TRACE_LINK_CONFIG_ERROR_NEXT_ACTION };
|
|
405
|
+
}
|
|
401
406
|
if (isObsTraceNotFoundMessage(message)) return { subsystem: "Tempo", nextAction: OBS_TRACE_NOT_FOUND_NEXT_ACTION };
|
|
402
407
|
return { subsystem: "Tempo", nextAction: OBS_TRACE_TEMPO_ERROR_NEXT_ACTION };
|
|
403
408
|
}
|
|
@@ -406,6 +411,10 @@ function isObsTraceSessionErrorMessage(message: string): boolean {
|
|
|
406
411
|
return /No current ObservMe session trace|No last-turn ObservMe trace|Unsafe ObservMe session id/u.test(message);
|
|
407
412
|
}
|
|
408
413
|
|
|
414
|
+
function isObsTraceLinkConfigErrorMessage(message: string): boolean {
|
|
415
|
+
return /Grafana trace link configuration is invalid/u.test(message);
|
|
416
|
+
}
|
|
417
|
+
|
|
409
418
|
function isObsTraceNotFoundMessage(message: string): boolean {
|
|
410
419
|
return /No trace was found/u.test(message);
|
|
411
420
|
}
|
package/src/commands/obs.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { handleObsToolsCommand } from "./obs-tools.ts";
|
|
|
22
22
|
import type { ObsTraceCommandContext, RegisterObsTraceCommandOptions } from "./obs-trace.ts";
|
|
23
23
|
import { handleObsTraceCommand } from "./obs-trace.ts";
|
|
24
24
|
import { completeObsSubcommands, firstObsCommandToken, obsUsageWithError } from "./obs-args.ts";
|
|
25
|
+
import { notifyObsCommand } from "./obs-command-support.ts";
|
|
25
26
|
|
|
26
27
|
export interface ObsCommandContext
|
|
27
28
|
extends ObsStatusCommandContext,
|
|
@@ -153,7 +154,11 @@ export async function handleObsCommand(
|
|
|
153
154
|
return;
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
await
|
|
157
|
+
await notifyObsCommand(
|
|
158
|
+
ctx,
|
|
159
|
+
obsUsageWithError(OBS_ROOT_USAGE, subcommand ? `Unknown subcommand: ${subcommand}.` : undefined),
|
|
160
|
+
"warning",
|
|
161
|
+
);
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
export function getObsRootCommandArgumentCompletions(prefix: string): Array<{ value: string; label: string }> | null {
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { CONFIG_DIR_NAME, withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
SessionStartEvent,
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
4
6
|
import { readDiagnosticMessage, sanitizeDiagnosticText } from "../diagnostics/sanitize.ts";
|
|
5
7
|
import { RESOURCE_ATTRIBUTES } from "../semconv/attributes.ts";
|
|
6
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
createCanonicalProjectLocalFileExclusively,
|
|
10
|
+
resolveProjectLocalFilePath,
|
|
11
|
+
} from "./project-paths.ts";
|
|
12
|
+
import type {
|
|
13
|
+
ProjectLocalFileOperationHooks,
|
|
14
|
+
ProjectLocalFilePathOptions,
|
|
15
|
+
} from "./project-paths.ts";
|
|
7
16
|
|
|
8
17
|
export type ProjectConfigBootstrapStatus = "created" | "exists" | "skipped_untrusted";
|
|
9
18
|
export type ProjectConfigNotifyLevel = "info" | "warning" | "error";
|
|
@@ -30,14 +39,15 @@ export interface EnsureProjectConfigOptions {
|
|
|
30
39
|
readonly configDirName?: string;
|
|
31
40
|
readonly cwd?: string;
|
|
32
41
|
readonly isProjectTrusted?: boolean | (() => boolean | Promise<boolean>);
|
|
42
|
+
readonly projectFileOperationHooks?: ProjectLocalFileOperationHooks;
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
export type EnsureProjectConfig = (options: EnsureProjectConfigOptions) => Promise<ProjectConfigBootstrapResult>;
|
|
36
46
|
|
|
37
|
-
type ProjectConfigBootstrapHandler = (event:
|
|
47
|
+
type ProjectConfigBootstrapHandler = (event: SessionStartEvent, ctx: ExtensionContext) => Promise<void> | void;
|
|
38
48
|
|
|
39
49
|
interface ProjectConfigBootstrapPiApi {
|
|
40
|
-
on: (eventName:
|
|
50
|
+
on: (eventName: "session_start", handler: ProjectConfigBootstrapHandler) => void;
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
const observmeYamlFileName = "observme.yaml";
|
|
@@ -46,7 +56,6 @@ export const PROJECT_OBSERVME_YAML_TEMPLATE = `observme:
|
|
|
46
56
|
enabled: true
|
|
47
57
|
environment: development
|
|
48
58
|
tenant: local-dev
|
|
49
|
-
replayOnStart: false
|
|
50
59
|
|
|
51
60
|
otlp:
|
|
52
61
|
endpoint: http://localhost:4318
|
|
@@ -54,7 +63,6 @@ export const PROJECT_OBSERVME_YAML_TEMPLATE = `observme:
|
|
|
54
63
|
timeoutMs: 3000
|
|
55
64
|
headers: {}
|
|
56
65
|
tls:
|
|
57
|
-
enabled: false
|
|
58
66
|
insecureSkipVerify: false
|
|
59
67
|
signalEndpoints:
|
|
60
68
|
traces: http://localhost:4318/v1/traces
|
|
@@ -86,6 +94,8 @@ export const PROJECT_OBSERVME_YAML_TEMPLATE = `observme:
|
|
|
86
94
|
propagateTraceContext: true
|
|
87
95
|
propagateToSubagents: true
|
|
88
96
|
capabilityEnv: OBSERVME_AGENT_CAPABILITY
|
|
97
|
+
# Opt in to one validated, branch-local custom entry for reload/resume recovery.
|
|
98
|
+
# This entry never participates in LLM context.
|
|
89
99
|
writeCorrelationEntry: false
|
|
90
100
|
|
|
91
101
|
traces:
|
|
@@ -101,6 +111,7 @@ export const PROJECT_OBSERVME_YAML_TEMPLATE = `observme:
|
|
|
101
111
|
enabled: true
|
|
102
112
|
exportIntervalMillis: 15000
|
|
103
113
|
exportTimeoutMillis: 3000
|
|
114
|
+
activeAgentLeaseDurationMillis: 60000
|
|
104
115
|
|
|
105
116
|
logs:
|
|
106
117
|
enabled: true
|
|
@@ -191,11 +202,16 @@ export function registerProjectConfigBootstrap(
|
|
|
191
202
|
export async function ensureProjectObservMeConfig(
|
|
192
203
|
options: EnsureProjectConfigOptions = {},
|
|
193
204
|
): Promise<ProjectConfigBootstrapResult> {
|
|
194
|
-
const
|
|
205
|
+
const pathOptions = createProjectObservMeConfigPathOptions(options);
|
|
206
|
+
const configPath = resolveProjectLocalFilePath(pathOptions);
|
|
195
207
|
const projectTrusted = await resolveBootstrapProjectTrust(options.isProjectTrusted);
|
|
196
208
|
|
|
197
209
|
if (!projectTrusted) return { path: configPath, status: "skipped_untrusted" };
|
|
198
|
-
return createProjectObservMeConfigFile(
|
|
210
|
+
return createProjectObservMeConfigFile(
|
|
211
|
+
configPath,
|
|
212
|
+
pathOptions,
|
|
213
|
+
options.projectFileOperationHooks,
|
|
214
|
+
);
|
|
199
215
|
}
|
|
200
216
|
|
|
201
217
|
function createProjectConfigBootstrapHandler(
|
|
@@ -253,30 +269,40 @@ async function notifyProjectConfigBootstrapFailed(ctx: ProjectConfigBootstrapCon
|
|
|
253
269
|
await ctx.ui?.notify?.(`ObservMe could not create the project config file: ${formatError(error)}`, "warning");
|
|
254
270
|
}
|
|
255
271
|
|
|
256
|
-
async function createProjectObservMeConfigFile(
|
|
257
|
-
|
|
272
|
+
async function createProjectObservMeConfigFile(
|
|
273
|
+
configPath: string,
|
|
274
|
+
pathOptions: ProjectLocalFilePathOptions,
|
|
275
|
+
hooks: ProjectLocalFileOperationHooks | undefined,
|
|
276
|
+
): Promise<ProjectConfigBootstrapResult> {
|
|
277
|
+
return withFileMutationQueue(
|
|
278
|
+
configPath,
|
|
279
|
+
createProjectObservMeConfigFileInQueue.bind(undefined, configPath, pathOptions, hooks),
|
|
280
|
+
);
|
|
258
281
|
}
|
|
259
282
|
|
|
260
|
-
async function createProjectObservMeConfigFileInQueue(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
283
|
+
async function createProjectObservMeConfigFileInQueue(
|
|
284
|
+
configPath: string,
|
|
285
|
+
pathOptions: ProjectLocalFilePathOptions,
|
|
286
|
+
hooks: ProjectLocalFileOperationHooks | undefined,
|
|
287
|
+
): Promise<ProjectConfigBootstrapResult> {
|
|
288
|
+
const status = await createCanonicalProjectLocalFileExclusively(
|
|
289
|
+
pathOptions,
|
|
290
|
+
PROJECT_OBSERVME_YAML_TEMPLATE,
|
|
291
|
+
hooks,
|
|
292
|
+
);
|
|
293
|
+
return { path: configPath, status };
|
|
270
294
|
}
|
|
271
295
|
|
|
272
|
-
function
|
|
273
|
-
|
|
296
|
+
function createProjectObservMeConfigPathOptions(
|
|
297
|
+
options: EnsureProjectConfigOptions,
|
|
298
|
+
): ProjectLocalFilePathOptions {
|
|
299
|
+
return {
|
|
274
300
|
cwd: options.cwd,
|
|
275
301
|
configDirName: options.configDirName,
|
|
276
302
|
defaultConfigDirName: CONFIG_DIR_NAME,
|
|
277
303
|
fileName: observmeYamlFileName,
|
|
278
304
|
inputLabel: "project config path",
|
|
279
|
-
}
|
|
305
|
+
};
|
|
280
306
|
}
|
|
281
307
|
|
|
282
308
|
async function resolveBootstrapProjectTrust(
|
|
@@ -287,14 +313,6 @@ async function resolveBootstrapProjectTrust(
|
|
|
287
313
|
return false;
|
|
288
314
|
}
|
|
289
315
|
|
|
290
|
-
function isFileAlreadyExistsError(error: unknown): boolean {
|
|
291
|
-
return isErrorWithCode(error) && error.code === "EEXIST";
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function isErrorWithCode(error: unknown): error is NodeJS.ErrnoException {
|
|
295
|
-
return typeof error === "object" && error !== null && "code" in error;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
316
|
function formatError(error: unknown): string {
|
|
299
317
|
return sanitizeDiagnosticText(readDiagnosticMessage(error));
|
|
300
318
|
}
|
package/src/config/defaults.ts
CHANGED
|
@@ -5,7 +5,6 @@ export const defaultObservMeConfig = {
|
|
|
5
5
|
enabled: true,
|
|
6
6
|
environment: "production",
|
|
7
7
|
tenant: "platform",
|
|
8
|
-
replayOnStart: false,
|
|
9
8
|
otlp: {
|
|
10
9
|
endpoint: "https://otel-collector.example.com:4318",
|
|
11
10
|
protocol: "http/protobuf",
|
|
@@ -14,7 +13,6 @@ export const defaultObservMeConfig = {
|
|
|
14
13
|
Authorization: "Bearer ${OBSERVME_OTLP_TOKEN}",
|
|
15
14
|
},
|
|
16
15
|
tls: {
|
|
17
|
-
enabled: true,
|
|
18
16
|
insecureSkipVerify: false,
|
|
19
17
|
},
|
|
20
18
|
},
|
|
@@ -60,6 +58,7 @@ export const defaultObservMeConfig = {
|
|
|
60
58
|
enabled: true,
|
|
61
59
|
exportIntervalMillis: 15000,
|
|
62
60
|
exportTimeoutMillis: 3000,
|
|
61
|
+
activeAgentLeaseDurationMillis: 60000,
|
|
63
62
|
},
|
|
64
63
|
logs: {
|
|
65
64
|
enabled: true,
|