@opengeni/observability 0.7.5 → 0.8.3-canary.0
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/dist/index.d.ts +72 -0
- package/dist/index.js +131 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +184 -9
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export type ObservabilitySettings = {
|
|
|
8
8
|
observabilityMetricsEnabled: boolean;
|
|
9
9
|
observabilityOtlpEndpoint?: string | undefined;
|
|
10
10
|
observabilityOtlpHeaders: string;
|
|
11
|
+
sandboxOwnershipEnabled?: boolean | undefined;
|
|
12
|
+
sandboxLazyProvisionEnabled?: boolean | undefined;
|
|
13
|
+
rigVerificationLeaseOwnershipEnabled?: boolean | undefined;
|
|
11
14
|
};
|
|
12
15
|
export type ObservabilityOptions = {
|
|
13
16
|
component: string;
|
|
@@ -42,6 +45,67 @@ export declare const OPENGENI_OBSERVABILITY_DISTRIBUTION: {
|
|
|
42
45
|
readonly grafanaDashboardLabel: "grafana_dashboard";
|
|
43
46
|
readonly grafanaDashboardLabelValue: "1";
|
|
44
47
|
};
|
|
48
|
+
/**
|
|
49
|
+
* Closed set of organization-tenancy compatibility lanes (the tenancy
|
|
50
|
+
* migration's validate phase; see `docs/organization-tenancy.md`).
|
|
51
|
+
*
|
|
52
|
+
* Each entry names one legacy behaviour a live request can still take today.
|
|
53
|
+
* The metric built from them is deliberately a COUNTER of lane USES, and it
|
|
54
|
+
* measures exactly one thing: **is this lane still being exercised?** That is
|
|
55
|
+
* the question the tenancy cutover gate ("zero incompatible writers") actually
|
|
56
|
+
* asks, and it is answerable.
|
|
57
|
+
*
|
|
58
|
+
* It is emphatically NOT a burndown of unmigrated rows, and must never be
|
|
59
|
+
* relabelled as one. Every one of these lanes is still *open*: the live write
|
|
60
|
+
* paths keep producing rows in it, so no row-count predicate over any of them
|
|
61
|
+
* can reach zero and a backlog gauge would read as a permanent, untruthful
|
|
62
|
+
* unmigrated backlog. `docs/organization-tenancy.md` records, per lane, why -
|
|
63
|
+
* including the lanes deliberately left uninstrumented because no truthful
|
|
64
|
+
* signal exists for them yet.
|
|
65
|
+
*
|
|
66
|
+
* The recorded value is the lane name and nothing else. No organization,
|
|
67
|
+
* workspace, session, subject, connection, or resource identity, and no
|
|
68
|
+
* credential, name, or content, may ever become a label here.
|
|
69
|
+
*/
|
|
70
|
+
export declare const TENANCY_COMPATIBILITY_LANES: readonly [
|
|
71
|
+
/**
|
|
72
|
+
* A provider connection carrying `authority_scope = 'legacy_user'` was
|
|
73
|
+
* resolved for accepted use: a personal row with no common authority or
|
|
74
|
+
* grant, admitted through `legacy_user_compatibility` provenance instead of
|
|
75
|
+
* an explicit delegation. Migration 0256 backfilled the existing rows, but
|
|
76
|
+
* its `bind_connection_authority` trigger still classifies a NEW personal
|
|
77
|
+
* connection this way whenever the inserting subject has no active
|
|
78
|
+
* organization membership, so this counter is expected to stay above zero
|
|
79
|
+
* until that classification changes. That is the finding, not a defect.
|
|
80
|
+
*/
|
|
81
|
+
"connection_legacy_user",
|
|
82
|
+
/**
|
|
83
|
+
* A workspace-scope connection ref carried no connection id, so the
|
|
84
|
+
* accepted-use authority (migration 0279) could not identify the exact row
|
|
85
|
+
* and the request fell back to the unprivileged pre-snapshot resolution.
|
|
86
|
+
* This lane writes no `connection_use_audit_facts` row and leaves no durable
|
|
87
|
+
* trace of any kind, so a use counter is the only possible evidence it was
|
|
88
|
+
* taken at all.
|
|
89
|
+
*/
|
|
90
|
+
"connection_pre_snapshot_ref",
|
|
91
|
+
/**
|
|
92
|
+
* A NEW persistable `/workspace` mutation was refused because the writer
|
|
93
|
+
* behind it has no recorded authority (`authority_unattributed`): either a
|
|
94
|
+
* direct request whose principal froze the pre-0096 legacy initiator, or a
|
|
95
|
+
* retained process whose pre-0277 row has no tenancy half. This counts
|
|
96
|
+
* REFUSED MUTATIONS, not unattributed rows - the turn and process admission
|
|
97
|
+
* lanes deliberately still accept a `legacy_unattributed` initiator, and
|
|
98
|
+
* nothing may invent the missing authority or disturb the running provider
|
|
99
|
+
* process.
|
|
100
|
+
*/
|
|
101
|
+
"workspace_writer_unattributed"];
|
|
102
|
+
export type TenancyCompatibilityLane = (typeof TENANCY_COMPATIBILITY_LANES)[number];
|
|
103
|
+
/**
|
|
104
|
+
* Record one live use of a tenancy compatibility lane. Fail-safe by
|
|
105
|
+
* construction: telemetry must never change an authorization or credential
|
|
106
|
+
* outcome, so a registry failure is counted best-effort and swallowed here.
|
|
107
|
+
*/
|
|
108
|
+
export declare function recordTenancyCompatibilityLaneUse(observability: Observability | null | undefined, lane: TenancyCompatibilityLane): void;
|
|
45
109
|
export type SandboxOperationMetricObservation = {
|
|
46
110
|
backend: string;
|
|
47
111
|
op: string;
|
|
@@ -61,6 +125,14 @@ export declare class Observability {
|
|
|
61
125
|
private readonly exporter;
|
|
62
126
|
private readonly resourceAttributes;
|
|
63
127
|
constructor(settings: ObservabilitySettings, options: ObservabilityOptions);
|
|
128
|
+
/**
|
|
129
|
+
* Publish every compatibility lane at zero on startup. Without this an
|
|
130
|
+
* un-exercised lane has no series at all, and an operator cannot tell "this
|
|
131
|
+
* lane is dormant" from "this lane was never wired up" - the two readings
|
|
132
|
+
* that matter most to a cutover gate.
|
|
133
|
+
*/
|
|
134
|
+
private registerTenancyCompatibilityLanes;
|
|
135
|
+
private registerSandboxRolloutConfig;
|
|
64
136
|
debug(message: string, attributes?: Attributes): void;
|
|
65
137
|
info(message: string, attributes?: Attributes): void;
|
|
66
138
|
warn(message: string, attributes?: Attributes): void;
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,53 @@ var SANDBOX_OPERATION_NAMES = /* @__PURE__ */ new Set([
|
|
|
50
50
|
"resolveExposedPort",
|
|
51
51
|
"serializeSessionState"
|
|
52
52
|
]);
|
|
53
|
+
var TENANCY_COMPATIBILITY_LANES = [
|
|
54
|
+
/**
|
|
55
|
+
* A provider connection carrying `authority_scope = 'legacy_user'` was
|
|
56
|
+
* resolved for accepted use: a personal row with no common authority or
|
|
57
|
+
* grant, admitted through `legacy_user_compatibility` provenance instead of
|
|
58
|
+
* an explicit delegation. Migration 0256 backfilled the existing rows, but
|
|
59
|
+
* its `bind_connection_authority` trigger still classifies a NEW personal
|
|
60
|
+
* connection this way whenever the inserting subject has no active
|
|
61
|
+
* organization membership, so this counter is expected to stay above zero
|
|
62
|
+
* until that classification changes. That is the finding, not a defect.
|
|
63
|
+
*/
|
|
64
|
+
"connection_legacy_user",
|
|
65
|
+
/**
|
|
66
|
+
* A workspace-scope connection ref carried no connection id, so the
|
|
67
|
+
* accepted-use authority (migration 0279) could not identify the exact row
|
|
68
|
+
* and the request fell back to the unprivileged pre-snapshot resolution.
|
|
69
|
+
* This lane writes no `connection_use_audit_facts` row and leaves no durable
|
|
70
|
+
* trace of any kind, so a use counter is the only possible evidence it was
|
|
71
|
+
* taken at all.
|
|
72
|
+
*/
|
|
73
|
+
"connection_pre_snapshot_ref",
|
|
74
|
+
/**
|
|
75
|
+
* A NEW persistable `/workspace` mutation was refused because the writer
|
|
76
|
+
* behind it has no recorded authority (`authority_unattributed`): either a
|
|
77
|
+
* direct request whose principal froze the pre-0096 legacy initiator, or a
|
|
78
|
+
* retained process whose pre-0277 row has no tenancy half. This counts
|
|
79
|
+
* REFUSED MUTATIONS, not unattributed rows - the turn and process admission
|
|
80
|
+
* lanes deliberately still accept a `legacy_unattributed` initiator, and
|
|
81
|
+
* nothing may invent the missing authority or disturb the running provider
|
|
82
|
+
* process.
|
|
83
|
+
*/
|
|
84
|
+
"workspace_writer_unattributed"
|
|
85
|
+
];
|
|
86
|
+
var TENANCY_COMPATIBILITY_LANE_SET = new Set(TENANCY_COMPATIBILITY_LANES);
|
|
87
|
+
var TENANCY_COMPATIBILITY_LANE_METRIC = {
|
|
88
|
+
name: "opengeni_tenancy_compatibility_lane_uses_total",
|
|
89
|
+
help: "Live uses of an organization-tenancy compatibility lane, by lane."
|
|
90
|
+
};
|
|
91
|
+
function recordTenancyCompatibilityLaneUse(observability, lane) {
|
|
92
|
+
if (!observability) return;
|
|
93
|
+
if (!TENANCY_COMPATIBILITY_LANE_SET.has(lane)) return;
|
|
94
|
+
try {
|
|
95
|
+
observability.incrementCounter({ ...TENANCY_COMPATIBILITY_LANE_METRIC, labels: { lane } });
|
|
96
|
+
} catch {
|
|
97
|
+
recordObserverFailure(observability, "tenancy_compatibility_lane");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
53
100
|
var INTERACTION_RESOURCES = /* @__PURE__ */ new Set(["browser", "computer"]);
|
|
54
101
|
var INTERACTION_OPERATIONS = /* @__PURE__ */ new Set([
|
|
55
102
|
"create",
|
|
@@ -114,6 +161,17 @@ var INTERACTION_INTERVENTION_OUTCOMES = /* @__PURE__ */ new Set([
|
|
|
114
161
|
"expired",
|
|
115
162
|
"cancelled"
|
|
116
163
|
]);
|
|
164
|
+
var PUBLIC_STARTUP_DEPENDENCIES = /* @__PURE__ */ new Set([
|
|
165
|
+
"Modal private-registry image",
|
|
166
|
+
"NATS",
|
|
167
|
+
"PostgreSQL runtime posture",
|
|
168
|
+
"Temporal",
|
|
169
|
+
"Temporal client",
|
|
170
|
+
"Temporal schedule (file upload reaper)",
|
|
171
|
+
"Temporal schedule (sandbox reaper)",
|
|
172
|
+
"Temporal schedule (session-workflow wake dispatcher)",
|
|
173
|
+
"Temporal schedule (site auth maintenance)"
|
|
174
|
+
]);
|
|
117
175
|
var PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
|
|
118
176
|
"http.request.method",
|
|
119
177
|
"http.response.status_code",
|
|
@@ -157,7 +215,8 @@ var PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
|
|
|
157
215
|
"retainedOutputKind"
|
|
158
216
|
]);
|
|
159
217
|
var PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS = /* @__PURE__ */ new Map([
|
|
160
|
-
["sandboxLeaseKey", /^slk_[0-9a-f]{32}$/]
|
|
218
|
+
["sandboxLeaseKey", /^slk_[0-9a-f]{32}$/],
|
|
219
|
+
["correlationId", /^[A-Za-z0-9._:-]{1,128}$/]
|
|
161
220
|
]);
|
|
162
221
|
var PUBLIC_CHANNEL_A_OPERATIONS = /* @__PURE__ */ new Set([
|
|
163
222
|
"fs.list",
|
|
@@ -329,6 +388,8 @@ var Observability = class {
|
|
|
329
388
|
},
|
|
330
389
|
value: 1
|
|
331
390
|
});
|
|
391
|
+
this.registerSandboxRolloutConfig();
|
|
392
|
+
this.registerTenancyCompatibilityLanes();
|
|
332
393
|
}
|
|
333
394
|
}
|
|
334
395
|
registry = new Registry();
|
|
@@ -339,6 +400,41 @@ var Observability = class {
|
|
|
339
400
|
now;
|
|
340
401
|
exporter;
|
|
341
402
|
resourceAttributes;
|
|
403
|
+
/**
|
|
404
|
+
* Publish every compatibility lane at zero on startup. Without this an
|
|
405
|
+
* un-exercised lane has no series at all, and an operator cannot tell "this
|
|
406
|
+
* lane is dormant" from "this lane was never wired up" - the two readings
|
|
407
|
+
* that matter most to a cutover gate.
|
|
408
|
+
*/
|
|
409
|
+
registerTenancyCompatibilityLanes() {
|
|
410
|
+
for (const lane of TENANCY_COMPATIBILITY_LANES) {
|
|
411
|
+
this.incrementCounter({ ...TENANCY_COMPATIBILITY_LANE_METRIC, labels: { lane }, amount: 0 });
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
registerSandboxRolloutConfig() {
|
|
415
|
+
const ownership = this.settings.sandboxOwnershipEnabled;
|
|
416
|
+
const lazyConfigured = this.settings.sandboxLazyProvisionEnabled;
|
|
417
|
+
const rigVerification = this.settings.rigVerificationLeaseOwnershipEnabled;
|
|
418
|
+
if (typeof ownership !== "boolean" || typeof lazyConfigured !== "boolean" || typeof rigVerification !== "boolean") {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const values = [
|
|
422
|
+
["ownership", "configured", ownership],
|
|
423
|
+
["ownership", "effective", ownership],
|
|
424
|
+
["lazy_provision", "configured", lazyConfigured],
|
|
425
|
+
["lazy_provision", "effective", ownership && lazyConfigured],
|
|
426
|
+
["rig_verification_lease_ownership", "configured", rigVerification],
|
|
427
|
+
["rig_verification_lease_ownership", "effective", rigVerification]
|
|
428
|
+
];
|
|
429
|
+
for (const [feature, state, enabled] of values) {
|
|
430
|
+
this.setGauge({
|
|
431
|
+
name: "opengeni_sandbox_rollout_config",
|
|
432
|
+
help: "Configured and effective sandbox rollout state for this workload revision.",
|
|
433
|
+
labels: { feature, state },
|
|
434
|
+
value: enabled ? 1 : 0
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
342
438
|
debug(message, attributes = {}) {
|
|
343
439
|
this.log("debug", message, attributes);
|
|
344
440
|
}
|
|
@@ -713,15 +809,19 @@ function recordObserverFailure(observability, observer) {
|
|
|
713
809
|
}
|
|
714
810
|
}
|
|
715
811
|
function logStartupDependencyRetry(observability, event) {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
attempt
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
812
|
+
const dependency = PUBLIC_STARTUP_DEPENDENCIES.has(event.label) ? event.label : "Dependency";
|
|
813
|
+
observability.warn(
|
|
814
|
+
`Startup dependency ${dependency} connection failed; retrying (${event.attempt}/${event.attempts} in ${event.delayMs}ms)`,
|
|
815
|
+
{
|
|
816
|
+
dependency,
|
|
817
|
+
attempt: event.attempt,
|
|
818
|
+
attempts: event.attempts,
|
|
819
|
+
delayMs: event.delayMs,
|
|
820
|
+
errorClass: "StartupDependencyError",
|
|
821
|
+
errorCode: "startup_dependency_retry",
|
|
822
|
+
origin: "observability"
|
|
823
|
+
}
|
|
824
|
+
);
|
|
725
825
|
}
|
|
726
826
|
function normalizeLabels(labels = {}) {
|
|
727
827
|
return Object.fromEntries(
|
|
@@ -739,6 +839,7 @@ function cleanAttributes(attributes) {
|
|
|
739
839
|
function projectPublicTelemetryAttributes(attributes) {
|
|
740
840
|
if ("errorClass" in attributes || "errorCode" in attributes) {
|
|
741
841
|
return {
|
|
842
|
+
...projectStartupDependencyAttributes(attributes),
|
|
742
843
|
...projectPublicChannelADiagnosticAttributes(attributes),
|
|
743
844
|
...projectPublicDiagnosticAttributes(attributes)
|
|
744
845
|
};
|
|
@@ -752,6 +853,21 @@ function projectPublicTelemetryAttributes(attributes) {
|
|
|
752
853
|
})
|
|
753
854
|
);
|
|
754
855
|
}
|
|
856
|
+
function projectStartupDependencyAttributes(attributes) {
|
|
857
|
+
if (attributes.errorClass !== "StartupDependencyError" || attributes.errorCode !== "startup_dependency_retry") {
|
|
858
|
+
return {};
|
|
859
|
+
}
|
|
860
|
+
const dependency = attributes.dependency;
|
|
861
|
+
const attempt = attributes.attempt;
|
|
862
|
+
const attempts = attributes.attempts;
|
|
863
|
+
const delayMs = attributes.delayMs;
|
|
864
|
+
return {
|
|
865
|
+
...typeof dependency === "string" && PUBLIC_STARTUP_DEPENDENCIES.has(dependency) ? { dependency } : {},
|
|
866
|
+
...typeof attempt === "number" && Number.isInteger(attempt) && attempt > 0 ? { attempt } : {},
|
|
867
|
+
...typeof attempts === "number" && Number.isInteger(attempts) && attempts > 0 ? { attempts } : {},
|
|
868
|
+
...typeof delayMs === "number" && Number.isFinite(delayMs) && delayMs >= 0 ? { delayMs } : {}
|
|
869
|
+
};
|
|
870
|
+
}
|
|
755
871
|
function projectPublicChannelADiagnosticAttributes(attributes) {
|
|
756
872
|
if (attributes.errorClass !== "SandboxChannelAOperationError") return {};
|
|
757
873
|
const backend = attributes.backend;
|
|
@@ -774,11 +890,13 @@ function projectPublicDiagnosticAttributes(attributes) {
|
|
|
774
890
|
const errorCode = attributes.errorCode;
|
|
775
891
|
const status = attributes.status;
|
|
776
892
|
const origin = attributes.origin;
|
|
893
|
+
const correlationId = attributes.correlationId;
|
|
777
894
|
return {
|
|
778
895
|
errorClass: typeof errorClass === "string" && PUBLIC_TELEMETRY_ERROR_CLASSES.has(errorClass) ? errorClass : "OperationError",
|
|
779
896
|
...typeof errorCode === "string" && PUBLIC_TELEMETRY_ERROR_CODES.has(errorCode) ? { errorCode } : {},
|
|
780
897
|
...typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599 ? { status } : {},
|
|
781
|
-
...typeof origin === "string" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {}
|
|
898
|
+
...typeof origin === "string" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {},
|
|
899
|
+
...typeof correlationId === "string" && PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS.get("correlationId")?.test(correlationId) ? { correlationId } : {}
|
|
782
900
|
};
|
|
783
901
|
}
|
|
784
902
|
function projectSpanErrorForTelemetry(error) {
|
|
@@ -861,12 +979,14 @@ async function defaultExporter(url, body, headers) {
|
|
|
861
979
|
export {
|
|
862
980
|
OPENGENI_OBSERVABILITY_DISTRIBUTION,
|
|
863
981
|
Observability,
|
|
982
|
+
TENANCY_COMPATIBILITY_LANES,
|
|
864
983
|
createObservability,
|
|
865
984
|
interactionAuthMetricObserver,
|
|
866
985
|
interactionInterventionMetricObserver,
|
|
867
986
|
interactionOperationMetricObserver,
|
|
868
987
|
logStartupDependencyRetry,
|
|
869
988
|
parseHeaders,
|
|
989
|
+
recordTenancyCompatibilityLaneUse,
|
|
870
990
|
sandboxLeaseTelemetryKey,
|
|
871
991
|
sandboxOperationMetricObserver
|
|
872
992
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { collectDefaultMetrics, Counter, Gauge, Histogram, Registry } from \"prom-client\";\nimport { SandboxBackend } from \"@opengeni/contracts\";\n\nexport type AttributeValue = string | number | boolean | null | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\nexport type ObservabilitySettings = {\n serviceName: string;\n environment: string;\n deploymentRevision?: string | undefined;\n observabilityStructuredLogs: boolean;\n observabilityMetricsEnabled: boolean;\n observabilityOtlpEndpoint?: string | undefined;\n observabilityOtlpHeaders: string;\n};\n\nexport type ObservabilityOptions = {\n component: string;\n now?: () => number;\n exporter?: (url: string, body: unknown, headers: Record<string, string>) => Promise<void>;\n};\n\nexport type Span = {\n traceId: string;\n spanId: string;\n end: (input?: { attributes?: Attributes; error?: unknown }) => void;\n};\n\nexport type MetricLabels = Record<string, AttributeValue>;\n\n/**\n * Stable, non-reversible correlation key for one logical sandbox lease. Public\n * telemetry intentionally drops workspace/group identifiers; this key lets an\n * operator correlate API, worker, and reaper failures without publishing\n * either UUID. The domain separator prevents reuse as a generic identifier\n * digest.\n */\nexport function sandboxLeaseTelemetryKey(workspaceId: string, sandboxGroupId: string): string {\n return `slk_${createHash(\"sha256\")\n .update(\"opengeni:sandbox-lease-telemetry:v1\\0\")\n .update(workspaceId)\n .update(\"\\0\")\n .update(sandboxGroupId)\n .digest(\"hex\")\n .slice(0, 32)}`;\n}\n\n/**\n * Stable selectors shared by OpenGeni's runtime metrics and optional\n * Prometheus/Grafana distribution. Operators can use these values for custom\n * namespaces and dashboard ConfigMaps without duplicating chart internals.\n */\nexport const OPENGENI_OBSERVABILITY_DISTRIBUTION = {\n monitoringNamespaceLabel: \"opengeni.ai/monitoring\",\n monitoringNamespaceLabelValue: \"enabled\",\n grafanaDashboardLabel: \"grafana_dashboard\",\n grafanaDashboardLabelValue: \"1\",\n} as const;\n\nconst httpHistogramBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\nconst durationHistogramBuckets = [\n 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 900, 1800, 3600,\n];\n\nconst SANDBOX_OPERATION_BACKENDS = new Set<string>([...SandboxBackend.options, \"unprovisioned\"]);\n\nconst SANDBOX_OPERATION_NAMES = new Set([\n \"desktopInput\",\n \"screenshot\",\n \"exec\",\n \"execCommand\",\n \"writeStdin\",\n \"cancelExecCommand\",\n \"readFile\",\n \"writeFile\",\n \"listDir\",\n \"pathExists\",\n \"viewImage\",\n \"materializeEntry\",\n \"editor.createFile\",\n \"editor.updateFile\",\n \"editor.deleteFile\",\n \"resolveExposedPort\",\n \"serializeSessionState\",\n]);\n\nconst INTERACTION_RESOURCES = new Set([\"browser\", \"computer\"]);\nconst INTERACTION_OPERATIONS = new Set([\n \"create\",\n \"end\",\n \"suspend\",\n \"resume\",\n \"publish\",\n \"observe\",\n \"act\",\n \"open_target\",\n \"select_target\",\n \"close_target\",\n \"attach\",\n \"auth_start\",\n \"auth_report\",\n \"auth_fill\",\n \"auth_verify\",\n \"intervention_create\",\n \"intervention_resolve\",\n]);\nconst INTERACTION_OUTCOMES = new Set([\n \"prepared\",\n \"dispatched\",\n \"completed\",\n \"failed\",\n \"outcome_unknown\",\n \"stale\",\n \"cancelled\",\n]);\nconst INTERACTION_MODES = new Set([\n \"semantic\",\n \"coordinate\",\n \"keyboard\",\n \"clipboard\",\n \"permission\",\n \"lifecycle\",\n \"media\",\n \"auth\",\n \"human\",\n]);\nconst INTERACTION_AUTH_STATES = new Set([\n \"discovering\",\n \"awaiting_choice\",\n \"awaiting_secret\",\n \"awaiting_external_action\",\n \"working\",\n \"verified\",\n \"failed\",\n \"cancelled\",\n]);\nconst INTERACTION_INTERVENTION_KINDS = new Set([\n \"manual_login\",\n \"mfa\",\n \"external_action\",\n \"confirmation\",\n \"other\",\n]);\nconst INTERACTION_INTERVENTION_OUTCOMES = new Set([\n \"opened\",\n \"completed\",\n \"dismissed\",\n \"expired\",\n \"cancelled\",\n]);\n\n/**\n * External logs and OTLP are public/third-party projections, not canonical\n * OpenGeni storage. Only this reviewed closed set of operational fields may\n * cross that boundary. Unknown keys are omitted regardless of their value, so\n * a new diagnostic, identifier, command, response, or provider field cannot\n * become public by accident. This is schema projection, never value inspection\n * or rewriting.\n */\nconst PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = new Set([\n \"http.request.method\",\n \"http.response.status_code\",\n \"opengeni.route\",\n \"opengeni.duration_ms\",\n \"opengeni.finalization_duration_ms\",\n \"opengeni.trigger_kind\",\n \"opengeni.status\",\n \"error.type\",\n \"error.status_code\",\n \"method\",\n \"route\",\n \"status\",\n \"durationMs\",\n \"attempt\",\n \"attempts\",\n \"delayMs\",\n \"provider\",\n \"providerApi\",\n \"model\",\n \"inputTokens\",\n \"outputTokens\",\n \"cachedTokens\",\n \"cacheWriteTokens\",\n \"reasoningTokens\",\n \"accountChangedFromPrevCall\",\n \"rejectedFields\",\n \"dependency\",\n \"activity\",\n \"backend\",\n \"op\",\n \"outcome\",\n \"eventType\",\n \"surface\",\n \"reason\",\n \"originalBytes\",\n \"deliveredBytes\",\n \"estimatedOriginalTokens\",\n \"estimatedDeliveredTokens\",\n \"fullEvidenceAvailable\",\n \"retainedOutputKind\",\n]);\n\n/** Opaque correlation fields require both a reviewed name and a closed value\n * grammar. Merely adding one to the ordinary allow-list would let an unrelated\n * caller accidentally publish a raw identifier under that name. */\nconst PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS = new Map<string, RegExp>([\n [\"sandboxLeaseKey\", /^slk_[0-9a-f]{32}$/],\n]);\n\nconst PUBLIC_CHANNEL_A_OPERATIONS = new Set([\n \"fs.list\",\n \"fs.list-batch\",\n \"fs.read\",\n \"fs.write\",\n \"fs.delete\",\n \"fs.move\",\n \"fs.mkdir\",\n \"git.status\",\n \"git.diff\",\n \"git.read-batch\",\n \"git.log\",\n \"git.show\",\n \"terminal.exec\",\n \"terminal.pty.open\",\n \"terminal.pty.write\",\n \"terminal.pty.resize\",\n \"terminal.pty.close\",\n \"read\",\n \"mutation\",\n]);\n\nconst PUBLIC_CHANNEL_A_FAILURE_REASONS = new Set([\n \"request_cancelled\",\n \"provider_read_busy\",\n \"provider_unavailable\",\n \"lifecycle_conflict\",\n \"request_rejected\",\n \"unexpected\",\n]);\n\n/**\n * Public diagnostic values are protocol constants, never values inferred from\n * an exception's name, constructor, code, message, or enumerable properties.\n * Unknown classes collapse to one fixed fallback; unknown codes and origins\n * are omitted rather than copied through based on syntax.\n */\nconst PUBLIC_TELEMETRY_ERROR_CLASSES = new Set([\n \"OperationError\",\n \"CodexCheckpointOperationError\",\n \"CodexFleetShadowOperationError\",\n \"ComputerActionTimeoutError\",\n \"ComputerUnavailableError\",\n \"CredentialRenewalOperationError\",\n \"CredentialReadOperationError\",\n \"EventPublishOperationError\",\n \"GitCredentialRenewalOperationError\",\n \"HostExportOperationError\",\n \"HttpOperationError\",\n \"McpLifecycleError\",\n \"McpOperationError\",\n \"MemoryEmbeddingOperationError\",\n \"MemorySearchOperationError\",\n \"NatsAuthCalloutOperationError\",\n \"OAuthOperationError\",\n \"RunCredentialRenewalOperationError\",\n \"RunStateCompatibilityError\",\n \"SandboxChannelAOperationError\",\n \"SnapshotOperationError\",\n \"StartupDependencyError\",\n \"TelemetryExportError\",\n \"CodemodeOperationError\",\n \"CodemodeTokenRenewalOperationError\",\n \"WorkerLifecycleOperation\",\n \"WorkerLifecycleOperationError\",\n \"WorkerOperationError\",\n \"WorkflowWakeOperationError\",\n]);\n\nconst PUBLIC_TELEMETRY_ERROR_CODES = new Set([\n \"agent_command_wake_failed\",\n \"artifact_materializer_native_input_framing_failed\",\n \"artifact_materializer_native_snapshot_open_failed\",\n \"artifact_materializer_native_state_mismatch\",\n \"artifact_materializer_source_content_type_mismatch\",\n \"artifact_materializer_source_open_failed\",\n \"artifact_materializer_source_revalidation_failed\",\n \"artifact_materializer_source_stream_identity_mismatch\",\n \"cleared_goal_live_publish_failed\",\n \"codex_failover_checkpoint_failed\",\n \"codex_fleet_shadow_failed\",\n \"codex_lease_loss_checkpoint_failed\",\n \"codex_active_credential_read_failed\",\n \"command_yield_timeout\",\n \"conflict\",\n \"control_wake_dispatch_failed\",\n \"fenced_event_live_publish_failed\",\n \"forbidden\",\n \"host_export_batch_delivery_failed\",\n \"host_export_failure_settlement_stale\",\n \"host_export_pump_iteration_failed\",\n \"host_export_retention_prune_failed\",\n \"idempotency_conflict\",\n \"incompatible_exposed_ports\",\n \"internal_error\",\n \"limit_exceeded\",\n \"mcp_close_failed\",\n \"mcp_connect_failed\",\n \"mcp_tool_call_failed\",\n \"mcp_tools_list_failed\",\n \"mcp_transport_failed\",\n \"memory_edit_embedding_failed\",\n \"memory_hybrid_vector_failed\",\n \"memory_save_embedding_failed\",\n \"nats_auth_callout_start_failed\",\n \"nested_agent_depth_exceeded\",\n \"nested_agent_depth_override_forbidden\",\n \"not_found\",\n \"oauth_operation_failed\",\n \"otlp_export_failed\",\n \"payment_required\",\n \"provider_verification_failed\",\n \"sandbox_channel_a_cancelled\",\n \"sandbox_channel_a_lifecycle_conflict\",\n \"sandbox_channel_a_operation_failed\",\n \"sandbox_channel_a_provider_busy\",\n \"sandbox_channel_a_provider_unavailable\",\n \"screenshot_capture_failed\",\n \"session_event_live_publish_failed\",\n \"session_workflow_wake_failed\",\n \"snapshot_operation_failed\",\n \"startup_dependency_retry\",\n \"tool_list_too_large\",\n \"tool_result_too_large\",\n \"codemode_operation_failed\",\n \"unauthenticated\",\n \"upstream_unavailable\",\n \"validation_failed\",\n \"worker_draining\",\n \"worker_operation_failed\",\n \"worker_shutdown_request_failed\",\n \"workspace_control_live_publish_failed\",\n]);\n\nconst PUBLIC_TELEMETRY_ERROR_ORIGINS = new Set([\n \"api\",\n \"core\",\n \"db\",\n \"events\",\n \"host-export\",\n \"oauth\",\n \"observability\",\n \"runtime\",\n \"sandbox-computer\",\n \"sandbox-resume\",\n \"codemode\",\n \"worker\",\n \"worker-lifecycle\",\n]);\n\nexport type SandboxOperationMetricObservation = {\n backend: string;\n op: string;\n outcome: \"ok\" | \"not_found\" | \"failed\";\n durationMs: number;\n};\n\nexport function createObservability(\n settings: ObservabilitySettings,\n options: ObservabilityOptions,\n): Observability {\n return new Observability(settings, options);\n}\n\ntype MetricRegistration = {\n kind: \"counter\" | \"gauge\" | \"histogram\";\n labelNames: string[];\n};\n\nexport class Observability {\n private readonly registry = new Registry();\n private readonly counters = new Map<string, Counter<string>>();\n private readonly gauges = new Map<string, Gauge<string>>();\n private readonly histograms = new Map<string, Histogram<string>>();\n private readonly registrations = new Map<string, MetricRegistration>();\n private readonly now: () => number;\n private readonly exporter: (\n url: string,\n body: unknown,\n headers: Record<string, string>,\n ) => Promise<void>;\n private readonly resourceAttributes: Attributes;\n\n constructor(\n private readonly settings: ObservabilitySettings,\n private readonly options: ObservabilityOptions,\n ) {\n this.now = options.now ?? Date.now;\n this.exporter = options.exporter ?? defaultExporter;\n this.resourceAttributes = {\n \"service.name\": settings.serviceName,\n \"deployment.environment\": settings.environment,\n \"opengeni.component\": options.component,\n };\n this.registry.setDefaultLabels({\n service: settings.serviceName,\n environment: settings.environment,\n component: options.component,\n deployment_revision: settings.deploymentRevision ?? \"dev\",\n });\n if (settings.observabilityMetricsEnabled) {\n collectDefaultMetrics({ register: this.registry, prefix: \"opengeni_\" });\n this.setGauge({\n name: \"opengeni_build_info\",\n help: \"OpenGeni build information.\",\n labels: {\n version: buildVersion(),\n revision: settings.deploymentRevision ?? \"dev\",\n },\n value: 1,\n });\n }\n }\n\n debug(message: string, attributes: Attributes = {}): void {\n this.log(\"debug\", message, attributes);\n }\n\n info(message: string, attributes: Attributes = {}): void {\n this.log(\"info\", message, attributes);\n }\n\n warn(message: string, attributes: Attributes = {}): void {\n this.log(\"warn\", message, attributes);\n }\n\n error(message: string, attributes: Attributes = {}): void {\n this.log(\"error\", message, attributes);\n }\n\n log(\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n message: string,\n attributes: Attributes = {},\n ): void {\n const publicAttributes = projectPublicTelemetryAttributes(attributes);\n if (!this.settings.observabilityStructuredLogs) {\n if (level === \"warn\") {\n console.warn(message);\n } else if (level === \"error\") {\n console.error(message);\n } else {\n console.log(message);\n }\n return;\n }\n const record = {\n timestamp: new Date(this.now()).toISOString(),\n level,\n message,\n service: this.settings.serviceName,\n environment: this.settings.environment,\n component: this.options.component,\n ...cleanAttributes(publicAttributes),\n };\n const serialized = JSON.stringify(record);\n if (level === \"warn\") {\n console.warn(serialized);\n } else if (level === \"error\") {\n console.error(serialized);\n } else {\n console.log(serialized);\n }\n }\n\n startSpan(name: string, attributes: Attributes = {}): Span {\n const traceId = randomHex(16);\n const spanId = randomHex(8);\n const startMs = this.now();\n let ended = false;\n return {\n traceId,\n spanId,\n end: (input = {}) => {\n if (ended) {\n return;\n }\n ended = true;\n const telemetryError =\n input.error !== undefined && input.error !== null\n ? projectSpanErrorForTelemetry(input.error)\n : undefined;\n const errorAttributes = telemetryError ? errorToAttributes(telemetryError) : {};\n this.exportSpan({\n traceId,\n spanId,\n name,\n startMs,\n endMs: this.now(),\n attributes: {\n ...projectPublicTelemetryAttributes({\n ...attributes,\n ...input.attributes,\n ...errorAttributes,\n }),\n },\n ...(telemetryError ? { error: telemetryError } : {}),\n });\n },\n };\n }\n\n recordHttpRequest(input: {\n method: string;\n route: string;\n status: number;\n durationSeconds: number;\n }): void {\n this.incrementCounter({\n name: \"opengeni_http_requests_total\",\n help: \"Total HTTP requests handled by OpenGeni.\",\n labels: {\n method: input.method,\n route: input.route,\n status: String(input.status),\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_http_request_duration_seconds\",\n help: \"HTTP request duration in seconds.\",\n buckets: httpHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n method: input.method,\n route: input.route,\n component: this.options.component,\n },\n });\n }\n\n recordWorkerActivity(input: { activity: string; status: string; durationSeconds: number }): void {\n this.incrementCounter({\n name: \"opengeni_worker_activity_runs_total\",\n help: \"Total worker activity executions.\",\n labels: {\n activity: input.activity,\n status: input.status,\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_worker_activity_duration_seconds\",\n help: \"Worker activity duration in seconds.\",\n buckets: durationHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n activity: input.activity,\n component: this.options.component,\n },\n });\n }\n\n incrementCounter(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const counter = this.counter(\n input.name,\n input.help ?? `${input.name} counter.`,\n Object.keys(labels),\n );\n counter.inc(labels as never, input.amount ?? 1);\n }\n\n setGauge(input: { name: string; help?: string; labels?: MetricLabels; value: number }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.set(labels as never, input.value);\n }\n\n incrementGauge(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.inc(labels as never, input.amount ?? 1);\n }\n\n observeHistogram(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n value: number;\n buckets?: number[];\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const histogram = this.histogram(\n input.name,\n input.help ?? `${input.name} histogram.`,\n Object.keys(labels),\n input.buckets ?? durationHistogramBuckets,\n );\n histogram.observe(labels as never, input.value);\n }\n\n async prometheusMetrics(): Promise<string> {\n if (!this.settings.observabilityMetricsEnabled) {\n return \"\";\n }\n return await this.registry.metrics();\n }\n\n private counter(name: string, help: string, labelNames: string[]): Counter<string> {\n const existing = this.counters.get(name);\n if (existing) {\n this.assertRegistration(name, \"counter\", labelNames);\n return existing;\n }\n this.register(name, \"counter\", labelNames);\n const metric = new Counter({ name, help, labelNames, registers: [this.registry] });\n this.counters.set(name, metric);\n return metric;\n }\n\n private gauge(name: string, help: string, labelNames: string[]): Gauge<string> {\n const existing = this.gauges.get(name);\n if (existing) {\n this.assertRegistration(name, \"gauge\", labelNames);\n return existing;\n }\n this.register(name, \"gauge\", labelNames);\n const metric = new Gauge({ name, help, labelNames, registers: [this.registry] });\n this.gauges.set(name, metric);\n return metric;\n }\n\n private histogram(\n name: string,\n help: string,\n labelNames: string[],\n buckets: number[],\n ): Histogram<string> {\n const existing = this.histograms.get(name);\n if (existing) {\n this.assertRegistration(name, \"histogram\", labelNames);\n return existing;\n }\n this.register(name, \"histogram\", labelNames);\n const metric = new Histogram({ name, help, labelNames, buckets, registers: [this.registry] });\n this.histograms.set(name, metric);\n return metric;\n }\n\n private register(name: string, kind: MetricRegistration[\"kind\"], labelNames: string[]): void {\n const sorted = [...labelNames].sort();\n this.registrations.set(name, { kind, labelNames: sorted });\n }\n\n private assertRegistration(\n name: string,\n kind: MetricRegistration[\"kind\"],\n labelNames: string[],\n ): void {\n const registration = this.registrations.get(name);\n const sorted = [...labelNames].sort();\n if (\n !registration ||\n registration.kind !== kind ||\n registration.labelNames.length !== sorted.length ||\n registration.labelNames.some((label, index) => label !== sorted[index])\n ) {\n throw new Error(\n `Metric ${name} was already registered as ${registration?.kind ?? \"unknown\"} ` +\n `with labels [${registration?.labelNames.join(\",\") ?? \"\"}], not ${kind} [${sorted.join(\",\")}]`,\n );\n }\n }\n\n private exportSpan(span: {\n traceId: string;\n spanId: string;\n name: string;\n startMs: number;\n endMs: number;\n attributes: Attributes;\n error?: TelemetrySpanError;\n }): void {\n if (!this.settings.observabilityOtlpEndpoint) {\n return;\n }\n const endpoint = `${this.settings.observabilityOtlpEndpoint.replace(/\\/$/, \"\")}/v1/traces`;\n const body = {\n resourceSpans: [\n {\n resource: {\n attributes: otlpAttributes(this.resourceAttributes),\n },\n scopeSpans: [\n {\n scope: {\n name: \"@opengeni/observability\",\n version: \"0.1.0\",\n },\n spans: [\n {\n traceId: span.traceId,\n spanId: span.spanId,\n name: span.name,\n kind: 1,\n startTimeUnixNano: millisToNanos(span.startMs),\n endTimeUnixNano: millisToNanos(span.endMs),\n attributes: otlpAttributes(span.attributes),\n status: span.error ? { code: 2, message: span.error.statusMessage } : { code: 1 },\n },\n ],\n },\n ],\n },\n ],\n };\n void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(\n () => {\n this.warn(\"OTLP span export failed\", {\n errorClass: \"TelemetryExportError\",\n errorCode: \"otlp_export_failed\",\n origin: \"observability\",\n });\n },\n );\n }\n}\n\n/**\n * Convert routed sandbox-provider observations into one bounded Prometheus\n * contract shared by API-direct and worker-turn execution. Provider/session\n * identifiers, commands, paths, and other request data can never become labels:\n * a future backend or operation is deliberately collapsed to `unknown` until\n * this allowlist is reviewed.\n *\n * The returned callback is fail-safe. Metrics must never change the provider\n * operation's result; a registration/exporter error is counted best-effort and\n * then swallowed at this telemetry boundary.\n */\nexport function sandboxOperationMetricObserver(\n observability: Observability,\n): (observation: SandboxOperationMetricObservation) => void {\n return (observation) => {\n const backend = SANDBOX_OPERATION_BACKENDS.has(observation.backend)\n ? observation.backend\n : \"unknown\";\n const op = SANDBOX_OPERATION_NAMES.has(observation.op) ? observation.op : \"unknown\";\n try {\n observability.incrementCounter({\n name: \"opengeni_sandbox_operations_total\",\n help: \"Physical routed sandbox provider operations by backend, operation, and outcome.\",\n labels: { backend, op, outcome: observation.outcome },\n });\n observability.observeHistogram({\n name: \"opengeni_sandbox_operation_duration_seconds\",\n help: \"Physical routed sandbox provider-operation duration in seconds.\",\n labels: { backend, op },\n value: Math.max(0, observation.durationMs) / 1_000,\n });\n } catch {\n try {\n observability.incrementCounter({\n name: \"opengeni_observability_observer_errors_total\",\n help: \"Observability observer failures isolated from product execution.\",\n labels: { observer: \"sandbox_operation\" },\n });\n } catch {\n // The metrics registry itself is unhealthy. Product execution remains\n // authoritative and must not inherit an observability failure.\n }\n }\n };\n}\n\nexport type InteractionOperationMetricObservation = {\n resource: string;\n operation: string;\n outcome: string;\n mode: string;\n durationMs: number;\n replayed?: boolean | undefined;\n};\n\n/** Safe low-cardinality telemetry for Browser/Computer control operations.\n * Resource ids, URLs, locators, page/app text, and input values are excluded by\n * construction. Unknown future enum values collapse until explicitly reviewed. */\nexport function interactionOperationMetricObserver(\n observability: Observability | null | undefined,\n): (observation: InteractionOperationMetricObservation) => void {\n if (!observability) return () => undefined;\n return (observation) => {\n if (observation.replayed) return;\n const resource = boundedMetricEnum(INTERACTION_RESOURCES, observation.resource);\n const operation = boundedMetricEnum(INTERACTION_OPERATIONS, observation.operation);\n const outcome = boundedMetricEnum(INTERACTION_OUTCOMES, observation.outcome);\n const mode = boundedMetricEnum(INTERACTION_MODES, observation.mode);\n try {\n observability.incrementCounter({\n name: \"opengeni_interaction_operations_total\",\n help: \"Browser and Computer operations by bounded resource, operation, mode, and outcome.\",\n labels: { resource, operation, mode, outcome },\n });\n observability.observeHistogram({\n name: \"opengeni_interaction_operation_duration_seconds\",\n help: \"Browser and Computer operation duration in seconds.\",\n labels: { resource, operation, mode },\n value: boundedMetricDuration(observation.durationMs),\n });\n } catch {\n recordObserverFailure(observability, \"interaction_operation\");\n }\n };\n}\n\nexport type InteractionAuthMetricObservation = {\n state: string;\n durationMs: number;\n replayed?: boolean | undefined;\n};\n\nexport function interactionAuthMetricObserver(\n observability: Observability | null | undefined,\n): (observation: InteractionAuthMetricObservation) => void {\n if (!observability) return () => undefined;\n return (observation) => {\n if (observation.replayed) return;\n const state = boundedMetricEnum(INTERACTION_AUTH_STATES, observation.state);\n try {\n observability.incrementCounter({\n name: \"opengeni_interaction_auth_transitions_total\",\n help: \"Browser authentication transitions by bounded resulting state.\",\n labels: { state },\n });\n observability.observeHistogram({\n name: \"opengeni_interaction_auth_transition_duration_seconds\",\n help: \"Browser authentication transition request duration in seconds.\",\n labels: { state },\n value: boundedMetricDuration(observation.durationMs),\n });\n } catch {\n recordObserverFailure(observability, \"interaction_auth\");\n }\n };\n}\n\nexport type InteractionInterventionMetricObservation = {\n kind: string;\n outcome: string;\n waitMs?: number | undefined;\n replayed?: boolean | undefined;\n};\n\nexport function interactionInterventionMetricObserver(\n observability: Observability | null | undefined,\n): (observation: InteractionInterventionMetricObservation) => void {\n if (!observability) return () => undefined;\n return (observation) => {\n if (observation.replayed) return;\n const kind = boundedMetricEnum(INTERACTION_INTERVENTION_KINDS, observation.kind);\n const outcome = boundedMetricEnum(INTERACTION_INTERVENTION_OUTCOMES, observation.outcome);\n try {\n observability.incrementCounter({\n name: \"opengeni_interaction_interventions_total\",\n help: \"Human browser/computer interventions by bounded kind and outcome.\",\n labels: { kind, outcome },\n });\n if (observation.waitMs !== undefined) {\n observability.observeHistogram({\n name: \"opengeni_interaction_intervention_wait_seconds\",\n help: \"Time an interaction intervention remained open before settlement.\",\n labels: { kind, outcome },\n value: boundedMetricDuration(observation.waitMs),\n });\n }\n } catch {\n recordObserverFailure(observability, \"interaction_intervention\");\n }\n };\n}\n\nfunction boundedMetricEnum(allowed: ReadonlySet<string>, value: string): string {\n return allowed.has(value) ? value : \"unknown\";\n}\n\nfunction boundedMetricDuration(durationMs: number): number {\n return Number.isFinite(durationMs) ? Math.max(0, durationMs) / 1_000 : 0;\n}\n\nfunction recordObserverFailure(observability: Observability, observer: string): void {\n try {\n observability.incrementCounter({\n name: \"opengeni_observability_observer_errors_total\",\n help: \"Observability observer failures isolated from product execution.\",\n labels: { observer },\n });\n } catch {\n // The metrics registry itself is unhealthy. Product execution remains\n // authoritative and must not inherit an observability failure.\n }\n}\n\nexport type StartupDependencyRetryEvent = {\n label: string;\n attempt: number;\n attempts: number;\n delayMs: number;\n error: unknown;\n};\n\nexport function logStartupDependencyRetry(\n observability: Observability,\n event: StartupDependencyRetryEvent,\n): void {\n observability.warn(\"Startup dependency connection failed; retrying\", {\n dependency: event.label,\n attempt: event.attempt,\n attempts: event.attempts,\n delayMs: event.delayMs,\n errorClass: \"StartupDependencyError\",\n errorCode: \"startup_dependency_retry\",\n origin: \"observability\",\n });\n}\n\nfunction normalizeLabels(labels: MetricLabels = {}): Record<string, string> {\n return Object.fromEntries(\n Object.entries(labels)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]): [string, string] => [key, String(value)])\n .sort((left, right) => left[0].localeCompare(right[0])),\n );\n}\n\nfunction buildVersion(): string {\n return process.env.OPENGENI_VERSION ?? process.env.npm_package_version ?? \"dev\";\n}\n\nfunction cleanAttributes(attributes: Attributes): Record<string, string | number | boolean | null> {\n return Object.fromEntries(\n Object.entries(attributes).filter(([, value]) => value !== undefined),\n ) as Record<string, string | number | boolean | null>;\n}\n\nfunction projectPublicTelemetryAttributes(attributes: Attributes): Attributes {\n if (\"errorClass\" in attributes || \"errorCode\" in attributes) {\n return {\n ...projectPublicChannelADiagnosticAttributes(attributes),\n ...projectPublicDiagnosticAttributes(attributes),\n };\n }\n return Object.fromEntries(\n Object.entries(attributes).filter(([key, value]) => {\n if (value === undefined) return false;\n if (PUBLIC_TELEMETRY_ATTRIBUTE_KEYS.has(key)) return true;\n const pattern = PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS.get(key);\n return typeof value === \"string\" && pattern?.test(value) === true;\n }),\n );\n}\n\nfunction projectPublicChannelADiagnosticAttributes(attributes: Attributes): Attributes {\n if (attributes.errorClass !== \"SandboxChannelAOperationError\") return {};\n const backend = attributes.backend;\n const op = attributes.op;\n const outcome = attributes.outcome;\n const reason = attributes.reason;\n const durationMs = attributes.durationMs;\n const sandboxLeaseKey = attributes.sandboxLeaseKey;\n return {\n ...(typeof backend === \"string\" && SANDBOX_OPERATION_BACKENDS.has(backend) ? { backend } : {}),\n ...(typeof op === \"string\" && PUBLIC_CHANNEL_A_OPERATIONS.has(op) ? { op } : {}),\n ...(outcome === \"failed\" ? { outcome } : {}),\n ...(typeof reason === \"string\" && PUBLIC_CHANNEL_A_FAILURE_REASONS.has(reason)\n ? { reason }\n : {}),\n ...(typeof durationMs === \"number\" && Number.isFinite(durationMs) && durationMs >= 0\n ? { durationMs }\n : {}),\n ...(typeof sandboxLeaseKey === \"string\" &&\n PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS.get(\"sandboxLeaseKey\")?.test(sandboxLeaseKey)\n ? { sandboxLeaseKey }\n : {}),\n };\n}\n\nfunction projectPublicDiagnosticAttributes(attributes: Attributes): Attributes {\n const errorClass = attributes.errorClass;\n const errorCode = attributes.errorCode;\n const status = attributes.status;\n const origin = attributes.origin;\n return {\n errorClass:\n typeof errorClass === \"string\" && PUBLIC_TELEMETRY_ERROR_CLASSES.has(errorClass)\n ? errorClass\n : \"OperationError\",\n ...(typeof errorCode === \"string\" && PUBLIC_TELEMETRY_ERROR_CODES.has(errorCode)\n ? { errorCode }\n : {}),\n ...(typeof status === \"number\" && Number.isInteger(status) && status >= 100 && status <= 599\n ? { status }\n : {}),\n ...(typeof origin === \"string\" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {}),\n };\n}\n\ntype TelemetrySpanError = {\n type: string;\n statusCode?: number;\n statusMessage: string;\n};\n\n/**\n * External OTLP projection. It intentionally exports only error class/status\n * metadata and never mutates canonical OpenGeni errors, events, or history.\n */\nfunction projectSpanErrorForTelemetry(error: unknown): TelemetrySpanError {\n const statusCode = errorStatusCode(error);\n return {\n type: \"OperationError\",\n ...(statusCode === undefined ? {} : { statusCode }),\n statusMessage: statusCode === undefined ? \"operation failed\" : `HTTP ${statusCode}`,\n };\n}\n\nfunction errorStatusCode(error: unknown): number | undefined {\n if (typeof error !== \"object\" || error === null) return undefined;\n try {\n const value = (error as { status?: unknown; statusCode?: unknown }).status;\n const statusCode =\n Number.isInteger(value) && typeof value === \"number\"\n ? value\n : (error as { statusCode?: unknown }).statusCode;\n return Number.isInteger(statusCode) &&\n typeof statusCode === \"number\" &&\n statusCode >= 100 &&\n statusCode <= 599\n ? statusCode\n : undefined;\n } catch {\n // OTLP projection must never mask the exact internal failure.\n return undefined;\n }\n}\n\nfunction errorToAttributes(error: TelemetrySpanError): Attributes {\n return {\n \"error.type\": error.type,\n ...(error.statusCode === undefined ? {} : { \"error.status_code\": error.statusCode }),\n };\n}\n\nfunction otlpAttributes(\n attributes: Attributes,\n): Array<{ key: string; value: Record<string, string | number | boolean> }> {\n return Object.entries(cleanAttributes(attributes)).map(([key, value]) => ({\n key,\n value: otlpValue(value),\n }));\n}\n\nfunction otlpValue(\n value: string | number | boolean | null,\n): Record<string, string | number | boolean> {\n if (typeof value === \"number\") {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === \"boolean\") {\n return { boolValue: value };\n }\n return { stringValue: value === null ? \"\" : boundedOtlpString(value) };\n}\n\nfunction boundedOtlpString(value: string): string {\n const bytes = new TextEncoder().encode(value);\n if (bytes.byteLength <= 512) return value;\n return `${new TextDecoder().decode(bytes.slice(0, 509))}…`;\n}\n\nfunction millisToNanos(ms: number): string {\n return String(BigInt(Math.round(ms)) * 1_000_000n);\n}\n\nfunction randomHex(bytes: number): string {\n const values = crypto.getRandomValues(new Uint8Array(bytes));\n return Array.from(values, (value) => value.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function parseHeaders(value: string): Record<string, string> {\n if (!value.trim()) {\n return {};\n }\n const entries: Array<[string, string]> = value\n .split(\",\")\n .map((pair): [string, string] => {\n const separator = pair.indexOf(\"=\");\n if (separator === -1) {\n return [pair.trim(), \"\"];\n }\n return [pair.slice(0, separator).trim(), pair.slice(separator + 1).trim()];\n })\n .filter(([key]) => key.length > 0);\n return Object.fromEntries(entries);\n}\n\nasync function defaultExporter(\n url: string,\n body: unknown,\n headers: Record<string, string>,\n): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...headers,\n },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n throw new Error(`OTLP endpoint returned HTTP ${response.status}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { collectDefaultMetrics, Counter, Gauge, Histogram, Registry } from \"prom-client\";\nimport { SandboxBackend } from \"@opengeni/contracts\";\n\nexport type AttributeValue = string | number | boolean | null | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\nexport type ObservabilitySettings = {\n serviceName: string;\n environment: string;\n deploymentRevision?: string | undefined;\n observabilityStructuredLogs: boolean;\n observabilityMetricsEnabled: boolean;\n observabilityOtlpEndpoint?: string | undefined;\n observabilityOtlpHeaders: string;\n sandboxOwnershipEnabled?: boolean | undefined;\n sandboxLazyProvisionEnabled?: boolean | undefined;\n rigVerificationLeaseOwnershipEnabled?: boolean | undefined;\n};\n\nexport type ObservabilityOptions = {\n component: string;\n now?: () => number;\n exporter?: (url: string, body: unknown, headers: Record<string, string>) => Promise<void>;\n};\n\nexport type Span = {\n traceId: string;\n spanId: string;\n end: (input?: { attributes?: Attributes; error?: unknown }) => void;\n};\n\nexport type MetricLabels = Record<string, AttributeValue>;\n\n/**\n * Stable, non-reversible correlation key for one logical sandbox lease. Public\n * telemetry intentionally drops workspace/group identifiers; this key lets an\n * operator correlate API, worker, and reaper failures without publishing\n * either UUID. The domain separator prevents reuse as a generic identifier\n * digest.\n */\nexport function sandboxLeaseTelemetryKey(workspaceId: string, sandboxGroupId: string): string {\n return `slk_${createHash(\"sha256\")\n .update(\"opengeni:sandbox-lease-telemetry:v1\\0\")\n .update(workspaceId)\n .update(\"\\0\")\n .update(sandboxGroupId)\n .digest(\"hex\")\n .slice(0, 32)}`;\n}\n\n/**\n * Stable selectors shared by OpenGeni's runtime metrics and optional\n * Prometheus/Grafana distribution. Operators can use these values for custom\n * namespaces and dashboard ConfigMaps without duplicating chart internals.\n */\nexport const OPENGENI_OBSERVABILITY_DISTRIBUTION = {\n monitoringNamespaceLabel: \"opengeni.ai/monitoring\",\n monitoringNamespaceLabelValue: \"enabled\",\n grafanaDashboardLabel: \"grafana_dashboard\",\n grafanaDashboardLabelValue: \"1\",\n} as const;\n\nconst httpHistogramBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\nconst durationHistogramBuckets = [\n 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 900, 1800, 3600,\n];\n\nconst SANDBOX_OPERATION_BACKENDS = new Set<string>([...SandboxBackend.options, \"unprovisioned\"]);\n\nconst SANDBOX_OPERATION_NAMES = new Set([\n \"desktopInput\",\n \"screenshot\",\n \"exec\",\n \"execCommand\",\n \"writeStdin\",\n \"cancelExecCommand\",\n \"readFile\",\n \"writeFile\",\n \"listDir\",\n \"pathExists\",\n \"viewImage\",\n \"materializeEntry\",\n \"editor.createFile\",\n \"editor.updateFile\",\n \"editor.deleteFile\",\n \"resolveExposedPort\",\n \"serializeSessionState\",\n]);\n\n/**\n * Closed set of organization-tenancy compatibility lanes (the tenancy\n * migration's validate phase; see `docs/organization-tenancy.md`).\n *\n * Each entry names one legacy behaviour a live request can still take today.\n * The metric built from them is deliberately a COUNTER of lane USES, and it\n * measures exactly one thing: **is this lane still being exercised?** That is\n * the question the tenancy cutover gate (\"zero incompatible writers\") actually\n * asks, and it is answerable.\n *\n * It is emphatically NOT a burndown of unmigrated rows, and must never be\n * relabelled as one. Every one of these lanes is still *open*: the live write\n * paths keep producing rows in it, so no row-count predicate over any of them\n * can reach zero and a backlog gauge would read as a permanent, untruthful\n * unmigrated backlog. `docs/organization-tenancy.md` records, per lane, why -\n * including the lanes deliberately left uninstrumented because no truthful\n * signal exists for them yet.\n *\n * The recorded value is the lane name and nothing else. No organization,\n * workspace, session, subject, connection, or resource identity, and no\n * credential, name, or content, may ever become a label here.\n */\nexport const TENANCY_COMPATIBILITY_LANES = [\n /**\n * A provider connection carrying `authority_scope = 'legacy_user'` was\n * resolved for accepted use: a personal row with no common authority or\n * grant, admitted through `legacy_user_compatibility` provenance instead of\n * an explicit delegation. Migration 0256 backfilled the existing rows, but\n * its `bind_connection_authority` trigger still classifies a NEW personal\n * connection this way whenever the inserting subject has no active\n * organization membership, so this counter is expected to stay above zero\n * until that classification changes. That is the finding, not a defect.\n */\n \"connection_legacy_user\",\n /**\n * A workspace-scope connection ref carried no connection id, so the\n * accepted-use authority (migration 0279) could not identify the exact row\n * and the request fell back to the unprivileged pre-snapshot resolution.\n * This lane writes no `connection_use_audit_facts` row and leaves no durable\n * trace of any kind, so a use counter is the only possible evidence it was\n * taken at all.\n */\n \"connection_pre_snapshot_ref\",\n /**\n * A NEW persistable `/workspace` mutation was refused because the writer\n * behind it has no recorded authority (`authority_unattributed`): either a\n * direct request whose principal froze the pre-0096 legacy initiator, or a\n * retained process whose pre-0277 row has no tenancy half. This counts\n * REFUSED MUTATIONS, not unattributed rows - the turn and process admission\n * lanes deliberately still accept a `legacy_unattributed` initiator, and\n * nothing may invent the missing authority or disturb the running provider\n * process.\n */\n \"workspace_writer_unattributed\",\n] as const;\nexport type TenancyCompatibilityLane = (typeof TENANCY_COMPATIBILITY_LANES)[number];\n\nconst TENANCY_COMPATIBILITY_LANE_SET = new Set<string>(TENANCY_COMPATIBILITY_LANES);\n\nconst TENANCY_COMPATIBILITY_LANE_METRIC = {\n name: \"opengeni_tenancy_compatibility_lane_uses_total\",\n help: \"Live uses of an organization-tenancy compatibility lane, by lane.\",\n} as const;\n\n/**\n * Record one live use of a tenancy compatibility lane. Fail-safe by\n * construction: telemetry must never change an authorization or credential\n * outcome, so a registry failure is counted best-effort and swallowed here.\n */\nexport function recordTenancyCompatibilityLaneUse(\n observability: Observability | null | undefined,\n lane: TenancyCompatibilityLane,\n): void {\n if (!observability) return;\n // An unreviewed lane name must not silently mint a new series.\n if (!TENANCY_COMPATIBILITY_LANE_SET.has(lane)) return;\n try {\n observability.incrementCounter({ ...TENANCY_COMPATIBILITY_LANE_METRIC, labels: { lane } });\n } catch {\n recordObserverFailure(observability, \"tenancy_compatibility_lane\");\n }\n}\n\nconst INTERACTION_RESOURCES = new Set([\"browser\", \"computer\"]);\nconst INTERACTION_OPERATIONS = new Set([\n \"create\",\n \"end\",\n \"suspend\",\n \"resume\",\n \"publish\",\n \"observe\",\n \"act\",\n \"open_target\",\n \"select_target\",\n \"close_target\",\n \"attach\",\n \"auth_start\",\n \"auth_report\",\n \"auth_fill\",\n \"auth_verify\",\n \"intervention_create\",\n \"intervention_resolve\",\n]);\nconst INTERACTION_OUTCOMES = new Set([\n \"prepared\",\n \"dispatched\",\n \"completed\",\n \"failed\",\n \"outcome_unknown\",\n \"stale\",\n \"cancelled\",\n]);\nconst INTERACTION_MODES = new Set([\n \"semantic\",\n \"coordinate\",\n \"keyboard\",\n \"clipboard\",\n \"permission\",\n \"lifecycle\",\n \"media\",\n \"auth\",\n \"human\",\n]);\nconst INTERACTION_AUTH_STATES = new Set([\n \"discovering\",\n \"awaiting_choice\",\n \"awaiting_secret\",\n \"awaiting_external_action\",\n \"working\",\n \"verified\",\n \"failed\",\n \"cancelled\",\n]);\nconst INTERACTION_INTERVENTION_KINDS = new Set([\n \"manual_login\",\n \"mfa\",\n \"external_action\",\n \"confirmation\",\n \"other\",\n]);\nconst INTERACTION_INTERVENTION_OUTCOMES = new Set([\n \"opened\",\n \"completed\",\n \"dismissed\",\n \"expired\",\n \"cancelled\",\n]);\nconst PUBLIC_STARTUP_DEPENDENCIES = new Set([\n \"Modal private-registry image\",\n \"NATS\",\n \"PostgreSQL runtime posture\",\n \"Temporal\",\n \"Temporal client\",\n \"Temporal schedule (file upload reaper)\",\n \"Temporal schedule (sandbox reaper)\",\n \"Temporal schedule (session-workflow wake dispatcher)\",\n \"Temporal schedule (site auth maintenance)\",\n]);\n\n/**\n * External logs and OTLP are public/third-party projections, not canonical\n * OpenGeni storage. Only this reviewed closed set of operational fields may\n * cross that boundary. Unknown keys are omitted regardless of their value, so\n * a new diagnostic, identifier, command, response, or provider field cannot\n * become public by accident. This is schema projection, never value inspection\n * or rewriting.\n */\nconst PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = new Set([\n \"http.request.method\",\n \"http.response.status_code\",\n \"opengeni.route\",\n \"opengeni.duration_ms\",\n \"opengeni.finalization_duration_ms\",\n \"opengeni.trigger_kind\",\n \"opengeni.status\",\n \"error.type\",\n \"error.status_code\",\n \"method\",\n \"route\",\n \"status\",\n \"durationMs\",\n \"attempt\",\n \"attempts\",\n \"delayMs\",\n \"provider\",\n \"providerApi\",\n \"model\",\n \"inputTokens\",\n \"outputTokens\",\n \"cachedTokens\",\n \"cacheWriteTokens\",\n \"reasoningTokens\",\n \"accountChangedFromPrevCall\",\n \"rejectedFields\",\n \"dependency\",\n \"activity\",\n \"backend\",\n \"op\",\n \"outcome\",\n \"eventType\",\n \"surface\",\n \"reason\",\n \"originalBytes\",\n \"deliveredBytes\",\n \"estimatedOriginalTokens\",\n \"estimatedDeliveredTokens\",\n \"fullEvidenceAvailable\",\n \"retainedOutputKind\",\n]);\n\n/** Opaque correlation fields require both a reviewed name and a closed value\n * grammar. Merely adding one to the ordinary allow-list would let an unrelated\n * caller accidentally publish a raw identifier under that name. */\nconst PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS = new Map<string, RegExp>([\n [\"sandboxLeaseKey\", /^slk_[0-9a-f]{32}$/],\n [\"correlationId\", /^[A-Za-z0-9._:-]{1,128}$/],\n]);\n\nconst PUBLIC_CHANNEL_A_OPERATIONS = new Set([\n \"fs.list\",\n \"fs.list-batch\",\n \"fs.read\",\n \"fs.write\",\n \"fs.delete\",\n \"fs.move\",\n \"fs.mkdir\",\n \"git.status\",\n \"git.diff\",\n \"git.read-batch\",\n \"git.log\",\n \"git.show\",\n \"terminal.exec\",\n \"terminal.pty.open\",\n \"terminal.pty.write\",\n \"terminal.pty.resize\",\n \"terminal.pty.close\",\n \"read\",\n \"mutation\",\n]);\n\nconst PUBLIC_CHANNEL_A_FAILURE_REASONS = new Set([\n \"request_cancelled\",\n \"provider_read_busy\",\n \"provider_unavailable\",\n \"lifecycle_conflict\",\n \"request_rejected\",\n \"unexpected\",\n]);\n\n/**\n * Public diagnostic values are protocol constants, never values inferred from\n * an exception's name, constructor, code, message, or enumerable properties.\n * Unknown classes collapse to one fixed fallback; unknown codes and origins\n * are omitted rather than copied through based on syntax.\n */\nconst PUBLIC_TELEMETRY_ERROR_CLASSES = new Set([\n \"OperationError\",\n \"CodexCheckpointOperationError\",\n \"CodexFleetShadowOperationError\",\n \"ComputerActionTimeoutError\",\n \"ComputerUnavailableError\",\n \"CredentialRenewalOperationError\",\n \"CredentialReadOperationError\",\n \"EventPublishOperationError\",\n \"GitCredentialRenewalOperationError\",\n \"HostExportOperationError\",\n \"HttpOperationError\",\n \"McpLifecycleError\",\n \"McpOperationError\",\n \"MemoryEmbeddingOperationError\",\n \"MemorySearchOperationError\",\n \"NatsAuthCalloutOperationError\",\n \"OAuthOperationError\",\n \"RunCredentialRenewalOperationError\",\n \"RunStateCompatibilityError\",\n \"SandboxChannelAOperationError\",\n \"SnapshotOperationError\",\n \"StartupDependencyError\",\n \"TelemetryExportError\",\n \"CodemodeOperationError\",\n \"CodemodeTokenRenewalOperationError\",\n \"WorkerLifecycleOperation\",\n \"WorkerLifecycleOperationError\",\n \"WorkerOperationError\",\n \"WorkflowWakeOperationError\",\n]);\n\nconst PUBLIC_TELEMETRY_ERROR_CODES = new Set([\n \"agent_command_wake_failed\",\n \"artifact_materializer_native_input_framing_failed\",\n \"artifact_materializer_native_snapshot_open_failed\",\n \"artifact_materializer_native_state_mismatch\",\n \"artifact_materializer_source_content_type_mismatch\",\n \"artifact_materializer_source_open_failed\",\n \"artifact_materializer_source_revalidation_failed\",\n \"artifact_materializer_source_stream_identity_mismatch\",\n \"cleared_goal_live_publish_failed\",\n \"codex_failover_checkpoint_failed\",\n \"codex_fleet_shadow_failed\",\n \"codex_lease_loss_checkpoint_failed\",\n \"codex_active_credential_read_failed\",\n \"command_yield_timeout\",\n \"conflict\",\n \"control_wake_dispatch_failed\",\n \"fenced_event_live_publish_failed\",\n \"forbidden\",\n \"host_export_batch_delivery_failed\",\n \"host_export_failure_settlement_stale\",\n \"host_export_pump_iteration_failed\",\n \"host_export_retention_prune_failed\",\n \"idempotency_conflict\",\n \"incompatible_exposed_ports\",\n \"internal_error\",\n \"limit_exceeded\",\n \"mcp_close_failed\",\n \"mcp_connect_failed\",\n \"mcp_tool_call_failed\",\n \"mcp_tools_list_failed\",\n \"mcp_transport_failed\",\n \"memory_edit_embedding_failed\",\n \"memory_hybrid_vector_failed\",\n \"memory_save_embedding_failed\",\n \"nats_auth_callout_start_failed\",\n \"nested_agent_depth_exceeded\",\n \"nested_agent_depth_override_forbidden\",\n \"not_found\",\n \"oauth_operation_failed\",\n \"otlp_export_failed\",\n \"payment_required\",\n \"provider_verification_failed\",\n \"sandbox_channel_a_cancelled\",\n \"sandbox_channel_a_lifecycle_conflict\",\n \"sandbox_channel_a_operation_failed\",\n \"sandbox_channel_a_provider_busy\",\n \"sandbox_channel_a_provider_unavailable\",\n \"screenshot_capture_failed\",\n \"session_event_live_publish_failed\",\n \"session_workflow_wake_failed\",\n \"snapshot_operation_failed\",\n \"startup_dependency_retry\",\n \"tool_list_too_large\",\n \"tool_result_too_large\",\n \"codemode_operation_failed\",\n \"unauthenticated\",\n \"upstream_unavailable\",\n \"validation_failed\",\n \"worker_draining\",\n \"worker_operation_failed\",\n \"worker_shutdown_request_failed\",\n \"workspace_control_live_publish_failed\",\n]);\n\nconst PUBLIC_TELEMETRY_ERROR_ORIGINS = new Set([\n \"api\",\n \"core\",\n \"db\",\n \"events\",\n \"host-export\",\n \"oauth\",\n \"observability\",\n \"runtime\",\n \"sandbox-computer\",\n \"sandbox-resume\",\n \"codemode\",\n \"worker\",\n \"worker-lifecycle\",\n]);\n\nexport type SandboxOperationMetricObservation = {\n backend: string;\n op: string;\n outcome: \"ok\" | \"not_found\" | \"failed\";\n durationMs: number;\n};\n\nexport function createObservability(\n settings: ObservabilitySettings,\n options: ObservabilityOptions,\n): Observability {\n return new Observability(settings, options);\n}\n\ntype MetricRegistration = {\n kind: \"counter\" | \"gauge\" | \"histogram\";\n labelNames: string[];\n};\n\nexport class Observability {\n private readonly registry = new Registry();\n private readonly counters = new Map<string, Counter<string>>();\n private readonly gauges = new Map<string, Gauge<string>>();\n private readonly histograms = new Map<string, Histogram<string>>();\n private readonly registrations = new Map<string, MetricRegistration>();\n private readonly now: () => number;\n private readonly exporter: (\n url: string,\n body: unknown,\n headers: Record<string, string>,\n ) => Promise<void>;\n private readonly resourceAttributes: Attributes;\n\n constructor(\n private readonly settings: ObservabilitySettings,\n private readonly options: ObservabilityOptions,\n ) {\n this.now = options.now ?? Date.now;\n this.exporter = options.exporter ?? defaultExporter;\n this.resourceAttributes = {\n \"service.name\": settings.serviceName,\n \"deployment.environment\": settings.environment,\n \"opengeni.component\": options.component,\n };\n this.registry.setDefaultLabels({\n service: settings.serviceName,\n environment: settings.environment,\n component: options.component,\n deployment_revision: settings.deploymentRevision ?? \"dev\",\n });\n if (settings.observabilityMetricsEnabled) {\n collectDefaultMetrics({ register: this.registry, prefix: \"opengeni_\" });\n this.setGauge({\n name: \"opengeni_build_info\",\n help: \"OpenGeni build information.\",\n labels: {\n version: buildVersion(),\n revision: settings.deploymentRevision ?? \"dev\",\n },\n value: 1,\n });\n this.registerSandboxRolloutConfig();\n this.registerTenancyCompatibilityLanes();\n }\n }\n\n /**\n * Publish every compatibility lane at zero on startup. Without this an\n * un-exercised lane has no series at all, and an operator cannot tell \"this\n * lane is dormant\" from \"this lane was never wired up\" - the two readings\n * that matter most to a cutover gate.\n */\n private registerTenancyCompatibilityLanes(): void {\n for (const lane of TENANCY_COMPATIBILITY_LANES) {\n this.incrementCounter({ ...TENANCY_COMPATIBILITY_LANE_METRIC, labels: { lane }, amount: 0 });\n }\n }\n\n private registerSandboxRolloutConfig(): void {\n const ownership = this.settings.sandboxOwnershipEnabled;\n const lazyConfigured = this.settings.sandboxLazyProvisionEnabled;\n const rigVerification = this.settings.rigVerificationLeaseOwnershipEnabled;\n if (\n typeof ownership !== \"boolean\" ||\n typeof lazyConfigured !== \"boolean\" ||\n typeof rigVerification !== \"boolean\"\n ) {\n return;\n }\n\n const values = [\n [\"ownership\", \"configured\", ownership],\n [\"ownership\", \"effective\", ownership],\n [\"lazy_provision\", \"configured\", lazyConfigured],\n [\"lazy_provision\", \"effective\", ownership && lazyConfigured],\n [\"rig_verification_lease_ownership\", \"configured\", rigVerification],\n [\"rig_verification_lease_ownership\", \"effective\", rigVerification],\n ] as const;\n for (const [feature, state, enabled] of values) {\n this.setGauge({\n name: \"opengeni_sandbox_rollout_config\",\n help: \"Configured and effective sandbox rollout state for this workload revision.\",\n labels: { feature, state },\n value: enabled ? 1 : 0,\n });\n }\n }\n\n debug(message: string, attributes: Attributes = {}): void {\n this.log(\"debug\", message, attributes);\n }\n\n info(message: string, attributes: Attributes = {}): void {\n this.log(\"info\", message, attributes);\n }\n\n warn(message: string, attributes: Attributes = {}): void {\n this.log(\"warn\", message, attributes);\n }\n\n error(message: string, attributes: Attributes = {}): void {\n this.log(\"error\", message, attributes);\n }\n\n log(\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n message: string,\n attributes: Attributes = {},\n ): void {\n const publicAttributes = projectPublicTelemetryAttributes(attributes);\n if (!this.settings.observabilityStructuredLogs) {\n if (level === \"warn\") {\n console.warn(message);\n } else if (level === \"error\") {\n console.error(message);\n } else {\n console.log(message);\n }\n return;\n }\n const record = {\n timestamp: new Date(this.now()).toISOString(),\n level,\n message,\n service: this.settings.serviceName,\n environment: this.settings.environment,\n component: this.options.component,\n ...cleanAttributes(publicAttributes),\n };\n const serialized = JSON.stringify(record);\n if (level === \"warn\") {\n console.warn(serialized);\n } else if (level === \"error\") {\n console.error(serialized);\n } else {\n console.log(serialized);\n }\n }\n\n startSpan(name: string, attributes: Attributes = {}): Span {\n const traceId = randomHex(16);\n const spanId = randomHex(8);\n const startMs = this.now();\n let ended = false;\n return {\n traceId,\n spanId,\n end: (input = {}) => {\n if (ended) {\n return;\n }\n ended = true;\n const telemetryError =\n input.error !== undefined && input.error !== null\n ? projectSpanErrorForTelemetry(input.error)\n : undefined;\n const errorAttributes = telemetryError ? errorToAttributes(telemetryError) : {};\n this.exportSpan({\n traceId,\n spanId,\n name,\n startMs,\n endMs: this.now(),\n attributes: {\n ...projectPublicTelemetryAttributes({\n ...attributes,\n ...input.attributes,\n ...errorAttributes,\n }),\n },\n ...(telemetryError ? { error: telemetryError } : {}),\n });\n },\n };\n }\n\n recordHttpRequest(input: {\n method: string;\n route: string;\n status: number;\n durationSeconds: number;\n }): void {\n this.incrementCounter({\n name: \"opengeni_http_requests_total\",\n help: \"Total HTTP requests handled by OpenGeni.\",\n labels: {\n method: input.method,\n route: input.route,\n status: String(input.status),\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_http_request_duration_seconds\",\n help: \"HTTP request duration in seconds.\",\n buckets: httpHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n method: input.method,\n route: input.route,\n component: this.options.component,\n },\n });\n }\n\n recordWorkerActivity(input: { activity: string; status: string; durationSeconds: number }): void {\n this.incrementCounter({\n name: \"opengeni_worker_activity_runs_total\",\n help: \"Total worker activity executions.\",\n labels: {\n activity: input.activity,\n status: input.status,\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_worker_activity_duration_seconds\",\n help: \"Worker activity duration in seconds.\",\n buckets: durationHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n activity: input.activity,\n component: this.options.component,\n },\n });\n }\n\n incrementCounter(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const counter = this.counter(\n input.name,\n input.help ?? `${input.name} counter.`,\n Object.keys(labels),\n );\n counter.inc(labels as never, input.amount ?? 1);\n }\n\n setGauge(input: { name: string; help?: string; labels?: MetricLabels; value: number }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.set(labels as never, input.value);\n }\n\n incrementGauge(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.inc(labels as never, input.amount ?? 1);\n }\n\n observeHistogram(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n value: number;\n buckets?: number[];\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const histogram = this.histogram(\n input.name,\n input.help ?? `${input.name} histogram.`,\n Object.keys(labels),\n input.buckets ?? durationHistogramBuckets,\n );\n histogram.observe(labels as never, input.value);\n }\n\n async prometheusMetrics(): Promise<string> {\n if (!this.settings.observabilityMetricsEnabled) {\n return \"\";\n }\n return await this.registry.metrics();\n }\n\n private counter(name: string, help: string, labelNames: string[]): Counter<string> {\n const existing = this.counters.get(name);\n if (existing) {\n this.assertRegistration(name, \"counter\", labelNames);\n return existing;\n }\n this.register(name, \"counter\", labelNames);\n const metric = new Counter({ name, help, labelNames, registers: [this.registry] });\n this.counters.set(name, metric);\n return metric;\n }\n\n private gauge(name: string, help: string, labelNames: string[]): Gauge<string> {\n const existing = this.gauges.get(name);\n if (existing) {\n this.assertRegistration(name, \"gauge\", labelNames);\n return existing;\n }\n this.register(name, \"gauge\", labelNames);\n const metric = new Gauge({ name, help, labelNames, registers: [this.registry] });\n this.gauges.set(name, metric);\n return metric;\n }\n\n private histogram(\n name: string,\n help: string,\n labelNames: string[],\n buckets: number[],\n ): Histogram<string> {\n const existing = this.histograms.get(name);\n if (existing) {\n this.assertRegistration(name, \"histogram\", labelNames);\n return existing;\n }\n this.register(name, \"histogram\", labelNames);\n const metric = new Histogram({ name, help, labelNames, buckets, registers: [this.registry] });\n this.histograms.set(name, metric);\n return metric;\n }\n\n private register(name: string, kind: MetricRegistration[\"kind\"], labelNames: string[]): void {\n const sorted = [...labelNames].sort();\n this.registrations.set(name, { kind, labelNames: sorted });\n }\n\n private assertRegistration(\n name: string,\n kind: MetricRegistration[\"kind\"],\n labelNames: string[],\n ): void {\n const registration = this.registrations.get(name);\n const sorted = [...labelNames].sort();\n if (\n !registration ||\n registration.kind !== kind ||\n registration.labelNames.length !== sorted.length ||\n registration.labelNames.some((label, index) => label !== sorted[index])\n ) {\n throw new Error(\n `Metric ${name} was already registered as ${registration?.kind ?? \"unknown\"} ` +\n `with labels [${registration?.labelNames.join(\",\") ?? \"\"}], not ${kind} [${sorted.join(\",\")}]`,\n );\n }\n }\n\n private exportSpan(span: {\n traceId: string;\n spanId: string;\n name: string;\n startMs: number;\n endMs: number;\n attributes: Attributes;\n error?: TelemetrySpanError;\n }): void {\n if (!this.settings.observabilityOtlpEndpoint) {\n return;\n }\n const endpoint = `${this.settings.observabilityOtlpEndpoint.replace(/\\/$/, \"\")}/v1/traces`;\n const body = {\n resourceSpans: [\n {\n resource: {\n attributes: otlpAttributes(this.resourceAttributes),\n },\n scopeSpans: [\n {\n scope: {\n name: \"@opengeni/observability\",\n version: \"0.1.0\",\n },\n spans: [\n {\n traceId: span.traceId,\n spanId: span.spanId,\n name: span.name,\n kind: 1,\n startTimeUnixNano: millisToNanos(span.startMs),\n endTimeUnixNano: millisToNanos(span.endMs),\n attributes: otlpAttributes(span.attributes),\n status: span.error ? { code: 2, message: span.error.statusMessage } : { code: 1 },\n },\n ],\n },\n ],\n },\n ],\n };\n void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(\n () => {\n this.warn(\"OTLP span export failed\", {\n errorClass: \"TelemetryExportError\",\n errorCode: \"otlp_export_failed\",\n origin: \"observability\",\n });\n },\n );\n }\n}\n\n/**\n * Convert routed sandbox-provider observations into one bounded Prometheus\n * contract shared by API-direct and worker-turn execution. Provider/session\n * identifiers, commands, paths, and other request data can never become labels:\n * a future backend or operation is deliberately collapsed to `unknown` until\n * this allowlist is reviewed.\n *\n * The returned callback is fail-safe. Metrics must never change the provider\n * operation's result; a registration/exporter error is counted best-effort and\n * then swallowed at this telemetry boundary.\n */\nexport function sandboxOperationMetricObserver(\n observability: Observability,\n): (observation: SandboxOperationMetricObservation) => void {\n return (observation) => {\n const backend = SANDBOX_OPERATION_BACKENDS.has(observation.backend)\n ? observation.backend\n : \"unknown\";\n const op = SANDBOX_OPERATION_NAMES.has(observation.op) ? observation.op : \"unknown\";\n try {\n observability.incrementCounter({\n name: \"opengeni_sandbox_operations_total\",\n help: \"Physical routed sandbox provider operations by backend, operation, and outcome.\",\n labels: { backend, op, outcome: observation.outcome },\n });\n observability.observeHistogram({\n name: \"opengeni_sandbox_operation_duration_seconds\",\n help: \"Physical routed sandbox provider-operation duration in seconds.\",\n labels: { backend, op },\n value: Math.max(0, observation.durationMs) / 1_000,\n });\n } catch {\n try {\n observability.incrementCounter({\n name: \"opengeni_observability_observer_errors_total\",\n help: \"Observability observer failures isolated from product execution.\",\n labels: { observer: \"sandbox_operation\" },\n });\n } catch {\n // The metrics registry itself is unhealthy. Product execution remains\n // authoritative and must not inherit an observability failure.\n }\n }\n };\n}\n\nexport type InteractionOperationMetricObservation = {\n resource: string;\n operation: string;\n outcome: string;\n mode: string;\n durationMs: number;\n replayed?: boolean | undefined;\n};\n\n/** Safe low-cardinality telemetry for Browser/Computer control operations.\n * Resource ids, URLs, locators, page/app text, and input values are excluded by\n * construction. Unknown future enum values collapse until explicitly reviewed. */\nexport function interactionOperationMetricObserver(\n observability: Observability | null | undefined,\n): (observation: InteractionOperationMetricObservation) => void {\n if (!observability) return () => undefined;\n return (observation) => {\n if (observation.replayed) return;\n const resource = boundedMetricEnum(INTERACTION_RESOURCES, observation.resource);\n const operation = boundedMetricEnum(INTERACTION_OPERATIONS, observation.operation);\n const outcome = boundedMetricEnum(INTERACTION_OUTCOMES, observation.outcome);\n const mode = boundedMetricEnum(INTERACTION_MODES, observation.mode);\n try {\n observability.incrementCounter({\n name: \"opengeni_interaction_operations_total\",\n help: \"Browser and Computer operations by bounded resource, operation, mode, and outcome.\",\n labels: { resource, operation, mode, outcome },\n });\n observability.observeHistogram({\n name: \"opengeni_interaction_operation_duration_seconds\",\n help: \"Browser and Computer operation duration in seconds.\",\n labels: { resource, operation, mode },\n value: boundedMetricDuration(observation.durationMs),\n });\n } catch {\n recordObserverFailure(observability, \"interaction_operation\");\n }\n };\n}\n\nexport type InteractionAuthMetricObservation = {\n state: string;\n durationMs: number;\n replayed?: boolean | undefined;\n};\n\nexport function interactionAuthMetricObserver(\n observability: Observability | null | undefined,\n): (observation: InteractionAuthMetricObservation) => void {\n if (!observability) return () => undefined;\n return (observation) => {\n if (observation.replayed) return;\n const state = boundedMetricEnum(INTERACTION_AUTH_STATES, observation.state);\n try {\n observability.incrementCounter({\n name: \"opengeni_interaction_auth_transitions_total\",\n help: \"Browser authentication transitions by bounded resulting state.\",\n labels: { state },\n });\n observability.observeHistogram({\n name: \"opengeni_interaction_auth_transition_duration_seconds\",\n help: \"Browser authentication transition request duration in seconds.\",\n labels: { state },\n value: boundedMetricDuration(observation.durationMs),\n });\n } catch {\n recordObserverFailure(observability, \"interaction_auth\");\n }\n };\n}\n\nexport type InteractionInterventionMetricObservation = {\n kind: string;\n outcome: string;\n waitMs?: number | undefined;\n replayed?: boolean | undefined;\n};\n\nexport function interactionInterventionMetricObserver(\n observability: Observability | null | undefined,\n): (observation: InteractionInterventionMetricObservation) => void {\n if (!observability) return () => undefined;\n return (observation) => {\n if (observation.replayed) return;\n const kind = boundedMetricEnum(INTERACTION_INTERVENTION_KINDS, observation.kind);\n const outcome = boundedMetricEnum(INTERACTION_INTERVENTION_OUTCOMES, observation.outcome);\n try {\n observability.incrementCounter({\n name: \"opengeni_interaction_interventions_total\",\n help: \"Human browser/computer interventions by bounded kind and outcome.\",\n labels: { kind, outcome },\n });\n if (observation.waitMs !== undefined) {\n observability.observeHistogram({\n name: \"opengeni_interaction_intervention_wait_seconds\",\n help: \"Time an interaction intervention remained open before settlement.\",\n labels: { kind, outcome },\n value: boundedMetricDuration(observation.waitMs),\n });\n }\n } catch {\n recordObserverFailure(observability, \"interaction_intervention\");\n }\n };\n}\n\nfunction boundedMetricEnum(allowed: ReadonlySet<string>, value: string): string {\n return allowed.has(value) ? value : \"unknown\";\n}\n\nfunction boundedMetricDuration(durationMs: number): number {\n return Number.isFinite(durationMs) ? Math.max(0, durationMs) / 1_000 : 0;\n}\n\nfunction recordObserverFailure(observability: Observability, observer: string): void {\n try {\n observability.incrementCounter({\n name: \"opengeni_observability_observer_errors_total\",\n help: \"Observability observer failures isolated from product execution.\",\n labels: { observer },\n });\n } catch {\n // The metrics registry itself is unhealthy. Product execution remains\n // authoritative and must not inherit an observability failure.\n }\n}\n\nexport type StartupDependencyRetryEvent = {\n label: string;\n attempt: number;\n attempts: number;\n delayMs: number;\n error: unknown;\n};\n\nexport function logStartupDependencyRetry(\n observability: Observability,\n event: StartupDependencyRetryEvent,\n): void {\n const dependency = PUBLIC_STARTUP_DEPENDENCIES.has(event.label) ? event.label : \"Dependency\";\n observability.warn(\n `Startup dependency ${dependency} connection failed; retrying (${event.attempt}/${event.attempts} in ${event.delayMs}ms)`,\n {\n dependency,\n attempt: event.attempt,\n attempts: event.attempts,\n delayMs: event.delayMs,\n errorClass: \"StartupDependencyError\",\n errorCode: \"startup_dependency_retry\",\n origin: \"observability\",\n },\n );\n}\n\nfunction normalizeLabels(labels: MetricLabels = {}): Record<string, string> {\n return Object.fromEntries(\n Object.entries(labels)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]): [string, string] => [key, String(value)])\n .sort((left, right) => left[0].localeCompare(right[0])),\n );\n}\n\nfunction buildVersion(): string {\n return process.env.OPENGENI_VERSION ?? process.env.npm_package_version ?? \"dev\";\n}\n\nfunction cleanAttributes(attributes: Attributes): Record<string, string | number | boolean | null> {\n return Object.fromEntries(\n Object.entries(attributes).filter(([, value]) => value !== undefined),\n ) as Record<string, string | number | boolean | null>;\n}\n\nfunction projectPublicTelemetryAttributes(attributes: Attributes): Attributes {\n if (\"errorClass\" in attributes || \"errorCode\" in attributes) {\n return {\n ...projectStartupDependencyAttributes(attributes),\n ...projectPublicChannelADiagnosticAttributes(attributes),\n ...projectPublicDiagnosticAttributes(attributes),\n };\n }\n return Object.fromEntries(\n Object.entries(attributes).filter(([key, value]) => {\n if (value === undefined) return false;\n if (PUBLIC_TELEMETRY_ATTRIBUTE_KEYS.has(key)) return true;\n const pattern = PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS.get(key);\n return typeof value === \"string\" && pattern?.test(value) === true;\n }),\n );\n}\n\nfunction projectStartupDependencyAttributes(attributes: Attributes): Attributes {\n if (\n attributes.errorClass !== \"StartupDependencyError\" ||\n attributes.errorCode !== \"startup_dependency_retry\"\n ) {\n return {};\n }\n const dependency = attributes.dependency;\n const attempt = attributes.attempt;\n const attempts = attributes.attempts;\n const delayMs = attributes.delayMs;\n return {\n ...(typeof dependency === \"string\" && PUBLIC_STARTUP_DEPENDENCIES.has(dependency)\n ? { dependency }\n : {}),\n ...(typeof attempt === \"number\" && Number.isInteger(attempt) && attempt > 0 ? { attempt } : {}),\n ...(typeof attempts === \"number\" && Number.isInteger(attempts) && attempts > 0\n ? { attempts }\n : {}),\n ...(typeof delayMs === \"number\" && Number.isFinite(delayMs) && delayMs >= 0 ? { delayMs } : {}),\n };\n}\n\nfunction projectPublicChannelADiagnosticAttributes(attributes: Attributes): Attributes {\n if (attributes.errorClass !== \"SandboxChannelAOperationError\") return {};\n const backend = attributes.backend;\n const op = attributes.op;\n const outcome = attributes.outcome;\n const reason = attributes.reason;\n const durationMs = attributes.durationMs;\n const sandboxLeaseKey = attributes.sandboxLeaseKey;\n return {\n ...(typeof backend === \"string\" && SANDBOX_OPERATION_BACKENDS.has(backend) ? { backend } : {}),\n ...(typeof op === \"string\" && PUBLIC_CHANNEL_A_OPERATIONS.has(op) ? { op } : {}),\n ...(outcome === \"failed\" ? { outcome } : {}),\n ...(typeof reason === \"string\" && PUBLIC_CHANNEL_A_FAILURE_REASONS.has(reason)\n ? { reason }\n : {}),\n ...(typeof durationMs === \"number\" && Number.isFinite(durationMs) && durationMs >= 0\n ? { durationMs }\n : {}),\n ...(typeof sandboxLeaseKey === \"string\" &&\n PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS.get(\"sandboxLeaseKey\")?.test(sandboxLeaseKey)\n ? { sandboxLeaseKey }\n : {}),\n };\n}\n\nfunction projectPublicDiagnosticAttributes(attributes: Attributes): Attributes {\n const errorClass = attributes.errorClass;\n const errorCode = attributes.errorCode;\n const status = attributes.status;\n const origin = attributes.origin;\n const correlationId = attributes.correlationId;\n return {\n errorClass:\n typeof errorClass === \"string\" && PUBLIC_TELEMETRY_ERROR_CLASSES.has(errorClass)\n ? errorClass\n : \"OperationError\",\n ...(typeof errorCode === \"string\" && PUBLIC_TELEMETRY_ERROR_CODES.has(errorCode)\n ? { errorCode }\n : {}),\n ...(typeof status === \"number\" && Number.isInteger(status) && status >= 100 && status <= 599\n ? { status }\n : {}),\n ...(typeof origin === \"string\" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {}),\n ...(typeof correlationId === \"string\" &&\n PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS.get(\"correlationId\")?.test(correlationId)\n ? { correlationId }\n : {}),\n };\n}\n\ntype TelemetrySpanError = {\n type: string;\n statusCode?: number;\n statusMessage: string;\n};\n\n/**\n * External OTLP projection. It intentionally exports only error class/status\n * metadata and never mutates canonical OpenGeni errors, events, or history.\n */\nfunction projectSpanErrorForTelemetry(error: unknown): TelemetrySpanError {\n const statusCode = errorStatusCode(error);\n return {\n type: \"OperationError\",\n ...(statusCode === undefined ? {} : { statusCode }),\n statusMessage: statusCode === undefined ? \"operation failed\" : `HTTP ${statusCode}`,\n };\n}\n\nfunction errorStatusCode(error: unknown): number | undefined {\n if (typeof error !== \"object\" || error === null) return undefined;\n try {\n const value = (error as { status?: unknown; statusCode?: unknown }).status;\n const statusCode =\n Number.isInteger(value) && typeof value === \"number\"\n ? value\n : (error as { statusCode?: unknown }).statusCode;\n return Number.isInteger(statusCode) &&\n typeof statusCode === \"number\" &&\n statusCode >= 100 &&\n statusCode <= 599\n ? statusCode\n : undefined;\n } catch {\n // OTLP projection must never mask the exact internal failure.\n return undefined;\n }\n}\n\nfunction errorToAttributes(error: TelemetrySpanError): Attributes {\n return {\n \"error.type\": error.type,\n ...(error.statusCode === undefined ? {} : { \"error.status_code\": error.statusCode }),\n };\n}\n\nfunction otlpAttributes(\n attributes: Attributes,\n): Array<{ key: string; value: Record<string, string | number | boolean> }> {\n return Object.entries(cleanAttributes(attributes)).map(([key, value]) => ({\n key,\n value: otlpValue(value),\n }));\n}\n\nfunction otlpValue(\n value: string | number | boolean | null,\n): Record<string, string | number | boolean> {\n if (typeof value === \"number\") {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === \"boolean\") {\n return { boolValue: value };\n }\n return { stringValue: value === null ? \"\" : boundedOtlpString(value) };\n}\n\nfunction boundedOtlpString(value: string): string {\n const bytes = new TextEncoder().encode(value);\n if (bytes.byteLength <= 512) return value;\n return `${new TextDecoder().decode(bytes.slice(0, 509))}…`;\n}\n\nfunction millisToNanos(ms: number): string {\n return String(BigInt(Math.round(ms)) * 1_000_000n);\n}\n\nfunction randomHex(bytes: number): string {\n const values = crypto.getRandomValues(new Uint8Array(bytes));\n return Array.from(values, (value) => value.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function parseHeaders(value: string): Record<string, string> {\n if (!value.trim()) {\n return {};\n }\n const entries: Array<[string, string]> = value\n .split(\",\")\n .map((pair): [string, string] => {\n const separator = pair.indexOf(\"=\");\n if (separator === -1) {\n return [pair.trim(), \"\"];\n }\n return [pair.slice(0, separator).trim(), pair.slice(separator + 1).trim()];\n })\n .filter(([key]) => key.length > 0);\n return Object.fromEntries(entries);\n}\n\nasync function defaultExporter(\n url: string,\n body: unknown,\n headers: Record<string, string>,\n): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...headers,\n },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n throw new Error(`OTLP endpoint returned HTTP ${response.status}`);\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/observability",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.3-canary.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@opengeni/contracts": "^
|
|
34
|
+
"@opengeni/contracts": "^2.2.0-canary.0",
|
|
35
35
|
"prom-client": "^15.1.3"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,9 @@ export type ObservabilitySettings = {
|
|
|
13
13
|
observabilityMetricsEnabled: boolean;
|
|
14
14
|
observabilityOtlpEndpoint?: string | undefined;
|
|
15
15
|
observabilityOtlpHeaders: string;
|
|
16
|
+
sandboxOwnershipEnabled?: boolean | undefined;
|
|
17
|
+
sandboxLazyProvisionEnabled?: boolean | undefined;
|
|
18
|
+
rigVerificationLeaseOwnershipEnabled?: boolean | undefined;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
export type ObservabilityOptions = {
|
|
@@ -85,6 +88,89 @@ const SANDBOX_OPERATION_NAMES = new Set([
|
|
|
85
88
|
"serializeSessionState",
|
|
86
89
|
]);
|
|
87
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Closed set of organization-tenancy compatibility lanes (the tenancy
|
|
93
|
+
* migration's validate phase; see `docs/organization-tenancy.md`).
|
|
94
|
+
*
|
|
95
|
+
* Each entry names one legacy behaviour a live request can still take today.
|
|
96
|
+
* The metric built from them is deliberately a COUNTER of lane USES, and it
|
|
97
|
+
* measures exactly one thing: **is this lane still being exercised?** That is
|
|
98
|
+
* the question the tenancy cutover gate ("zero incompatible writers") actually
|
|
99
|
+
* asks, and it is answerable.
|
|
100
|
+
*
|
|
101
|
+
* It is emphatically NOT a burndown of unmigrated rows, and must never be
|
|
102
|
+
* relabelled as one. Every one of these lanes is still *open*: the live write
|
|
103
|
+
* paths keep producing rows in it, so no row-count predicate over any of them
|
|
104
|
+
* can reach zero and a backlog gauge would read as a permanent, untruthful
|
|
105
|
+
* unmigrated backlog. `docs/organization-tenancy.md` records, per lane, why -
|
|
106
|
+
* including the lanes deliberately left uninstrumented because no truthful
|
|
107
|
+
* signal exists for them yet.
|
|
108
|
+
*
|
|
109
|
+
* The recorded value is the lane name and nothing else. No organization,
|
|
110
|
+
* workspace, session, subject, connection, or resource identity, and no
|
|
111
|
+
* credential, name, or content, may ever become a label here.
|
|
112
|
+
*/
|
|
113
|
+
export const TENANCY_COMPATIBILITY_LANES = [
|
|
114
|
+
/**
|
|
115
|
+
* A provider connection carrying `authority_scope = 'legacy_user'` was
|
|
116
|
+
* resolved for accepted use: a personal row with no common authority or
|
|
117
|
+
* grant, admitted through `legacy_user_compatibility` provenance instead of
|
|
118
|
+
* an explicit delegation. Migration 0256 backfilled the existing rows, but
|
|
119
|
+
* its `bind_connection_authority` trigger still classifies a NEW personal
|
|
120
|
+
* connection this way whenever the inserting subject has no active
|
|
121
|
+
* organization membership, so this counter is expected to stay above zero
|
|
122
|
+
* until that classification changes. That is the finding, not a defect.
|
|
123
|
+
*/
|
|
124
|
+
"connection_legacy_user",
|
|
125
|
+
/**
|
|
126
|
+
* A workspace-scope connection ref carried no connection id, so the
|
|
127
|
+
* accepted-use authority (migration 0279) could not identify the exact row
|
|
128
|
+
* and the request fell back to the unprivileged pre-snapshot resolution.
|
|
129
|
+
* This lane writes no `connection_use_audit_facts` row and leaves no durable
|
|
130
|
+
* trace of any kind, so a use counter is the only possible evidence it was
|
|
131
|
+
* taken at all.
|
|
132
|
+
*/
|
|
133
|
+
"connection_pre_snapshot_ref",
|
|
134
|
+
/**
|
|
135
|
+
* A NEW persistable `/workspace` mutation was refused because the writer
|
|
136
|
+
* behind it has no recorded authority (`authority_unattributed`): either a
|
|
137
|
+
* direct request whose principal froze the pre-0096 legacy initiator, or a
|
|
138
|
+
* retained process whose pre-0277 row has no tenancy half. This counts
|
|
139
|
+
* REFUSED MUTATIONS, not unattributed rows - the turn and process admission
|
|
140
|
+
* lanes deliberately still accept a `legacy_unattributed` initiator, and
|
|
141
|
+
* nothing may invent the missing authority or disturb the running provider
|
|
142
|
+
* process.
|
|
143
|
+
*/
|
|
144
|
+
"workspace_writer_unattributed",
|
|
145
|
+
] as const;
|
|
146
|
+
export type TenancyCompatibilityLane = (typeof TENANCY_COMPATIBILITY_LANES)[number];
|
|
147
|
+
|
|
148
|
+
const TENANCY_COMPATIBILITY_LANE_SET = new Set<string>(TENANCY_COMPATIBILITY_LANES);
|
|
149
|
+
|
|
150
|
+
const TENANCY_COMPATIBILITY_LANE_METRIC = {
|
|
151
|
+
name: "opengeni_tenancy_compatibility_lane_uses_total",
|
|
152
|
+
help: "Live uses of an organization-tenancy compatibility lane, by lane.",
|
|
153
|
+
} as const;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Record one live use of a tenancy compatibility lane. Fail-safe by
|
|
157
|
+
* construction: telemetry must never change an authorization or credential
|
|
158
|
+
* outcome, so a registry failure is counted best-effort and swallowed here.
|
|
159
|
+
*/
|
|
160
|
+
export function recordTenancyCompatibilityLaneUse(
|
|
161
|
+
observability: Observability | null | undefined,
|
|
162
|
+
lane: TenancyCompatibilityLane,
|
|
163
|
+
): void {
|
|
164
|
+
if (!observability) return;
|
|
165
|
+
// An unreviewed lane name must not silently mint a new series.
|
|
166
|
+
if (!TENANCY_COMPATIBILITY_LANE_SET.has(lane)) return;
|
|
167
|
+
try {
|
|
168
|
+
observability.incrementCounter({ ...TENANCY_COMPATIBILITY_LANE_METRIC, labels: { lane } });
|
|
169
|
+
} catch {
|
|
170
|
+
recordObserverFailure(observability, "tenancy_compatibility_lane");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
88
174
|
const INTERACTION_RESOURCES = new Set(["browser", "computer"]);
|
|
89
175
|
const INTERACTION_OPERATIONS = new Set([
|
|
90
176
|
"create",
|
|
@@ -149,6 +235,17 @@ const INTERACTION_INTERVENTION_OUTCOMES = new Set([
|
|
|
149
235
|
"expired",
|
|
150
236
|
"cancelled",
|
|
151
237
|
]);
|
|
238
|
+
const PUBLIC_STARTUP_DEPENDENCIES = new Set([
|
|
239
|
+
"Modal private-registry image",
|
|
240
|
+
"NATS",
|
|
241
|
+
"PostgreSQL runtime posture",
|
|
242
|
+
"Temporal",
|
|
243
|
+
"Temporal client",
|
|
244
|
+
"Temporal schedule (file upload reaper)",
|
|
245
|
+
"Temporal schedule (sandbox reaper)",
|
|
246
|
+
"Temporal schedule (session-workflow wake dispatcher)",
|
|
247
|
+
"Temporal schedule (site auth maintenance)",
|
|
248
|
+
]);
|
|
152
249
|
|
|
153
250
|
/**
|
|
154
251
|
* External logs and OTLP are public/third-party projections, not canonical
|
|
@@ -206,6 +303,7 @@ const PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = new Set([
|
|
|
206
303
|
* caller accidentally publish a raw identifier under that name. */
|
|
207
304
|
const PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS = new Map<string, RegExp>([
|
|
208
305
|
["sandboxLeaseKey", /^slk_[0-9a-f]{32}$/],
|
|
306
|
+
["correlationId", /^[A-Za-z0-9._:-]{1,128}$/],
|
|
209
307
|
]);
|
|
210
308
|
|
|
211
309
|
const PUBLIC_CHANNEL_A_OPERATIONS = new Set([
|
|
@@ -419,6 +517,50 @@ export class Observability {
|
|
|
419
517
|
},
|
|
420
518
|
value: 1,
|
|
421
519
|
});
|
|
520
|
+
this.registerSandboxRolloutConfig();
|
|
521
|
+
this.registerTenancyCompatibilityLanes();
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Publish every compatibility lane at zero on startup. Without this an
|
|
527
|
+
* un-exercised lane has no series at all, and an operator cannot tell "this
|
|
528
|
+
* lane is dormant" from "this lane was never wired up" - the two readings
|
|
529
|
+
* that matter most to a cutover gate.
|
|
530
|
+
*/
|
|
531
|
+
private registerTenancyCompatibilityLanes(): void {
|
|
532
|
+
for (const lane of TENANCY_COMPATIBILITY_LANES) {
|
|
533
|
+
this.incrementCounter({ ...TENANCY_COMPATIBILITY_LANE_METRIC, labels: { lane }, amount: 0 });
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private registerSandboxRolloutConfig(): void {
|
|
538
|
+
const ownership = this.settings.sandboxOwnershipEnabled;
|
|
539
|
+
const lazyConfigured = this.settings.sandboxLazyProvisionEnabled;
|
|
540
|
+
const rigVerification = this.settings.rigVerificationLeaseOwnershipEnabled;
|
|
541
|
+
if (
|
|
542
|
+
typeof ownership !== "boolean" ||
|
|
543
|
+
typeof lazyConfigured !== "boolean" ||
|
|
544
|
+
typeof rigVerification !== "boolean"
|
|
545
|
+
) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const values = [
|
|
550
|
+
["ownership", "configured", ownership],
|
|
551
|
+
["ownership", "effective", ownership],
|
|
552
|
+
["lazy_provision", "configured", lazyConfigured],
|
|
553
|
+
["lazy_provision", "effective", ownership && lazyConfigured],
|
|
554
|
+
["rig_verification_lease_ownership", "configured", rigVerification],
|
|
555
|
+
["rig_verification_lease_ownership", "effective", rigVerification],
|
|
556
|
+
] as const;
|
|
557
|
+
for (const [feature, state, enabled] of values) {
|
|
558
|
+
this.setGauge({
|
|
559
|
+
name: "opengeni_sandbox_rollout_config",
|
|
560
|
+
help: "Configured and effective sandbox rollout state for this workload revision.",
|
|
561
|
+
labels: { feature, state },
|
|
562
|
+
value: enabled ? 1 : 0,
|
|
563
|
+
});
|
|
422
564
|
}
|
|
423
565
|
}
|
|
424
566
|
|
|
@@ -934,15 +1076,19 @@ export function logStartupDependencyRetry(
|
|
|
934
1076
|
observability: Observability,
|
|
935
1077
|
event: StartupDependencyRetryEvent,
|
|
936
1078
|
): void {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
attempt
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1079
|
+
const dependency = PUBLIC_STARTUP_DEPENDENCIES.has(event.label) ? event.label : "Dependency";
|
|
1080
|
+
observability.warn(
|
|
1081
|
+
`Startup dependency ${dependency} connection failed; retrying (${event.attempt}/${event.attempts} in ${event.delayMs}ms)`,
|
|
1082
|
+
{
|
|
1083
|
+
dependency,
|
|
1084
|
+
attempt: event.attempt,
|
|
1085
|
+
attempts: event.attempts,
|
|
1086
|
+
delayMs: event.delayMs,
|
|
1087
|
+
errorClass: "StartupDependencyError",
|
|
1088
|
+
errorCode: "startup_dependency_retry",
|
|
1089
|
+
origin: "observability",
|
|
1090
|
+
},
|
|
1091
|
+
);
|
|
946
1092
|
}
|
|
947
1093
|
|
|
948
1094
|
function normalizeLabels(labels: MetricLabels = {}): Record<string, string> {
|
|
@@ -967,6 +1113,7 @@ function cleanAttributes(attributes: Attributes): Record<string, string | number
|
|
|
967
1113
|
function projectPublicTelemetryAttributes(attributes: Attributes): Attributes {
|
|
968
1114
|
if ("errorClass" in attributes || "errorCode" in attributes) {
|
|
969
1115
|
return {
|
|
1116
|
+
...projectStartupDependencyAttributes(attributes),
|
|
970
1117
|
...projectPublicChannelADiagnosticAttributes(attributes),
|
|
971
1118
|
...projectPublicDiagnosticAttributes(attributes),
|
|
972
1119
|
};
|
|
@@ -981,6 +1128,29 @@ function projectPublicTelemetryAttributes(attributes: Attributes): Attributes {
|
|
|
981
1128
|
);
|
|
982
1129
|
}
|
|
983
1130
|
|
|
1131
|
+
function projectStartupDependencyAttributes(attributes: Attributes): Attributes {
|
|
1132
|
+
if (
|
|
1133
|
+
attributes.errorClass !== "StartupDependencyError" ||
|
|
1134
|
+
attributes.errorCode !== "startup_dependency_retry"
|
|
1135
|
+
) {
|
|
1136
|
+
return {};
|
|
1137
|
+
}
|
|
1138
|
+
const dependency = attributes.dependency;
|
|
1139
|
+
const attempt = attributes.attempt;
|
|
1140
|
+
const attempts = attributes.attempts;
|
|
1141
|
+
const delayMs = attributes.delayMs;
|
|
1142
|
+
return {
|
|
1143
|
+
...(typeof dependency === "string" && PUBLIC_STARTUP_DEPENDENCIES.has(dependency)
|
|
1144
|
+
? { dependency }
|
|
1145
|
+
: {}),
|
|
1146
|
+
...(typeof attempt === "number" && Number.isInteger(attempt) && attempt > 0 ? { attempt } : {}),
|
|
1147
|
+
...(typeof attempts === "number" && Number.isInteger(attempts) && attempts > 0
|
|
1148
|
+
? { attempts }
|
|
1149
|
+
: {}),
|
|
1150
|
+
...(typeof delayMs === "number" && Number.isFinite(delayMs) && delayMs >= 0 ? { delayMs } : {}),
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
|
|
984
1154
|
function projectPublicChannelADiagnosticAttributes(attributes: Attributes): Attributes {
|
|
985
1155
|
if (attributes.errorClass !== "SandboxChannelAOperationError") return {};
|
|
986
1156
|
const backend = attributes.backend;
|
|
@@ -1011,6 +1181,7 @@ function projectPublicDiagnosticAttributes(attributes: Attributes): Attributes {
|
|
|
1011
1181
|
const errorCode = attributes.errorCode;
|
|
1012
1182
|
const status = attributes.status;
|
|
1013
1183
|
const origin = attributes.origin;
|
|
1184
|
+
const correlationId = attributes.correlationId;
|
|
1014
1185
|
return {
|
|
1015
1186
|
errorClass:
|
|
1016
1187
|
typeof errorClass === "string" && PUBLIC_TELEMETRY_ERROR_CLASSES.has(errorClass)
|
|
@@ -1023,6 +1194,10 @@ function projectPublicDiagnosticAttributes(attributes: Attributes): Attributes {
|
|
|
1023
1194
|
? { status }
|
|
1024
1195
|
: {}),
|
|
1025
1196
|
...(typeof origin === "string" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {}),
|
|
1197
|
+
...(typeof correlationId === "string" &&
|
|
1198
|
+
PUBLIC_TELEMETRY_OPAQUE_ATTRIBUTE_PATTERNS.get("correlationId")?.test(correlationId)
|
|
1199
|
+
? { correlationId }
|
|
1200
|
+
: {}),
|
|
1026
1201
|
};
|
|
1027
1202
|
}
|
|
1028
1203
|
|