@frontmcp/observability 1.9.1 → 1.9.3

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/index.js CHANGED
@@ -399,8 +399,8 @@ __export(index_exports, {
399
399
  module.exports = __toCommonJS(index_exports);
400
400
 
401
401
  // libs/observability/src/plugin/observability.plugin.ts
402
- var import_api16 = require("@opentelemetry/api");
403
- var import_sdk2 = require("@frontmcp/sdk");
402
+ var import_api17 = require("@opentelemetry/api");
403
+ var import_sdk4 = require("@frontmcp/sdk");
404
404
 
405
405
  // libs/observability/src/logging/sinks/stdout.sink.ts
406
406
  function defaultStream() {
@@ -763,6 +763,13 @@ var RequestLogCollector = class {
763
763
  this.status = status;
764
764
  this.statusCode = statusCode;
765
765
  }
766
+ /**
767
+ * Set the HTTP status the request was answered with; one of 400 or more marks it failed.
768
+ */
769
+ setStatusCode(statusCode) {
770
+ this.statusCode = statusCode;
771
+ if (statusCode >= 400) this.status = "error";
772
+ }
766
773
  /**
767
774
  * Set error details.
768
775
  */
@@ -831,11 +838,13 @@ var RequestLogCollector = class {
831
838
  }
832
839
  };
833
840
 
834
- // libs/observability/src/request-log/request-log.tokens.ts
835
- var REQUEST_LOG_COLLECTOR = /* @__PURE__ */ Symbol.for("frontmcp:observability:request-log-collector");
841
+ // libs/observability/src/request-log/request-log.hooks.ts
842
+ var import_sdk3 = require("@frontmcp/sdk");
836
843
 
837
- // libs/observability/src/telemetry/telemetry.accessor.ts
838
- var import_api14 = require("@opentelemetry/api");
844
+ // libs/observability/src/plugin/observability.hooks.ts
845
+ var import_api13 = require("@opentelemetry/api");
846
+ var import_sdk2 = require("@frontmcp/sdk");
847
+ var import_utils = require("@frontmcp/utils");
839
848
 
840
849
  // libs/observability/src/otel/otel.types.ts
841
850
  var McpAttributes = {
@@ -950,60 +959,31 @@ var EnduserAttributes = {
950
959
  SCOPE: "enduser.scope"
951
960
  };
952
961
 
953
- // libs/observability/src/otel/trace-context-bridge.ts
954
- var import_api = require("@opentelemetry/api");
955
- function frontmcpToOTelSpanContext(tc) {
956
- return {
957
- traceId: tc.traceId,
958
- spanId: tc.parentId,
959
- traceFlags: tc.traceFlags & import_api.TraceFlags.SAMPLED ? import_api.TraceFlags.SAMPLED : import_api.TraceFlags.NONE,
960
- isRemote: true
961
- };
962
- }
963
- function otelToFrontmcpContext(sc) {
964
- const flags = sc.traceFlags.toString(16).padStart(2, "0");
965
- return {
966
- traceId: sc.traceId,
967
- parentId: sc.spanId,
968
- traceFlags: sc.traceFlags,
969
- raw: `00-${sc.traceId}-${sc.spanId}-${flags}`
970
- };
971
- }
972
- function createOTelContextFromTrace(tc) {
973
- const spanContext = frontmcpToOTelSpanContext(tc);
974
- const span = import_api.trace.wrapSpanContext(spanContext);
975
- return import_api.trace.setSpan(import_api.context.active(), span);
976
- }
977
-
978
- // libs/observability/src/plugin/observability.hooks.ts
979
- var import_api12 = require("@opentelemetry/api");
980
- var import_utils = require("@frontmcp/utils");
981
-
982
962
  // libs/observability/src/otel/spans/auth.span.ts
983
- var import_api3 = require("@opentelemetry/api");
963
+ var import_api2 = require("@opentelemetry/api");
984
964
 
985
965
  // libs/observability/src/otel/spans/span.utils.ts
986
- var import_api2 = require("@opentelemetry/api");
966
+ var import_api = require("@opentelemetry/api");
987
967
  function startSpan(tracer, options) {
988
- const parentCtx = options.parentContext ?? import_api2.context.active();
968
+ const parentCtx = options.parentContext ?? import_api.context.active();
989
969
  const span = tracer.startSpan(
990
970
  options.name,
991
971
  {
992
- kind: options.kind ?? import_api2.SpanKind.INTERNAL,
972
+ kind: options.kind ?? import_api.SpanKind.INTERNAL,
993
973
  attributes: options.attributes
994
974
  },
995
975
  parentCtx
996
976
  );
997
- const spanContext = import_api2.trace.setSpan(parentCtx, span);
977
+ const spanContext = import_api.trace.setSpan(parentCtx, span);
998
978
  return { span, context: spanContext };
999
979
  }
1000
980
  function endSpanOk(span) {
1001
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
981
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
1002
982
  span.end();
1003
983
  }
1004
984
  function endSpanError(span, error) {
1005
985
  const message = typeof error === "string" ? error : error.message;
1006
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
986
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message });
1007
987
  if (error instanceof Error) {
1008
988
  span.recordException(error);
1009
989
  }
@@ -1011,7 +991,7 @@ function endSpanError(span, error) {
1011
991
  }
1012
992
  async function withSpan(tracer, options, fn) {
1013
993
  const { span, context: spanContext } = startSpan(tracer, options);
1014
- return import_api2.context.with(spanContext, async () => {
994
+ return import_api.context.with(spanContext, async () => {
1015
995
  try {
1016
996
  const result = await fn(span);
1017
997
  endSpanOk(span);
@@ -1027,7 +1007,7 @@ async function withSpan(tracer, options, fn) {
1027
1007
  function startAuthSpan(tracer, options) {
1028
1008
  return startSpan(tracer, {
1029
1009
  name: `auth ${options.flowName}`,
1030
- kind: import_api3.SpanKind.INTERNAL,
1010
+ kind: import_api2.SpanKind.INTERNAL,
1031
1011
  attributes: {
1032
1012
  [FrontMcpAttributes.FLOW_NAME]: options.flowName
1033
1013
  },
@@ -1042,11 +1022,11 @@ function setAuthResult(span, result) {
1042
1022
  }
1043
1023
 
1044
1024
  // libs/observability/src/otel/spans/fetch.span.ts
1045
- var import_api4 = require("@opentelemetry/api");
1025
+ var import_api3 = require("@opentelemetry/api");
1046
1026
  function startFetchSpan(tracer, options) {
1047
1027
  return startSpan(tracer, {
1048
1028
  name: options.method.toUpperCase(),
1049
- kind: import_api4.SpanKind.CLIENT,
1029
+ kind: import_api3.SpanKind.CLIENT,
1050
1030
  attributes: {
1051
1031
  [HttpAttributes.METHOD]: options.method.toUpperCase(),
1052
1032
  [HttpAttributes.URL_FULL]: options.url
@@ -1058,6 +1038,33 @@ function setFetchResponseStatus(span, statusCode) {
1058
1038
  span.setAttribute(HttpAttributes.STATUS_CODE, statusCode);
1059
1039
  }
1060
1040
 
1041
+ // libs/observability/src/otel/spans/hook.span.ts
1042
+ var import_api4 = require("@opentelemetry/api");
1043
+ function recordHookEvent(parentSpan, stage, owner) {
1044
+ const attributes = {};
1045
+ if (owner) {
1046
+ attributes[FrontMcpAttributes.HOOK_OWNER] = owner;
1047
+ }
1048
+ parentSpan.addEvent(`hook.${stage}`, attributes);
1049
+ }
1050
+ function startHookSpan(tracer, options) {
1051
+ const attributes = {
1052
+ [FrontMcpAttributes.HOOK_STAGE]: options.stage
1053
+ };
1054
+ if (options.owner) {
1055
+ attributes[FrontMcpAttributes.HOOK_OWNER] = options.owner;
1056
+ }
1057
+ if (options.flowName) {
1058
+ attributes[FrontMcpAttributes.FLOW_NAME] = options.flowName;
1059
+ }
1060
+ return startSpan(tracer, {
1061
+ name: `hook ${options.stage}`,
1062
+ kind: import_api4.SpanKind.INTERNAL,
1063
+ attributes,
1064
+ parentContext: options.parentContext
1065
+ });
1066
+ }
1067
+
1061
1068
  // libs/observability/src/otel/spans/http-server.span.ts
1062
1069
  var import_api5 = require("@opentelemetry/api");
1063
1070
  function startHttpServerSpan(tracer, options) {
@@ -1226,6 +1233,31 @@ function setTransportRequestType(span, requestType) {
1226
1233
  span.setAttribute(FrontMcpAttributes.TRANSPORT_REQUEST_TYPE, requestType);
1227
1234
  }
1228
1235
 
1236
+ // libs/observability/src/otel/trace-context-bridge.ts
1237
+ var import_api12 = require("@opentelemetry/api");
1238
+ function frontmcpToOTelSpanContext(tc) {
1239
+ return {
1240
+ traceId: tc.traceId,
1241
+ spanId: tc.parentId,
1242
+ traceFlags: tc.traceFlags & import_api12.TraceFlags.SAMPLED ? import_api12.TraceFlags.SAMPLED : import_api12.TraceFlags.NONE,
1243
+ isRemote: true
1244
+ };
1245
+ }
1246
+ function otelToFrontmcpContext(sc) {
1247
+ const flags = sc.traceFlags.toString(16).padStart(2, "0");
1248
+ return {
1249
+ traceId: sc.traceId,
1250
+ parentId: sc.spanId,
1251
+ traceFlags: sc.traceFlags,
1252
+ raw: `00-${sc.traceId}-${sc.spanId}-${flags}`
1253
+ };
1254
+ }
1255
+ function createOTelContextFromTrace(tc) {
1256
+ const spanContext = frontmcpToOTelSpanContext(tc);
1257
+ const span = import_api12.trace.wrapSpanContext(spanContext);
1258
+ return import_api12.trace.setSpan(import_api12.context.active(), span);
1259
+ }
1260
+
1229
1261
  // libs/observability/src/plugin/observability.hooks.ts
1230
1262
  var SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:span");
1231
1263
  var SPAN_CTX_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:span-ctx");
@@ -1233,11 +1265,13 @@ var EXEC_SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:exec-span");
1233
1265
  var ACTIVE_SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:active-span");
1234
1266
  var ACTIVE_OTEL_CTX_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:active-otel-ctx");
1235
1267
  var STAGE_TS_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:stage-ts");
1268
+ var STAGE_EVENTS_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:stage-events");
1269
+ var HTTP_SPAN_CTX_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:http-span-ctx");
1236
1270
  function sessionTracingId(sessionId) {
1237
1271
  return (0, import_utils.sha256Hex)(sessionId).slice(0, 16);
1238
1272
  }
1239
1273
  function getTracer() {
1240
- return import_api12.trace.getTracer("@frontmcp/observability");
1274
+ return import_api13.trace.getTracer("@frontmcp/observability");
1241
1275
  }
1242
1276
  function extractContext(flowCtx) {
1243
1277
  try {
@@ -1267,7 +1301,44 @@ function clearActiveSpan(flowCtx) {
1267
1301
  } catch {
1268
1302
  }
1269
1303
  }
1304
+ function beginFlowSpan(flowCtx, options, span, spanCtx) {
1305
+ flowCtx.state[SPAN_KEY] = span;
1306
+ flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1307
+ flowCtx.state[STAGE_TS_KEY] = Date.now();
1308
+ flowCtx.state[STAGE_EVENTS_KEY] = options.flowStageEvents !== false;
1309
+ }
1310
+ function parentContextOf(ctx) {
1311
+ return ctx.get?.(HTTP_SPAN_CTX_KEY) ?? createOTelContextFromTrace(ctx.traceContext);
1312
+ }
1313
+ function flowFailureOf(flowError) {
1314
+ const failed = flowError?.originalError;
1315
+ const error = flowError instanceof import_sdk2.FlowControl ? failed instanceof Error ? failed : new import_sdk2.InternalMcpError(`Flow ended with: ${flowError.type}`) : flowError instanceof Error ? flowError : new Error(String(flowError));
1316
+ const reported = (0, import_sdk2.toMcpError)(error);
1317
+ return {
1318
+ error,
1319
+ type: reported.name,
1320
+ message: reported.getPublicMessage(),
1321
+ code: reported.code,
1322
+ errorId: reported.errorId
1323
+ };
1324
+ }
1325
+ function endFailedSpan(span, flowError) {
1326
+ const failure = flowFailureOf(flowError);
1327
+ span.setStatus({ code: import_api13.SpanStatusCode.ERROR, message: failure.message });
1328
+ span.recordException({
1329
+ name: failure.type,
1330
+ message: failure.message,
1331
+ code: failure.code,
1332
+ stack: failure.error.stack
1333
+ });
1334
+ span.end();
1335
+ }
1336
+ function traceparentOf(span) {
1337
+ const { traceId, spanId, traceFlags } = span.spanContext();
1338
+ return `00-${traceId}-${spanId}-${traceFlags.toString(16).padStart(2, "0")}`;
1339
+ }
1270
1340
  function recordStageEvent(span, stageName, state) {
1341
+ if (state[STAGE_EVENTS_KEY] === false) return;
1271
1342
  const now = Date.now();
1272
1343
  const prev = state[STAGE_TS_KEY];
1273
1344
  const attributes = {};
@@ -1293,9 +1364,8 @@ function onHttpWillTrace(options, flowCtx) {
1293
1364
  });
1294
1365
  span.setAttribute(McpAttributes.SESSION_ID, sessionHash);
1295
1366
  span.setAttribute(FrontMcpAttributes.SESSION_ID_HASH, sessionHash);
1296
- flowCtx.state[SPAN_KEY] = span;
1297
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1298
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1367
+ ctx.set?.(HTTP_SPAN_CTX_KEY, spanCtx);
1368
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1299
1369
  recordStageEvent(span, "traceRequest", flowCtx.state);
1300
1370
  }
1301
1371
  function onHttpWillAcquireQuota(flowCtx) {
@@ -1339,7 +1409,7 @@ function onToolWillParse(options, flowCtx) {
1339
1409
  const ctx = extractContext(flowCtx);
1340
1410
  if (!ctx) return;
1341
1411
  const tracer = getTracer();
1342
- const parentOTelCtx = createOTelContextFromTrace(ctx.traceContext);
1412
+ const parentOTelCtx = parentContextOf(ctx);
1343
1413
  const sessionHash = sessionTracingId(ctx.sessionId);
1344
1414
  const { span, context: spanCtx } = startRpcSpan(tracer, {
1345
1415
  method: "tools/call",
@@ -1347,9 +1417,7 @@ function onToolWillParse(options, flowCtx) {
1347
1417
  sessionIdHash: sessionHash,
1348
1418
  parentContext: parentOTelCtx
1349
1419
  });
1350
- flowCtx.state[SPAN_KEY] = span;
1351
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1352
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1420
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1353
1421
  recordStageEvent(span, "parseInput", flowCtx.state);
1354
1422
  }
1355
1423
  function onToolWillFindTool(flowCtx) {
@@ -1417,10 +1485,10 @@ function onToolWillApplyUI(flowCtx) {
1417
1485
  function onToolDidFinalize(flowCtx) {
1418
1486
  const rpcSpan = flowCtx.state?.[SPAN_KEY];
1419
1487
  const toolSpan = flowCtx.state?.[EXEC_SPAN_KEY];
1420
- const error = flowCtx.state?.error;
1488
+ const error = flowCtx.state?.flowError;
1421
1489
  if (error) {
1422
- if (toolSpan) endSpanError(toolSpan, error instanceof Error ? error : String(error));
1423
- if (rpcSpan) endSpanError(rpcSpan, error instanceof Error ? error : String(error));
1490
+ if (toolSpan) endFailedSpan(toolSpan, error);
1491
+ if (rpcSpan) endFailedSpan(rpcSpan, error);
1424
1492
  } else if (rpcSpan) {
1425
1493
  recordStageEvent(rpcSpan, "finalize", flowCtx.state);
1426
1494
  endSpanOk(rpcSpan);
@@ -1431,7 +1499,7 @@ function onResourceWillParse(options, flowCtx) {
1431
1499
  const ctx = extractContext(flowCtx);
1432
1500
  if (!ctx) return;
1433
1501
  const tracer = getTracer();
1434
- const parentOTelCtx = createOTelContextFromTrace(ctx.traceContext);
1502
+ const parentOTelCtx = parentContextOf(ctx);
1435
1503
  const sessionHash = sessionTracingId(ctx.sessionId);
1436
1504
  const { span, context: spanCtx } = startRpcSpan(tracer, {
1437
1505
  method: "resources/read",
@@ -1439,9 +1507,7 @@ function onResourceWillParse(options, flowCtx) {
1439
1507
  sessionIdHash: sessionHash,
1440
1508
  parentContext: parentOTelCtx
1441
1509
  });
1442
- flowCtx.state[SPAN_KEY] = span;
1443
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1444
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1510
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1445
1511
  recordStageEvent(span, "parseInput", flowCtx.state);
1446
1512
  }
1447
1513
  function onResourceWillFind(flowCtx) {
@@ -1470,10 +1536,10 @@ function onResourceDidExecute(flowCtx) {
1470
1536
  function onResourceDidFinalize(flowCtx) {
1471
1537
  const rpcSpan = flowCtx.state?.[SPAN_KEY];
1472
1538
  const resSpan = flowCtx.state?.[EXEC_SPAN_KEY];
1473
- const error = flowCtx.state?.error;
1539
+ const error = flowCtx.state?.flowError;
1474
1540
  if (error) {
1475
- if (resSpan) endSpanError(resSpan, error instanceof Error ? error : String(error));
1476
- if (rpcSpan) endSpanError(rpcSpan, error instanceof Error ? error : String(error));
1541
+ if (resSpan) endFailedSpan(resSpan, error);
1542
+ if (rpcSpan) endFailedSpan(rpcSpan, error);
1477
1543
  } else if (rpcSpan) {
1478
1544
  recordStageEvent(rpcSpan, "finalize", flowCtx.state);
1479
1545
  endSpanOk(rpcSpan);
@@ -1484,7 +1550,7 @@ function onPromptWillParse(options, flowCtx) {
1484
1550
  const ctx = extractContext(flowCtx);
1485
1551
  if (!ctx) return;
1486
1552
  const tracer = getTracer();
1487
- const parentOTelCtx = createOTelContextFromTrace(ctx.traceContext);
1553
+ const parentOTelCtx = parentContextOf(ctx);
1488
1554
  const sessionHash = sessionTracingId(ctx.sessionId);
1489
1555
  const { span, context: spanCtx } = startRpcSpan(tracer, {
1490
1556
  method: "prompts/get",
@@ -1492,9 +1558,7 @@ function onPromptWillParse(options, flowCtx) {
1492
1558
  sessionIdHash: sessionHash,
1493
1559
  parentContext: parentOTelCtx
1494
1560
  });
1495
- flowCtx.state[SPAN_KEY] = span;
1496
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1497
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1561
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1498
1562
  recordStageEvent(span, "parseInput", flowCtx.state);
1499
1563
  }
1500
1564
  function onPromptWillFind(flowCtx) {
@@ -1526,10 +1590,10 @@ function onPromptDidExecute(flowCtx) {
1526
1590
  function onPromptDidFinalize(flowCtx) {
1527
1591
  const rpcSpan = flowCtx.state?.[SPAN_KEY];
1528
1592
  const promptSpan = flowCtx.state?.[EXEC_SPAN_KEY];
1529
- const error = flowCtx.state?.error;
1593
+ const error = flowCtx.state?.flowError;
1530
1594
  if (error) {
1531
- if (promptSpan) endSpanError(promptSpan, error instanceof Error ? error : String(error));
1532
- if (rpcSpan) endSpanError(rpcSpan, error instanceof Error ? error : String(error));
1595
+ if (promptSpan) endFailedSpan(promptSpan, error);
1596
+ if (rpcSpan) endFailedSpan(rpcSpan, error);
1533
1597
  } else if (rpcSpan) {
1534
1598
  recordStageEvent(rpcSpan, "finalize", flowCtx.state);
1535
1599
  endSpanOk(rpcSpan);
@@ -1540,7 +1604,7 @@ function onAgentWillParse(options, flowCtx) {
1540
1604
  const ctx = extractContext(flowCtx);
1541
1605
  if (!ctx) return;
1542
1606
  const tracer = getTracer();
1543
- const parentOTelCtx = createOTelContextFromTrace(ctx.traceContext);
1607
+ const parentOTelCtx = parentContextOf(ctx);
1544
1608
  const sessionHash = sessionTracingId(ctx.sessionId);
1545
1609
  const { span, context: spanCtx } = startRpcSpan(tracer, {
1546
1610
  method: "agents/call",
@@ -1548,9 +1612,7 @@ function onAgentWillParse(options, flowCtx) {
1548
1612
  sessionIdHash: sessionHash,
1549
1613
  parentContext: parentOTelCtx
1550
1614
  });
1551
- flowCtx.state[SPAN_KEY] = span;
1552
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1553
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1615
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1554
1616
  recordStageEvent(span, "parseInput", flowCtx.state);
1555
1617
  }
1556
1618
  function onAgentWillFind(flowCtx) {
@@ -1564,7 +1626,7 @@ function onAgentWillExecute(options, flowCtx) {
1564
1626
  const agentName = flowCtx.state?.input?.name ?? "unknown";
1565
1627
  const { span: agentSpan } = startSpan(tracer, {
1566
1628
  name: `agent ${agentName}`,
1567
- kind: import_api12.SpanKind.INTERNAL,
1629
+ kind: import_api13.SpanKind.INTERNAL,
1568
1630
  attributes: {
1569
1631
  [FrontMcpAttributes.FLOW_NAME]: "agents:call-agent",
1570
1632
  [McpAttributes.COMPONENT_TYPE]: "agent",
@@ -1572,7 +1634,7 @@ function onAgentWillExecute(options, flowCtx) {
1572
1634
  },
1573
1635
  parentContext: rpcCtx
1574
1636
  });
1575
- const agentOTelCtx = import_api12.trace.setSpan(rpcCtx ?? import_api12.context.active(), agentSpan);
1637
+ const agentOTelCtx = import_api13.trace.setSpan(rpcCtx ?? import_api13.context.active(), agentSpan);
1576
1638
  flowCtx.state[EXEC_SPAN_KEY] = agentSpan;
1577
1639
  flowCtx.state[STAGE_TS_KEY] = Date.now();
1578
1640
  recordStageEvent(agentSpan, "execute.start", flowCtx.state);
@@ -1589,10 +1651,10 @@ function onAgentDidExecute(flowCtx) {
1589
1651
  function onAgentDidFinalize(flowCtx) {
1590
1652
  const rpcSpan = flowCtx.state?.[SPAN_KEY];
1591
1653
  const agentSpan = flowCtx.state?.[EXEC_SPAN_KEY];
1592
- const error = flowCtx.state?.error;
1654
+ const error = flowCtx.state?.flowError;
1593
1655
  if (error) {
1594
- if (agentSpan) endSpanError(agentSpan, error instanceof Error ? error : String(error));
1595
- if (rpcSpan) endSpanError(rpcSpan, error instanceof Error ? error : String(error));
1656
+ if (agentSpan) endFailedSpan(agentSpan, error);
1657
+ if (rpcSpan) endFailedSpan(rpcSpan, error);
1596
1658
  } else if (rpcSpan) {
1597
1659
  recordStageEvent(rpcSpan, "finalize", flowCtx.state);
1598
1660
  endSpanOk(rpcSpan);
@@ -1603,11 +1665,11 @@ function onGenericFlowWillStart(flowName, options, flowCtx) {
1603
1665
  const ctx = extractContext(flowCtx);
1604
1666
  if (!ctx) return;
1605
1667
  const tracer = getTracer();
1606
- const parentOTelCtx = createOTelContextFromTrace(ctx.traceContext);
1668
+ const parentOTelCtx = parentContextOf(ctx);
1607
1669
  const sessionHash = sessionTracingId(ctx.sessionId);
1608
1670
  const { span, context: spanCtx } = startSpan(tracer, {
1609
1671
  name: flowName,
1610
- kind: import_api12.SpanKind.INTERNAL,
1672
+ kind: import_api13.SpanKind.INTERNAL,
1611
1673
  attributes: {
1612
1674
  [FrontMcpAttributes.FLOW_NAME]: flowName,
1613
1675
  [McpAttributes.SESSION_ID]: sessionHash,
@@ -1617,9 +1679,7 @@ function onGenericFlowWillStart(flowName, options, flowCtx) {
1617
1679
  },
1618
1680
  parentContext: parentOTelCtx
1619
1681
  });
1620
- flowCtx.state[SPAN_KEY] = span;
1621
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1622
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1682
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1623
1683
  }
1624
1684
  function onGenericFlowStage(stageName, flowCtx) {
1625
1685
  const span = flowCtx.state?.[SPAN_KEY];
@@ -1628,9 +1688,9 @@ function onGenericFlowStage(stageName, flowCtx) {
1628
1688
  function onGenericFlowDidFinalize(flowCtx) {
1629
1689
  const span = flowCtx.state?.[SPAN_KEY];
1630
1690
  if (!span) return;
1631
- const error = flowCtx.state?.error;
1691
+ const error = flowCtx.state?.flowError;
1632
1692
  if (error) {
1633
- endSpanError(span, error instanceof Error ? error : String(error));
1693
+ endFailedSpan(span, error);
1634
1694
  } else {
1635
1695
  endSpanOk(span);
1636
1696
  }
@@ -1640,16 +1700,14 @@ function onTransportWillStart(transportType, options, flowCtx) {
1640
1700
  const ctx = extractContext(flowCtx);
1641
1701
  if (!ctx) return;
1642
1702
  const tracer = getTracer();
1643
- const parentOTelCtx = createOTelContextFromTrace(ctx.traceContext);
1703
+ const parentOTelCtx = parentContextOf(ctx);
1644
1704
  const sessionHash = sessionTracingId(ctx.sessionId);
1645
1705
  const { span, context: spanCtx } = startTransportSpan(tracer, {
1646
1706
  type: transportType,
1647
1707
  sessionIdHash: sessionHash,
1648
1708
  parentContext: parentOTelCtx
1649
1709
  });
1650
- flowCtx.state[SPAN_KEY] = span;
1651
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1652
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1710
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1653
1711
  recordStageEvent(span, "parseInput", flowCtx.state);
1654
1712
  }
1655
1713
  function onTransportDidRoute(flowCtx) {
@@ -1667,9 +1725,9 @@ function onTransportDidFinalize(flowCtx) {
1667
1725
  const span = flowCtx.state?.[SPAN_KEY];
1668
1726
  if (!span) return;
1669
1727
  recordStageEvent(span, "cleanup", flowCtx.state);
1670
- const error = flowCtx.state?.error;
1728
+ const error = flowCtx.state?.flowError;
1671
1729
  if (error) {
1672
- endSpanError(span, error instanceof Error ? error : String(error));
1730
+ endFailedSpan(span, error);
1673
1731
  } else {
1674
1732
  endSpanOk(span);
1675
1733
  }
@@ -1679,14 +1737,12 @@ function onAuthWillStart(flowName, options, flowCtx) {
1679
1737
  const ctx = extractContext(flowCtx);
1680
1738
  if (!ctx) return;
1681
1739
  const tracer = getTracer();
1682
- const parentOTelCtx = createOTelContextFromTrace(ctx.traceContext);
1740
+ const parentOTelCtx = parentContextOf(ctx);
1683
1741
  const { span, context: spanCtx } = startAuthSpan(tracer, {
1684
1742
  flowName,
1685
1743
  parentContext: parentOTelCtx
1686
1744
  });
1687
- flowCtx.state[SPAN_KEY] = span;
1688
- flowCtx.state[SPAN_CTX_KEY] = spanCtx;
1689
- flowCtx.state[STAGE_TS_KEY] = Date.now();
1745
+ beginFlowSpan(flowCtx, options, span, spanCtx);
1690
1746
  recordStageEvent(span, "parseInput", flowCtx.state);
1691
1747
  }
1692
1748
  function onAuthDidDetermineMode(flowCtx) {
@@ -1703,33 +1759,38 @@ function onAuthStage(stageName, flowCtx) {
1703
1759
  function onAuthDidFinalize(flowCtx) {
1704
1760
  const span = flowCtx.state?.[SPAN_KEY];
1705
1761
  if (!span) return;
1706
- const error = flowCtx.state?.error;
1762
+ const error = flowCtx.state?.flowError;
1707
1763
  if (error) {
1708
1764
  setAuthResult(span, "unauthorized");
1709
- endSpanError(span, error instanceof Error ? error : String(error));
1765
+ endFailedSpan(span, error);
1710
1766
  } else {
1711
1767
  setAuthResult(span, "authorized");
1712
1768
  endSpanOk(span);
1713
1769
  }
1714
1770
  }
1715
1771
  function wrapContextFetch(options, flowCtx) {
1716
- if (options.fetchSpans === false) return;
1717
1772
  const toolCtx = flowCtx.state?.toolContext ?? flowCtx.state?.toolCallContext;
1718
1773
  if (!toolCtx || typeof toolCtx.fetch !== "function") return;
1719
1774
  const originalFetch = toolCtx.fetch.bind(toolCtx);
1720
1775
  const parentOTelCtx = flowCtx.state?.[SPAN_CTX_KEY];
1721
1776
  toolCtx.fetch = async (input, init) => {
1722
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
1723
- const method = init?.method ?? "GET";
1724
- const tracer = getTracer();
1725
- const { span } = startFetchSpan(tracer, { method, url, parentContext: parentOTelCtx });
1777
+ const fetchSpan = options.fetchSpans === false ? void 0 : startFetchSpan(getTracer(), {
1778
+ method: init?.method ?? (input instanceof Request ? input.method : "GET"),
1779
+ url: typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url,
1780
+ parentContext: parentOTelCtx
1781
+ }).span;
1782
+ const callingSpan = fetchSpan ?? flowCtx.state?.[EXEC_SPAN_KEY] ?? flowCtx.state?.[SPAN_KEY];
1783
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
1784
+ if (callingSpan && !headers.has("traceparent")) headers.set("traceparent", traceparentOf(callingSpan));
1726
1785
  try {
1727
- const response = await originalFetch(input, init);
1728
- setFetchResponseStatus(span, response.status);
1729
- endSpanOk(span);
1786
+ const response = await originalFetch(input, { ...init, headers });
1787
+ if (fetchSpan) {
1788
+ setFetchResponseStatus(fetchSpan, response.status);
1789
+ endSpanOk(fetchSpan);
1790
+ }
1730
1791
  return response;
1731
1792
  } catch (err) {
1732
- endSpanError(span, err instanceof Error ? err : String(err));
1793
+ if (fetchSpan) endSpanError(fetchSpan, err instanceof Error ? err : String(err));
1733
1794
  throw err;
1734
1795
  }
1735
1796
  };
@@ -1747,13 +1808,94 @@ function onAgentDidExecuteEnrich(flowCtx) {
1747
1808
  }
1748
1809
  }
1749
1810
  }
1811
+ var HOOK_OBSERVER_KEY = /* @__PURE__ */ Symbol.for("frontmcp:hook-observer");
1812
+ function installHookObserver(flowCtx, observer) {
1813
+ const ctx = extractContext(flowCtx);
1814
+ if (ctx && !ctx.get?.(HOOK_OBSERVER_KEY)) ctx.set?.(HOOK_OBSERVER_KEY, observer);
1815
+ }
1816
+ async function runInHookSpan(flowCtx, hook, run) {
1817
+ const ctx = extractContext(flowCtx);
1818
+ const { span } = startHookSpan(getTracer(), {
1819
+ ...hook,
1820
+ parentContext: flowCtx.state?.[SPAN_CTX_KEY] ?? (ctx && parentContextOf(ctx))
1821
+ });
1822
+ try {
1823
+ await run();
1824
+ endSpanOk(span);
1825
+ } catch (error) {
1826
+ if (error instanceof import_sdk2.FlowControl && error.type !== "fail" && error.type !== "abort") endSpanOk(span);
1827
+ else endFailedSpan(span, error);
1828
+ throw error;
1829
+ }
1830
+ }
1750
1831
  function reportStartup(data) {
1751
1832
  const tracer = getTracer();
1752
1833
  emitStartupReport(tracer, data);
1753
1834
  }
1754
1835
 
1836
+ // libs/observability/src/request-log/request-log.tokens.ts
1837
+ var REQUEST_LOG_COLLECTOR = /* @__PURE__ */ Symbol.for("frontmcp:observability:request-log-collector");
1838
+
1839
+ // libs/observability/src/request-log/request-log.hooks.ts
1840
+ var FRONTMCP_CONTEXT = /* @__PURE__ */ Symbol.for("frontmcp:CONTEXT");
1841
+ function resolve(flowCtx, token) {
1842
+ try {
1843
+ return flowCtx.get?.(token);
1844
+ } catch {
1845
+ return void 0;
1846
+ }
1847
+ }
1848
+ function currentRequestLog(flowCtx) {
1849
+ return resolve(flowCtx, FRONTMCP_CONTEXT)?.get(REQUEST_LOG_COLLECTOR);
1850
+ }
1851
+ function startRequestLog(flowCtx, options) {
1852
+ const context = resolve(flowCtx, FRONTMCP_CONTEXT);
1853
+ if (!context) return;
1854
+ const collector = new RequestLogCollector(
1855
+ {
1856
+ requestId: context.requestId,
1857
+ traceId: context.traceContext.traceId,
1858
+ sessionIdHash: sessionTracingId(context.sessionId).slice(0, 12),
1859
+ scopeId: context.scopeId
1860
+ },
1861
+ options
1862
+ );
1863
+ context.set(REQUEST_LOG_COLLECTOR, collector);
1864
+ const request = flowCtx.rawInput?.request;
1865
+ if (request?.method && request.path) collector.setHttpInfo(request.method, request.path);
1866
+ const rpcMethod = request?.body?.method;
1867
+ if (typeof rpcMethod === "string") collector.setRpcMethod(rpcMethod);
1868
+ }
1869
+ function recordRequestLogFailure(flowCtx) {
1870
+ const flowError = flowCtx.state?.["flowError"];
1871
+ if (!flowError) return;
1872
+ const { type, message, code, errorId } = flowFailureOf(flowError);
1873
+ currentRequestLog(flowCtx)?.setError({ type, message, code, error_id: errorId });
1874
+ }
1875
+ function recordRequestLogAuth(flowCtx) {
1876
+ const verifyResult = flowCtx.state?.["verifyResult"];
1877
+ const authorization = flowCtx.rawInput?.request?.headers?.["authorization"];
1878
+ const scheme = typeof authorization === "string" ? authorization.trim().split(/\s+/)[0].toLowerCase() : "";
1879
+ const sub = verifyResult?.authorization?.user?.sub;
1880
+ const anonymous = typeof sub !== "string" || (0, import_sdk3.isAnonymousSubject)(sub);
1881
+ currentRequestLog(flowCtx)?.setAuthInfo(
1882
+ anonymous || !scheme ? "anonymous" : scheme,
1883
+ verifyResult?.kind === "authorized" && !anonymous
1884
+ );
1885
+ }
1886
+ async function completeRequestLog(flowCtx) {
1887
+ const collector = currentRequestLog(flowCtx);
1888
+ if (!collector || collector.isFinalized()) return;
1889
+ const statusCode = flowCtx.state?.["statusCode"] ?? flowCtx.rawInput?.response?.statusCode;
1890
+ if (typeof statusCode === "number") collector.setStatusCode(statusCode);
1891
+ await collector.finalize();
1892
+ }
1893
+
1894
+ // libs/observability/src/telemetry/telemetry.accessor.ts
1895
+ var import_api15 = require("@opentelemetry/api");
1896
+
1755
1897
  // libs/observability/src/telemetry/telemetry.counters.ts
1756
- var import_api13 = require("@opentelemetry/api");
1898
+ var import_api14 = require("@opentelemetry/api");
1757
1899
  function snapshotKey(name, attributes) {
1758
1900
  const keys = Object.keys(attributes).sort();
1759
1901
  if (keys.length === 0) return name;
@@ -1788,7 +1930,7 @@ var counterCache = /* @__PURE__ */ new Map();
1788
1930
  function createCounter(name, description) {
1789
1931
  const existing = counterCache.get(name);
1790
1932
  if (existing) return existing;
1791
- const meter = import_api13.metrics.getMeter(METER_NAME);
1933
+ const meter = import_api14.metrics.getMeter(METER_NAME);
1792
1934
  const otelCounter = meter.createCounter(name, description ? { description } : void 0);
1793
1935
  const counter = new TelemetryCounterImpl(name, otelCounter);
1794
1936
  counterCache.set(name, counter);
@@ -1867,14 +2009,14 @@ var TelemetrySpan = class {
1867
2009
  /** Record an error on this span */
1868
2010
  recordError(error) {
1869
2011
  this.span.recordException(error);
1870
- this.span.setStatus({ code: import_api14.SpanStatusCode.ERROR, message: error.message });
2012
+ this.span.setStatus({ code: import_api15.SpanStatusCode.ERROR, message: error.message });
1871
2013
  this.hasError = true;
1872
2014
  return this;
1873
2015
  }
1874
2016
  /** End the span (preserves ERROR status if already set) */
1875
2017
  end() {
1876
2018
  if (!this.hasError) {
1877
- this.span.setStatus({ code: import_api14.SpanStatusCode.OK });
2019
+ this.span.setStatus({ code: import_api15.SpanStatusCode.OK });
1878
2020
  }
1879
2021
  this.span.end();
1880
2022
  }
@@ -1882,7 +2024,7 @@ var TelemetrySpan = class {
1882
2024
  endWithError(error) {
1883
2025
  const msg = typeof error === "string" ? error : error.message;
1884
2026
  if (error instanceof Error) this.span.recordException(error);
1885
- this.span.setStatus({ code: import_api14.SpanStatusCode.ERROR, message: msg });
2027
+ this.span.setStatus({ code: import_api15.SpanStatusCode.ERROR, message: msg });
1886
2028
  this.span.end();
1887
2029
  }
1888
2030
  /** Get the underlying OTel span (escape hatch) */
@@ -1893,7 +2035,7 @@ var TelemetrySpan = class {
1893
2035
  var TelemetryAccessor = class {
1894
2036
  constructor(ctx) {
1895
2037
  this.ctx = ctx;
1896
- this.tracer = import_api14.trace.getTracer("@frontmcp/observability");
2038
+ this.tracer = import_api15.trace.getTracer("@frontmcp/observability");
1897
2039
  this.fallbackContext = createOTelContextFromTrace(ctx.traceContext);
1898
2040
  this.sessionHash = sessionTracingId(ctx.sessionId);
1899
2041
  this.baseAttributes = {
@@ -1929,7 +2071,7 @@ var TelemetryAccessor = class {
1929
2071
  const span = this.tracer.startSpan(
1930
2072
  name,
1931
2073
  {
1932
- kind: import_api14.SpanKind.INTERNAL,
2074
+ kind: import_api15.SpanKind.INTERNAL,
1933
2075
  attributes: { ...this.baseAttributes, ...attributes }
1934
2076
  },
1935
2077
  this.getActiveContext()
@@ -1978,11 +2120,11 @@ var TelemetryAccessor = class {
1978
2120
  } else {
1979
2121
  const span = this.tracer.startSpan(
1980
2122
  name,
1981
- { kind: import_api14.SpanKind.INTERNAL, attributes: this.baseAttributes },
2123
+ { kind: import_api15.SpanKind.INTERNAL, attributes: this.baseAttributes },
1982
2124
  this.fallbackContext
1983
2125
  );
1984
2126
  if (attributes) span.addEvent(name, attributes);
1985
- span.setStatus({ code: import_api14.SpanStatusCode.OK });
2127
+ span.setStatus({ code: import_api15.SpanStatusCode.OK });
1986
2128
  span.end();
1987
2129
  }
1988
2130
  }
@@ -2019,7 +2161,7 @@ var TelemetryAccessor = class {
2019
2161
  * Useful for including in external API calls or logs.
2020
2162
  */
2021
2163
  get traceId() {
2022
- return import_api14.trace.getSpan(this.fallbackContext)?.spanContext().traceId ?? "";
2164
+ return import_api15.trace.getSpan(this.fallbackContext)?.spanContext().traceId ?? "";
2023
2165
  }
2024
2166
  /**
2025
2167
  * Get the session tracing ID (privacy-safe hash).
@@ -2030,10 +2172,10 @@ var TelemetryAccessor = class {
2030
2172
  };
2031
2173
 
2032
2174
  // libs/observability/src/telemetry/telemetry.factory.ts
2033
- var import_api15 = require("@opentelemetry/api");
2175
+ var import_api16 = require("@opentelemetry/api");
2034
2176
  var TelemetryFactory = class {
2035
2177
  constructor(tracerName = "@frontmcp/observability") {
2036
- this.tracer = import_api15.trace.getTracer(tracerName);
2178
+ this.tracer = import_api16.trace.getTracer(tracerName);
2037
2179
  }
2038
2180
  /**
2039
2181
  * Create (or retrieve cached) a named counter. Increments propagate to
@@ -2080,32 +2222,32 @@ var TELEMETRY_ACCESSOR = /* @__PURE__ */ Symbol.for("frontmcp:observability:tele
2080
2222
  var TELEMETRY_FACTORY = /* @__PURE__ */ Symbol.for("frontmcp:observability:telemetry-factory");
2081
2223
 
2082
2224
  // libs/observability/src/plugin/observability.plugin.ts
2083
- var PromptHook = (0, import_sdk2.FlowHooksOf)("prompts:get-prompt");
2084
- var ListPromptsHook = (0, import_sdk2.FlowHooksOf)("prompts:list-prompts");
2085
- var SkillSearchHook = (0, import_sdk2.FlowHooksOf)("skills:search");
2086
- var SkillLoadHook = (0, import_sdk2.FlowHooksOf)("skills:load");
2087
- var CompletionHook = (0, import_sdk2.FlowHooksOf)("completion:complete");
2088
- var LegacySseHook = (0, import_sdk2.FlowHooksOf)("handle:legacy-sse");
2089
- var StreamableHttpHook = (0, import_sdk2.FlowHooksOf)("handle:streamable-http");
2090
- var StatelessHttpHook = (0, import_sdk2.FlowHooksOf)("handle:stateless-http");
2091
- var AuthVerifyHook = (0, import_sdk2.FlowHooksOf)("auth:verify");
2092
- var SessionVerifyHook = (0, import_sdk2.FlowHooksOf)("session:verify");
2093
- var OAuthTokenHook = (0, import_sdk2.FlowHooksOf)("oauth:token");
2094
- var OAuthAuthorizeHook = (0, import_sdk2.FlowHooksOf)("oauth:authorize");
2095
- var OAuthCallbackHook = (0, import_sdk2.FlowHooksOf)("oauth:callback");
2096
- var OAuthProviderCallbackHook = (0, import_sdk2.FlowHooksOf)("oauth:provider-callback");
2097
- var OAuthRegisterHook = (0, import_sdk2.FlowHooksOf)("oauth:register");
2098
- var ElicitRequestHook = (0, import_sdk2.FlowHooksOf)("elicitation:request");
2099
- var ElicitResultHook = (0, import_sdk2.FlowHooksOf)("elicitation:result");
2100
- var SubscribeResourceHook = (0, import_sdk2.FlowHooksOf)("resources:subscribe");
2101
- var UnsubscribeResourceHook = (0, import_sdk2.FlowHooksOf)("resources:unsubscribe");
2102
- var SkillsHttpLlmTxtHook = (0, import_sdk2.FlowHooksOf)("skills-http:llm-txt");
2103
- var SkillsHttpLlmFullTxtHook = (0, import_sdk2.FlowHooksOf)("skills-http:llm-full-txt");
2104
- var SkillsHttpApiHook = (0, import_sdk2.FlowHooksOf)("skills-http:api");
2105
- var WellKnownJwksHook = (0, import_sdk2.FlowHooksOf)("well-known.jwks");
2106
- var WellKnownOAuthServerHook = (0, import_sdk2.FlowHooksOf)("well-known.oauth-authorization-server");
2107
- var WellKnownPrmHook = (0, import_sdk2.FlowHooksOf)("well-known.prm");
2108
- var LoggingSetLevelHook = (0, import_sdk2.FlowHooksOf)("logging:set-level");
2225
+ var PromptHook = (0, import_sdk4.FlowHooksOf)("prompts:get-prompt");
2226
+ var ListPromptsHook = (0, import_sdk4.FlowHooksOf)("prompts:list-prompts");
2227
+ var SkillSearchHook = (0, import_sdk4.FlowHooksOf)("skills:search");
2228
+ var SkillLoadHook = (0, import_sdk4.FlowHooksOf)("skills:load");
2229
+ var CompletionHook = (0, import_sdk4.FlowHooksOf)("completion:complete");
2230
+ var LegacySseHook = (0, import_sdk4.FlowHooksOf)("handle:legacy-sse");
2231
+ var StreamableHttpHook = (0, import_sdk4.FlowHooksOf)("handle:streamable-http");
2232
+ var StatelessHttpHook = (0, import_sdk4.FlowHooksOf)("handle:stateless-http");
2233
+ var AuthVerifyHook = (0, import_sdk4.FlowHooksOf)("auth:verify");
2234
+ var SessionVerifyHook = (0, import_sdk4.FlowHooksOf)("session:verify");
2235
+ var OAuthTokenHook = (0, import_sdk4.FlowHooksOf)("oauth:token");
2236
+ var OAuthAuthorizeHook = (0, import_sdk4.FlowHooksOf)("oauth:authorize");
2237
+ var OAuthCallbackHook = (0, import_sdk4.FlowHooksOf)("oauth:callback");
2238
+ var OAuthProviderCallbackHook = (0, import_sdk4.FlowHooksOf)("oauth:provider-callback");
2239
+ var OAuthRegisterHook = (0, import_sdk4.FlowHooksOf)("oauth:register");
2240
+ var ElicitRequestHook = (0, import_sdk4.FlowHooksOf)("elicitation:request");
2241
+ var ElicitResultHook = (0, import_sdk4.FlowHooksOf)("elicitation:result");
2242
+ var SubscribeResourceHook = (0, import_sdk4.FlowHooksOf)("resources:subscribe");
2243
+ var UnsubscribeResourceHook = (0, import_sdk4.FlowHooksOf)("resources:unsubscribe");
2244
+ var SkillsHttpLlmTxtHook = (0, import_sdk4.FlowHooksOf)("skills-http:llm-txt");
2245
+ var SkillsHttpLlmFullTxtHook = (0, import_sdk4.FlowHooksOf)("skills-http:llm-full-txt");
2246
+ var SkillsHttpApiHook = (0, import_sdk4.FlowHooksOf)("skills-http:api");
2247
+ var WellKnownJwksHook = (0, import_sdk4.FlowHooksOf)("well-known.jwks");
2248
+ var WellKnownOAuthServerHook = (0, import_sdk4.FlowHooksOf)("well-known.oauth-authorization-server");
2249
+ var WellKnownPrmHook = (0, import_sdk4.FlowHooksOf)("well-known.prm");
2250
+ var LoggingSetLevelHook = (0, import_sdk4.FlowHooksOf)("logging:set-level");
2109
2251
  var DEFAULT_TRACING = {
2110
2252
  httpSpans: true,
2111
2253
  executionSpans: true,
@@ -2118,17 +2260,46 @@ var DEFAULT_TRACING = {
2118
2260
  elicitationSpans: true,
2119
2261
  startupReport: true
2120
2262
  };
2121
- var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2263
+ var ObservabilityPlugin = class extends import_sdk4.DynamicPlugin {
2122
2264
  constructor(options = {}) {
2123
2265
  super();
2266
+ /** Wraps each other hook the request runs: names it on the request's log, and gives it a span with `hookSpans`. */
2267
+ this.observeHook = async ({ flowName, stage, target, flowContext }, run) => {
2268
+ if (target === this) return run();
2269
+ if (this.requestLogsEnabled) currentRequestLog(flowContext)?.addHook(`${flowName}:${stage}`);
2270
+ if (!this.traces("hookSpans")) return run();
2271
+ const owner = target?.constructor?.name;
2272
+ return runInHookSpan(flowContext, { flowName, stage, owner }, run);
2273
+ };
2124
2274
  this.options = resolveOptions(options);
2125
2275
  this.tracingOpts = this.options.tracing !== false ? this.options.tracing : DEFAULT_TRACING;
2126
2276
  }
2127
2277
  get tracingEnabled() {
2128
2278
  return this.options.tracing !== false;
2129
2279
  }
2280
+ get requestLogsEnabled() {
2281
+ return this.options.requestLogs !== false;
2282
+ }
2283
+ /** Whether tracing is on and its `kind` option isn't off. */
2284
+ traces(kind) {
2285
+ return this.tracingEnabled && this.tracingOpts[kind] !== false;
2286
+ }
2287
+ /** Watches the hooks of the request `ctx` serves, from the first flow of it that this plugin sees. */
2288
+ watchHooks(ctx) {
2289
+ if (this.requestLogsEnabled || this.traces("hookSpans"))
2290
+ installHookObserver(ctx, this.observeHook);
2291
+ }
2292
+ /** Names the entry a request serves on its request log, and records the entry flow's failure. */
2293
+ annotateRequestLog(ctx, set) {
2294
+ if (!this.requestLogsEnabled) return;
2295
+ const flowCtx = ctx;
2296
+ const log = currentRequestLog(ctx);
2297
+ if (log && flowCtx.state?.input) set(log, flowCtx.state.input);
2298
+ }
2130
2299
  _httpWillTrace(ctx) {
2131
2300
  if (this.tracingEnabled) onHttpWillTrace(this.tracingOpts, ctx);
2301
+ if (this.options.requestLogs !== false) startRequestLog(ctx, this.options.requestLogs);
2302
+ this.watchHooks(ctx);
2132
2303
  }
2133
2304
  _httpWillQuota(ctx) {
2134
2305
  if (this.tracingEnabled) onHttpWillAcquireQuota(ctx);
@@ -2141,6 +2312,7 @@ var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2141
2312
  }
2142
2313
  _httpDidAuth(ctx) {
2143
2314
  if (this.tracingEnabled) onHttpDidCheckAuth(ctx);
2315
+ if (this.requestLogsEnabled) recordRequestLogAuth(ctx);
2144
2316
  }
2145
2317
  _httpWillRoute(ctx) {
2146
2318
  if (this.tracingEnabled) onHttpWillRoute(ctx);
@@ -2150,12 +2322,15 @@ var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2150
2322
  }
2151
2323
  _httpDidFinalize(ctx) {
2152
2324
  if (this.tracingEnabled) onHttpDidFinalize(ctx);
2325
+ if (this.requestLogsEnabled) void completeRequestLog(ctx);
2153
2326
  }
2154
2327
  _toolWillParse(ctx) {
2328
+ this.watchHooks(ctx);
2155
2329
  if (this.tracingEnabled) onToolWillParse(this.tracingOpts, ctx);
2156
2330
  }
2157
2331
  _toolWillFind(ctx) {
2158
2332
  if (this.tracingEnabled) onToolWillFindTool(ctx);
2333
+ this.annotateRequestLog(ctx, (log, input) => log.setToolName(String(input["name"])));
2159
2334
  }
2160
2335
  _toolWillCheckAuth(ctx) {
2161
2336
  if (this.tracingEnabled) onToolWillCheckAuth(ctx);
@@ -2186,12 +2361,15 @@ var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2186
2361
  }
2187
2362
  _toolDidFinalize(ctx) {
2188
2363
  if (this.tracingEnabled) onToolDidFinalize(ctx);
2364
+ if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
2189
2365
  }
2190
2366
  _resourceWillParse(ctx) {
2367
+ this.watchHooks(ctx);
2191
2368
  if (this.tracingEnabled) onResourceWillParse(this.tracingOpts, ctx);
2192
2369
  }
2193
2370
  _resourceWillFind(ctx) {
2194
2371
  if (this.tracingEnabled) onResourceWillFind(ctx);
2372
+ this.annotateRequestLog(ctx, (log, input) => log.setResourceUri(String(input["uri"])));
2195
2373
  }
2196
2374
  _resourceWillCheckAuthorities(ctx) {
2197
2375
  if (this.tracingEnabled) onEntryWillCheckAuthorities(ctx);
@@ -2207,12 +2385,15 @@ var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2207
2385
  }
2208
2386
  _resourceDidFinalize(ctx) {
2209
2387
  if (this.tracingEnabled) onResourceDidFinalize(ctx);
2388
+ if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
2210
2389
  }
2211
2390
  _promptWillParse(ctx) {
2391
+ this.watchHooks(ctx);
2212
2392
  if (this.tracingEnabled) onPromptWillParse(this.tracingOpts, ctx);
2213
2393
  }
2214
2394
  _promptWillFind(ctx) {
2215
2395
  if (this.tracingEnabled) onPromptWillFind(ctx);
2396
+ this.annotateRequestLog(ctx, (log, input) => log.setPromptName(String(input["name"])));
2216
2397
  }
2217
2398
  _promptWillCheckAuthorities(ctx) {
2218
2399
  if (this.tracingEnabled) onEntryWillCheckAuthorities(ctx);
@@ -2228,8 +2409,10 @@ var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2228
2409
  }
2229
2410
  _promptDidFinalize(ctx) {
2230
2411
  if (this.tracingEnabled) onPromptDidFinalize(ctx);
2412
+ if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
2231
2413
  }
2232
2414
  _agentWillParse(ctx) {
2415
+ this.watchHooks(ctx);
2233
2416
  if (this.tracingEnabled) onAgentWillParse(this.tracingOpts, ctx);
2234
2417
  }
2235
2418
  _agentWillFind(ctx) {
@@ -2383,46 +2566,46 @@ var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2383
2566
  if (this.tracingEnabled) onAuthDidFinalize(ctx);
2384
2567
  }
2385
2568
  _oauthTokenWill(ctx) {
2386
- if (this.tracingEnabled) onGenericFlowWillStart("oauth/token", this.tracingOpts, ctx);
2569
+ if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/token", this.tracingOpts, ctx);
2387
2570
  }
2388
2571
  _oauthTokenDone(ctx) {
2389
- if (this.tracingEnabled) onGenericFlowDidFinalize(ctx);
2572
+ if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
2390
2573
  }
2391
2574
  _oauthAuthzWill(ctx) {
2392
- if (this.tracingEnabled) onGenericFlowWillStart("oauth/authorize", this.tracingOpts, ctx);
2575
+ if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/authorize", this.tracingOpts, ctx);
2393
2576
  }
2394
2577
  _oauthAuthzDone(ctx) {
2395
- if (this.tracingEnabled) onGenericFlowDidFinalize(ctx);
2578
+ if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
2396
2579
  }
2397
2580
  _oauthCallbackWill(ctx) {
2398
- if (this.tracingEnabled) onGenericFlowWillStart("oauth/callback", this.tracingOpts, ctx);
2581
+ if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/callback", this.tracingOpts, ctx);
2399
2582
  }
2400
2583
  _oauthCallbackDone(ctx) {
2401
- if (this.tracingEnabled) onGenericFlowDidFinalize(ctx);
2584
+ if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
2402
2585
  }
2403
2586
  _oauthProviderWill(ctx) {
2404
- if (this.tracingEnabled) onGenericFlowWillStart("oauth/provider-callback", this.tracingOpts, ctx);
2587
+ if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/provider-callback", this.tracingOpts, ctx);
2405
2588
  }
2406
2589
  _oauthProviderDone(ctx) {
2407
- if (this.tracingEnabled) onGenericFlowDidFinalize(ctx);
2590
+ if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
2408
2591
  }
2409
2592
  _oauthRegisterWill(ctx) {
2410
- if (this.tracingEnabled) onGenericFlowWillStart("oauth/register", this.tracingOpts, ctx);
2593
+ if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/register", this.tracingOpts, ctx);
2411
2594
  }
2412
2595
  _oauthRegisterDone(ctx) {
2413
- if (this.tracingEnabled) onGenericFlowDidFinalize(ctx);
2596
+ if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
2414
2597
  }
2415
2598
  _elicitReqWill(ctx) {
2416
- if (this.tracingEnabled) onGenericFlowWillStart("elicitation/request", this.tracingOpts, ctx);
2599
+ if (this.traces("elicitationSpans")) onGenericFlowWillStart("elicitation/request", this.tracingOpts, ctx);
2417
2600
  }
2418
2601
  _elicitReqDone(ctx) {
2419
- if (this.tracingEnabled) onGenericFlowDidFinalize(ctx);
2602
+ if (this.traces("elicitationSpans")) onGenericFlowDidFinalize(ctx);
2420
2603
  }
2421
2604
  _elicitResWill(ctx) {
2422
- if (this.tracingEnabled) onGenericFlowWillStart("elicitation/result", this.tracingOpts, ctx);
2605
+ if (this.traces("elicitationSpans")) onGenericFlowWillStart("elicitation/result", this.tracingOpts, ctx);
2423
2606
  }
2424
2607
  _elicitResDone(ctx) {
2425
- if (this.tracingEnabled) onGenericFlowDidFinalize(ctx);
2608
+ if (this.traces("elicitationSpans")) onGenericFlowDidFinalize(ctx);
2426
2609
  }
2427
2610
  _subscribeWill(ctx) {
2428
2611
  if (this.tracingEnabled) onGenericFlowWillStart("resources/subscribe", this.tracingOpts, ctx);
@@ -2488,12 +2671,10 @@ var ObservabilityPlugin = class extends import_sdk2.DynamicPlugin {
2488
2671
  // Startup Report (public method)
2489
2672
  // ═══════════════════════════════════════════════════════════════════════════
2490
2673
  /**
2491
- * Emit startup telemetry report.
2492
- * Call after scope.initialize() completes with component counts.
2674
+ * Emit the `frontmcp.startup` span (`tracing.startupReport`). The scope calls it once it is ready.
2493
2675
  */
2494
2676
  reportStartupTelemetry(data) {
2495
- if (this.options.tracing === false) return;
2496
- reportStartup(data);
2677
+ if (this.traces("startupReport")) reportStartup(data);
2497
2678
  }
2498
2679
  };
2499
2680
  ObservabilityPlugin.defaultOptions = {
@@ -2511,7 +2692,7 @@ ObservabilityPlugin.dynamicProviders = (input) => {
2511
2692
  providers.push({
2512
2693
  name: "observability:tracer",
2513
2694
  provide: OTEL_TRACER,
2514
- useValue: import_api16.trace.getTracer("@frontmcp/observability")
2695
+ useValue: import_api17.trace.getTracer("@frontmcp/observability")
2515
2696
  });
2516
2697
  providers.push({
2517
2698
  name: "observability:config",
@@ -2521,8 +2702,8 @@ ObservabilityPlugin.dynamicProviders = (input) => {
2521
2702
  providers.push({
2522
2703
  name: "observability:telemetry-accessor",
2523
2704
  provide: TELEMETRY_ACCESSOR,
2524
- scope: import_sdk2.ProviderScope.CONTEXT,
2525
- inject: () => [import_sdk2.FRONTMCP_CONTEXT],
2705
+ scope: import_sdk4.ProviderScope.CONTEXT,
2706
+ inject: () => [import_sdk4.FRONTMCP_CONTEXT],
2526
2707
  useFactory: (ctx) => new TelemetryAccessor(ctx)
2527
2708
  });
2528
2709
  providers.push({
@@ -2554,8 +2735,8 @@ ObservabilityPlugin.dynamicProviders = (input) => {
2554
2735
  providers.push({
2555
2736
  name: "observability:request-log-collector",
2556
2737
  provide: REQUEST_LOG_COLLECTOR,
2557
- scope: import_sdk2.ProviderScope.CONTEXT,
2558
- inject: () => [import_sdk2.FRONTMCP_CONTEXT],
2738
+ scope: import_sdk4.ProviderScope.CONTEXT,
2739
+ inject: () => [import_sdk4.FRONTMCP_CONTEXT],
2559
2740
  useFactory: (ctx) => {
2560
2741
  return new RequestLogCollector(
2561
2742
  {
@@ -2572,85 +2753,85 @@ ObservabilityPlugin.dynamicProviders = (input) => {
2572
2753
  return providers;
2573
2754
  };
2574
2755
  __decorateClass([
2575
- import_sdk2.HttpHook.Will("traceRequest", { priority: -1e3 })
2756
+ import_sdk4.HttpHook.Will("traceRequest", { priority: -1e3 })
2576
2757
  ], ObservabilityPlugin.prototype, "_httpWillTrace", 1);
2577
2758
  __decorateClass([
2578
- import_sdk2.HttpHook.Will("acquireQuota", { priority: -1e3 })
2759
+ import_sdk4.HttpHook.Will("acquireQuota", { priority: -1e3 })
2579
2760
  ], ObservabilityPlugin.prototype, "_httpWillQuota", 1);
2580
2761
  __decorateClass([
2581
- import_sdk2.HttpHook.Did("acquireSemaphore", { priority: 1e3 })
2762
+ import_sdk4.HttpHook.Did("acquireSemaphore", { priority: 1e3 })
2582
2763
  ], ObservabilityPlugin.prototype, "_httpDidSemaphore", 1);
2583
2764
  __decorateClass([
2584
- import_sdk2.HttpHook.Will("checkAuthorization", { priority: -1e3 })
2765
+ import_sdk4.HttpHook.Will("checkAuthorization", { priority: -1e3 })
2585
2766
  ], ObservabilityPlugin.prototype, "_httpWillAuth", 1);
2586
2767
  __decorateClass([
2587
- import_sdk2.HttpHook.Did("checkAuthorization", { priority: 1e3 })
2768
+ import_sdk4.HttpHook.Did("checkAuthorization", { priority: 1e3 })
2588
2769
  ], ObservabilityPlugin.prototype, "_httpDidAuth", 1);
2589
2770
  __decorateClass([
2590
- import_sdk2.HttpHook.Will("router", { priority: -1e3 })
2771
+ import_sdk4.HttpHook.Will("router", { priority: -1e3 })
2591
2772
  ], ObservabilityPlugin.prototype, "_httpWillRoute", 1);
2592
2773
  __decorateClass([
2593
- import_sdk2.HttpHook.Did("router", { priority: 1e3 })
2774
+ import_sdk4.HttpHook.Did("router", { priority: 1e3 })
2594
2775
  ], ObservabilityPlugin.prototype, "_httpDidRoute", 1);
2595
2776
  __decorateClass([
2596
- import_sdk2.HttpHook.Did("finalize", { priority: 1e3 })
2777
+ import_sdk4.HttpHook.Did("finalize", { priority: 1e3 })
2597
2778
  ], ObservabilityPlugin.prototype, "_httpDidFinalize", 1);
2598
2779
  __decorateClass([
2599
- import_sdk2.ToolHook.Will("parseInput", { priority: -1e3 })
2780
+ import_sdk4.ToolHook.Will("parseInput", { priority: -1e3 })
2600
2781
  ], ObservabilityPlugin.prototype, "_toolWillParse", 1);
2601
2782
  __decorateClass([
2602
- import_sdk2.ToolHook.Will("findTool", { priority: -1e3 })
2783
+ import_sdk4.ToolHook.Will("findTool", { priority: -1e3 })
2603
2784
  ], ObservabilityPlugin.prototype, "_toolWillFind", 1);
2604
2785
  __decorateClass([
2605
- import_sdk2.ToolHook.Will("checkToolAuthorization", { priority: -1e3 })
2786
+ import_sdk4.ToolHook.Will("checkToolAuthorization", { priority: -1e3 })
2606
2787
  ], ObservabilityPlugin.prototype, "_toolWillCheckAuth", 1);
2607
2788
  __decorateClass([
2608
- import_sdk2.ToolHook.Will("checkEntryAuthorities", { priority: -1e3 })
2789
+ import_sdk4.ToolHook.Will("checkEntryAuthorities", { priority: -1e3 })
2609
2790
  ], ObservabilityPlugin.prototype, "_toolWillCheckAuthorities", 1);
2610
2791
  __decorateClass([
2611
- import_sdk2.ToolHook.Did("checkEntryAuthorities", { priority: 1e3 })
2792
+ import_sdk4.ToolHook.Did("checkEntryAuthorities", { priority: 1e3 })
2612
2793
  ], ObservabilityPlugin.prototype, "_toolDidCheckAuthorities", 1);
2613
2794
  __decorateClass([
2614
- import_sdk2.ToolHook.Will("createToolCallContext", { priority: -1e3 })
2795
+ import_sdk4.ToolHook.Will("createToolCallContext", { priority: -1e3 })
2615
2796
  ], ObservabilityPlugin.prototype, "_toolWillCreateCtx", 1);
2616
2797
  __decorateClass([
2617
- import_sdk2.ToolHook.Will("validateInput", { priority: -1e3 })
2798
+ import_sdk4.ToolHook.Will("validateInput", { priority: -1e3 })
2618
2799
  ], ObservabilityPlugin.prototype, "_toolWillValidate", 1);
2619
2800
  __decorateClass([
2620
- import_sdk2.ToolHook.Will("execute", { priority: -1e3 })
2801
+ import_sdk4.ToolHook.Will("execute", { priority: -1e3 })
2621
2802
  ], ObservabilityPlugin.prototype, "_toolWillExecute", 1);
2622
2803
  __decorateClass([
2623
- import_sdk2.ToolHook.Did("execute", { priority: 1e3 })
2804
+ import_sdk4.ToolHook.Did("execute", { priority: 1e3 })
2624
2805
  ], ObservabilityPlugin.prototype, "_toolDidExecute", 1);
2625
2806
  __decorateClass([
2626
- import_sdk2.ToolHook.Will("validateOutput", { priority: -1e3 })
2807
+ import_sdk4.ToolHook.Will("validateOutput", { priority: -1e3 })
2627
2808
  ], ObservabilityPlugin.prototype, "_toolWillValidateOut", 1);
2628
2809
  __decorateClass([
2629
- import_sdk2.ToolHook.Will("applyUI", { priority: -1e3 })
2810
+ import_sdk4.ToolHook.Will("applyUI", { priority: -1e3 })
2630
2811
  ], ObservabilityPlugin.prototype, "_toolWillApplyUI", 1);
2631
2812
  __decorateClass([
2632
- import_sdk2.ToolHook.Did("finalize", { priority: 1e3 })
2813
+ import_sdk4.ToolHook.Did("finalize", { priority: 1e3 })
2633
2814
  ], ObservabilityPlugin.prototype, "_toolDidFinalize", 1);
2634
2815
  __decorateClass([
2635
- import_sdk2.ResourceHook.Will("parseInput", { priority: -1e3 })
2816
+ import_sdk4.ResourceHook.Will("parseInput", { priority: -1e3 })
2636
2817
  ], ObservabilityPlugin.prototype, "_resourceWillParse", 1);
2637
2818
  __decorateClass([
2638
- import_sdk2.ResourceHook.Will("findResource", { priority: -1e3 })
2819
+ import_sdk4.ResourceHook.Will("findResource", { priority: -1e3 })
2639
2820
  ], ObservabilityPlugin.prototype, "_resourceWillFind", 1);
2640
2821
  __decorateClass([
2641
- import_sdk2.ResourceHook.Will("checkEntryAuthorities", { priority: -1e3 })
2822
+ import_sdk4.ResourceHook.Will("checkEntryAuthorities", { priority: -1e3 })
2642
2823
  ], ObservabilityPlugin.prototype, "_resourceWillCheckAuthorities", 1);
2643
2824
  __decorateClass([
2644
- import_sdk2.ResourceHook.Did("checkEntryAuthorities", { priority: 1e3 })
2825
+ import_sdk4.ResourceHook.Did("checkEntryAuthorities", { priority: 1e3 })
2645
2826
  ], ObservabilityPlugin.prototype, "_resourceDidCheckAuthorities", 1);
2646
2827
  __decorateClass([
2647
- import_sdk2.ResourceHook.Will("execute", { priority: -1e3 })
2828
+ import_sdk4.ResourceHook.Will("execute", { priority: -1e3 })
2648
2829
  ], ObservabilityPlugin.prototype, "_resourceWillExecute", 1);
2649
2830
  __decorateClass([
2650
- import_sdk2.ResourceHook.Did("execute", { priority: 1e3 })
2831
+ import_sdk4.ResourceHook.Did("execute", { priority: 1e3 })
2651
2832
  ], ObservabilityPlugin.prototype, "_resourceDidExecute", 1);
2652
2833
  __decorateClass([
2653
- import_sdk2.ResourceHook.Did("finalize", { priority: 1e3 })
2834
+ import_sdk4.ResourceHook.Did("finalize", { priority: 1e3 })
2654
2835
  ], ObservabilityPlugin.prototype, "_resourceDidFinalize", 1);
2655
2836
  __decorateClass([
2656
2837
  PromptHook.Will("parseInput", { priority: -1e3 })
@@ -2674,55 +2855,55 @@ __decorateClass([
2674
2855
  PromptHook.Did("finalize", { priority: 1e3 })
2675
2856
  ], ObservabilityPlugin.prototype, "_promptDidFinalize", 1);
2676
2857
  __decorateClass([
2677
- import_sdk2.AgentCallHook.Will("parseInput", { priority: -1e3 })
2858
+ import_sdk4.AgentCallHook.Will("parseInput", { priority: -1e3 })
2678
2859
  ], ObservabilityPlugin.prototype, "_agentWillParse", 1);
2679
2860
  __decorateClass([
2680
- import_sdk2.AgentCallHook.Will("findAgent", { priority: -1e3 })
2861
+ import_sdk4.AgentCallHook.Will("findAgent", { priority: -1e3 })
2681
2862
  ], ObservabilityPlugin.prototype, "_agentWillFind", 1);
2682
2863
  __decorateClass([
2683
- import_sdk2.AgentCallHook.Will("checkEntryAuthorities", { priority: -1e3 })
2864
+ import_sdk4.AgentCallHook.Will("checkEntryAuthorities", { priority: -1e3 })
2684
2865
  ], ObservabilityPlugin.prototype, "_agentWillCheckAuthorities", 1);
2685
2866
  __decorateClass([
2686
- import_sdk2.AgentCallHook.Did("checkEntryAuthorities", { priority: 1e3 })
2867
+ import_sdk4.AgentCallHook.Did("checkEntryAuthorities", { priority: 1e3 })
2687
2868
  ], ObservabilityPlugin.prototype, "_agentDidCheckAuthorities", 1);
2688
2869
  __decorateClass([
2689
- import_sdk2.AgentCallHook.Will("execute", { priority: -1e3 })
2870
+ import_sdk4.AgentCallHook.Will("execute", { priority: -1e3 })
2690
2871
  ], ObservabilityPlugin.prototype, "_agentWillExecute", 1);
2691
2872
  __decorateClass([
2692
- import_sdk2.AgentCallHook.Did("execute", { priority: 1e3 })
2873
+ import_sdk4.AgentCallHook.Did("execute", { priority: 1e3 })
2693
2874
  ], ObservabilityPlugin.prototype, "_agentDidExecute", 1);
2694
2875
  __decorateClass([
2695
- import_sdk2.AgentCallHook.Did("finalize", { priority: 1e3 })
2876
+ import_sdk4.AgentCallHook.Did("finalize", { priority: 1e3 })
2696
2877
  ], ObservabilityPlugin.prototype, "_agentDidFinalize", 1);
2697
2878
  __decorateClass([
2698
- import_sdk2.ListToolsHook.Will("parseInput", { priority: -1e3 })
2879
+ import_sdk4.ListToolsHook.Will("parseInput", { priority: -1e3 })
2699
2880
  ], ObservabilityPlugin.prototype, "_listToolsWill", 1);
2700
2881
  __decorateClass([
2701
- import_sdk2.ListToolsHook.Did("findTools", { priority: 1e3 })
2882
+ import_sdk4.ListToolsHook.Did("findTools", { priority: 1e3 })
2702
2883
  ], ObservabilityPlugin.prototype, "_listToolsDidFind", 1);
2703
2884
  __decorateClass([
2704
- import_sdk2.ListToolsHook.Did("filterByAuthorities", { priority: 1e3 })
2885
+ import_sdk4.ListToolsHook.Did("filterByAuthorities", { priority: 1e3 })
2705
2886
  ], ObservabilityPlugin.prototype, "_listToolsDidFilter", 1);
2706
2887
  __decorateClass([
2707
- import_sdk2.ListToolsHook.Did("parseTools", { priority: 1e3 })
2888
+ import_sdk4.ListToolsHook.Did("parseTools", { priority: 1e3 })
2708
2889
  ], ObservabilityPlugin.prototype, "_listToolsDone", 1);
2709
2890
  __decorateClass([
2710
- import_sdk2.ListResourcesHook.Will("parseInput", { priority: -1e3 })
2891
+ import_sdk4.ListResourcesHook.Will("parseInput", { priority: -1e3 })
2711
2892
  ], ObservabilityPlugin.prototype, "_listResourcesWill", 1);
2712
2893
  __decorateClass([
2713
- import_sdk2.ListResourcesHook.Did("filterByAuthorities", { priority: 1e3 })
2894
+ import_sdk4.ListResourcesHook.Did("filterByAuthorities", { priority: 1e3 })
2714
2895
  ], ObservabilityPlugin.prototype, "_listResourcesDidFilter", 1);
2715
2896
  __decorateClass([
2716
- import_sdk2.ListResourcesHook.Did("parseResources", { priority: 1e3 })
2897
+ import_sdk4.ListResourcesHook.Did("parseResources", { priority: 1e3 })
2717
2898
  ], ObservabilityPlugin.prototype, "_listResourcesDone", 1);
2718
2899
  __decorateClass([
2719
- import_sdk2.ListResourceTemplatesHook.Will("parseInput", { priority: -1e3 })
2900
+ import_sdk4.ListResourceTemplatesHook.Will("parseInput", { priority: -1e3 })
2720
2901
  ], ObservabilityPlugin.prototype, "_listTemplatesWill", 1);
2721
2902
  __decorateClass([
2722
- import_sdk2.ListResourceTemplatesHook.Did("filterByAuthorities", { priority: 1e3 })
2903
+ import_sdk4.ListResourceTemplatesHook.Did("filterByAuthorities", { priority: 1e3 })
2723
2904
  ], ObservabilityPlugin.prototype, "_listTemplatesDidFilter", 1);
2724
2905
  __decorateClass([
2725
- import_sdk2.ListResourceTemplatesHook.Did("parseTemplates", { priority: 1e3 })
2906
+ import_sdk4.ListResourceTemplatesHook.Did("parseTemplates", { priority: 1e3 })
2726
2907
  ], ObservabilityPlugin.prototype, "_listTemplatesDone", 1);
2727
2908
  __decorateClass([
2728
2909
  ListPromptsHook.Will("parseInput", { priority: -1e3 })
@@ -2923,13 +3104,13 @@ __decorateClass([
2923
3104
  LoggingSetLevelHook.Did("finalize", { priority: 1e3 })
2924
3105
  ], ObservabilityPlugin.prototype, "_setLevelDone", 1);
2925
3106
  __decorateClass([
2926
- import_sdk2.ToolHook.Did("createToolCallContext", { priority: 999 })
3107
+ import_sdk4.ToolHook.Did("createToolCallContext", { priority: 999 })
2927
3108
  ], ObservabilityPlugin.prototype, "_toolDidCreateCtxFetch", 1);
2928
3109
  __decorateClass([
2929
- import_sdk2.AgentCallHook.Did("execute", { priority: 999 })
3110
+ import_sdk4.AgentCallHook.Did("execute", { priority: 999 })
2930
3111
  ], ObservabilityPlugin.prototype, "_agentDidExecuteMeta", 1);
2931
3112
  ObservabilityPlugin = __decorateClass([
2932
- (0, import_sdk2.Plugin)({
3113
+ (0, import_sdk4.Plugin)({
2933
3114
  name: "observability",
2934
3115
  description: "Full-system OpenTelemetry instrumentation, structured logging, and request logs",
2935
3116
  providers: [],
@@ -2966,11 +3147,11 @@ function resolveRequestLogOptions(input) {
2966
3147
  }
2967
3148
 
2968
3149
  // libs/observability/src/otel/context-propagator.ts
2969
- var import_api17 = require("@opentelemetry/api");
3150
+ var import_api18 = require("@opentelemetry/api");
2970
3151
  var TRACEPARENT_HEADER = "traceparent";
2971
3152
  var FrontMcpPropagator = class {
2972
3153
  inject(context, carrier, setter) {
2973
- const span = import_api17.trace.getSpan(context);
3154
+ const span = import_api18.trace.getSpan(context);
2974
3155
  if (!span) return;
2975
3156
  const sc = span.spanContext();
2976
3157
  if (!sc || !isValidSpanContext(sc)) return;
@@ -2990,8 +3171,8 @@ var FrontMcpPropagator = class {
2990
3171
  traceFlags: parsed.traceFlags,
2991
3172
  isRemote: true
2992
3173
  };
2993
- const span = import_api17.trace.wrapSpanContext(spanContext);
2994
- return import_api17.trace.setSpan(context, span);
3174
+ const span = import_api18.trace.wrapSpanContext(spanContext);
3175
+ return import_api18.trace.setSpan(context, span);
2995
3176
  }
2996
3177
  fields() {
2997
3178
  return [TRACEPARENT_HEADER];
@@ -3058,33 +3239,6 @@ function setupOTel(options) {
3058
3239
  }
3059
3240
  }
3060
3241
 
3061
- // libs/observability/src/otel/spans/hook.span.ts
3062
- var import_api18 = require("@opentelemetry/api");
3063
- function recordHookEvent(parentSpan, stage, owner) {
3064
- const attributes = {};
3065
- if (owner) {
3066
- attributes[FrontMcpAttributes.HOOK_OWNER] = owner;
3067
- }
3068
- parentSpan.addEvent(`hook.${stage}`, attributes);
3069
- }
3070
- function startHookSpan(tracer, options) {
3071
- const attributes = {
3072
- [FrontMcpAttributes.HOOK_STAGE]: options.stage
3073
- };
3074
- if (options.owner) {
3075
- attributes[FrontMcpAttributes.HOOK_OWNER] = options.owner;
3076
- }
3077
- if (options.flowName) {
3078
- attributes[FrontMcpAttributes.FLOW_NAME] = options.flowName;
3079
- }
3080
- return startSpan(tracer, {
3081
- name: `hook ${options.stage}`,
3082
- kind: import_api18.SpanKind.INTERNAL,
3083
- attributes,
3084
- parentContext: options.parentContext
3085
- });
3086
- }
3087
-
3088
3242
  // libs/observability/src/otel/spans/pretty-span-exporter.ts
3089
3243
  var import_api19 = require("@opentelemetry/api");
3090
3244
  var RESET = "\x1B[0m";
@@ -3252,6 +3406,13 @@ function renderJsonExposition(counters, gauges = []) {
3252
3406
  var import_utils2 = require("@frontmcp/utils");
3253
3407
  var NS_PER_SECOND = 1e9;
3254
3408
  var MICROS_PER_SECOND = 1e6;
3409
+ function probeSafely(probe) {
3410
+ try {
3411
+ return probe();
3412
+ } catch {
3413
+ return void 0;
3414
+ }
3415
+ }
3255
3416
  function defaultMonitorEventLoopDelay() {
3256
3417
  try {
3257
3418
  const { monitorEventLoopDelay } = require("node:perf_hooks");
@@ -3297,54 +3458,70 @@ var ProcessStatsCollector = class {
3297
3458
  this.getActiveHandles = init.getActiveHandles ?? defaultGetActiveHandles;
3298
3459
  this.getActiveRequests = init.getActiveRequests ?? defaultGetActiveRequests;
3299
3460
  this.readFdCount = init.readFdCount ?? defaultReadFdCount;
3300
- this.cpuStart = this.cpuUsage();
3461
+ this.cpuStart = probeSafely(() => this.cpuUsage());
3301
3462
  if (this.options.eventLoopLag !== false) {
3302
3463
  this.histogram = init.monitorEventLoopDelay ? init.monitorEventLoopDelay() : defaultMonitorEventLoopDelay();
3303
3464
  }
3304
3465
  }
3305
3466
  collect() {
3306
3467
  const entries = [];
3307
- const cpu = this.cpuUsage(this.cpuStart);
3308
- entries.push({
3309
- name: "frontmcp_process_cpu_seconds_total",
3310
- value: cpu.user / MICROS_PER_SECOND,
3311
- attributes: { mode: "user" },
3312
- help: "CPU time consumed since collector start, by mode (seconds)"
3313
- });
3314
- entries.push({
3315
- name: "frontmcp_process_cpu_seconds_total",
3316
- value: cpu.system / MICROS_PER_SECOND,
3317
- attributes: { mode: "system" }
3318
- });
3319
- const mem = this.memoryUsage();
3320
- entries.push({
3321
- name: "frontmcp_process_resident_memory_bytes",
3322
- value: mem.rss,
3323
- help: "Resident memory size in bytes"
3324
- });
3325
- entries.push({
3326
- name: "frontmcp_process_heap_bytes",
3327
- value: mem.heapTotal,
3328
- help: "Total V8 heap size in bytes"
3329
- });
3330
- entries.push({
3331
- name: "frontmcp_process_heap_used_bytes",
3332
- value: mem.heapUsed,
3333
- help: "Used V8 heap size in bytes"
3334
- });
3335
- entries.push({
3336
- name: "frontmcp_process_external_bytes",
3337
- value: mem.external,
3338
- help: "Memory used by C++ objects bound to JS in bytes"
3339
- });
3340
- entries.push({
3341
- name: "frontmcp_process_uptime_seconds",
3342
- value: this.uptime(),
3343
- help: "Time since process start in seconds"
3468
+ this.cpuStart ??= probeSafely(() => this.cpuUsage());
3469
+ const cpu = this.cpuStart && probeSafely(() => this.cpuUsage(this.cpuStart));
3470
+ if (cpu) {
3471
+ entries.push({
3472
+ name: "frontmcp_process_cpu_seconds_total",
3473
+ value: cpu.user / MICROS_PER_SECOND,
3474
+ attributes: { mode: "user" },
3475
+ help: "CPU time consumed since collector start, by mode (seconds)"
3476
+ });
3477
+ entries.push({
3478
+ name: "frontmcp_process_cpu_seconds_total",
3479
+ value: cpu.system / MICROS_PER_SECOND,
3480
+ attributes: { mode: "system" }
3481
+ });
3482
+ }
3483
+ const mem = probeSafely(() => this.memoryUsage());
3484
+ if (mem && mem.rss > 0) {
3485
+ entries.push({
3486
+ name: "frontmcp_process_resident_memory_bytes",
3487
+ value: mem.rss,
3488
+ help: "Resident memory size in bytes"
3489
+ });
3490
+ entries.push({
3491
+ name: "frontmcp_process_heap_bytes",
3492
+ value: mem.heapTotal,
3493
+ help: "Total V8 heap size in bytes"
3494
+ });
3495
+ entries.push({
3496
+ name: "frontmcp_process_heap_used_bytes",
3497
+ value: mem.heapUsed,
3498
+ help: "Used V8 heap size in bytes"
3499
+ });
3500
+ entries.push({
3501
+ name: "frontmcp_process_external_bytes",
3502
+ value: mem.external,
3503
+ help: "Memory used by C++ objects bound to JS in bytes"
3504
+ });
3505
+ }
3506
+ const uptime = probeSafely(() => this.uptime());
3507
+ if (uptime !== void 0 && uptime > 0) {
3508
+ entries.push({
3509
+ name: "frontmcp_process_uptime_seconds",
3510
+ value: uptime,
3511
+ help: "Time since process start in seconds"
3512
+ });
3513
+ }
3514
+ const histogram = this.options.eventLoopLag !== false ? this.histogram : void 0;
3515
+ const lag = histogram && probeSafely(() => {
3516
+ const sample = {
3517
+ meanSeconds: histogram.mean / NS_PER_SECOND,
3518
+ p99Seconds: histogram.percentile(99) / NS_PER_SECOND
3519
+ };
3520
+ histogram.reset();
3521
+ return sample;
3344
3522
  });
3345
- if (this.options.eventLoopLag !== false && this.histogram) {
3346
- const meanSeconds = this.histogram.mean / NS_PER_SECOND;
3347
- const p99Seconds = this.histogram.percentile(99) / NS_PER_SECOND;
3523
+ if (lag) {
3524
+ const { meanSeconds, p99Seconds } = lag;
3348
3525
  if (Number.isFinite(meanSeconds)) {
3349
3526
  entries.push({
3350
3527
  name: "frontmcp_nodejs_eventloop_lag_seconds",
@@ -3360,7 +3537,6 @@ var ProcessStatsCollector = class {
3360
3537
  attributes: { quantile: "p99" }
3361
3538
  });
3362
3539
  }
3363
- this.histogram.reset();
3364
3540
  }
3365
3541
  if (this.options.activeHandles !== false) {
3366
3542
  const handles = this.getActiveHandles?.();