mergen-server 1.4.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/LICENSE +57 -0
- package/README.md +96 -0
- package/dist/__stubs__/calibration.js +471 -0
- package/dist/__stubs__/causal.js +601 -0
- package/dist/__stubs__/closed-source.js +150 -0
- package/dist/__stubs__/license.js +35 -0
- package/dist/__stubs__/plans.js +197 -0
- package/dist/app.js +467 -0
- package/dist/cli.js +250 -0
- package/dist/commands/agent-identity.js +79 -0
- package/dist/commands/gate.js +107 -0
- package/dist/commands/github.js +632 -0
- package/dist/commands/incident.js +644 -0
- package/dist/commands/policy.js +710 -0
- package/dist/commands/scan.js +243 -0
- package/dist/commands/setup.js +1218 -0
- package/dist/commands/shared.js +109 -0
- package/dist/commands/team.js +809 -0
- package/dist/datadog/blame-attribution.js +228 -0
- package/dist/datadog/client.js +159 -0
- package/dist/datadog/compactor.js +169 -0
- package/dist/datadog/fingerprinter.js +34 -0
- package/dist/datadog/incident-state.js +21 -0
- package/dist/datadog/line-matcher.js +56 -0
- package/dist/datadog/memory-store.js +399 -0
- package/dist/datadog/otel-trace.js +67 -0
- package/dist/index.js +505 -0
- package/dist/intelligence/action-risk.js +132 -0
- package/dist/intelligence/activity-feed.js +40 -0
- package/dist/intelligence/agent-identity.js +115 -0
- package/dist/intelligence/agent-pipeline.js +329 -0
- package/dist/intelligence/agent-profiles.js +81 -0
- package/dist/intelligence/ai-commit.js +16 -0
- package/dist/intelligence/approval-analytics.js +42 -0
- package/dist/intelligence/approval-events.js +5 -0
- package/dist/intelligence/arch-boundaries.js +94 -0
- package/dist/intelligence/arch-graph.js +117 -0
- package/dist/intelligence/autonomy.js +397 -0
- package/dist/intelligence/azure-broker.js +57 -0
- package/dist/intelligence/azure-proxy-sessions.js +49 -0
- package/dist/intelligence/baseline.js +1 -0
- package/dist/intelligence/behavior-baseline.js +134 -0
- package/dist/intelligence/billing.js +1 -0
- package/dist/intelligence/blast-radius.js +224 -0
- package/dist/intelligence/bypass.js +157 -0
- package/dist/intelligence/calibration-classifier.js +77 -0
- package/dist/intelligence/calibration-git-sync.js +74 -0
- package/dist/intelligence/calibration.js +1 -0
- package/dist/intelligence/cascade-detector.js +58 -0
- package/dist/intelligence/case-study-generator.js +126 -0
- package/dist/intelligence/causal-graph.js +126 -0
- package/dist/intelligence/causal.js +1 -0
- package/dist/intelligence/change-risk.js +102 -0
- package/dist/intelligence/compliance-report.js +134 -0
- package/dist/intelligence/confidence-report.js +85 -0
- package/dist/intelligence/corpus-to-policy.js +140 -0
- package/dist/intelligence/credential-broker.js +112 -0
- package/dist/intelligence/credential-misuse.js +39 -0
- package/dist/intelligence/debug-sessions.js +108 -0
- package/dist/intelligence/decision-escalation.js +36 -0
- package/dist/intelligence/decision-memory.js +47 -0
- package/dist/intelligence/decision-similarity.js +85 -0
- package/dist/intelligence/default-safety-tests.js +53 -0
- package/dist/intelligence/degradation-watcher.js +108 -0
- package/dist/intelligence/detector-plugins.js +70 -0
- package/dist/intelligence/detectors.js +1 -0
- package/dist/intelligence/device-auth.js +138 -0
- package/dist/intelligence/diff-size.js +42 -0
- package/dist/intelligence/ebpf-verify.js +45 -0
- package/dist/intelligence/enterprise-policy-engine.js +1122 -0
- package/dist/intelligence/error-fingerprint.js +1 -0
- package/dist/intelligence/execution-gate.js +100 -0
- package/dist/intelligence/execution-mode.js +22 -0
- package/dist/intelligence/gate-analytics.js +256 -0
- package/dist/intelligence/gate-decision.js +64 -0
- package/dist/intelligence/gcp-broker.js +50 -0
- package/dist/intelligence/git-adr-sync.js +207 -0
- package/dist/intelligence/hitl-hold.js +460 -0
- package/dist/intelligence/hypothesis-history.js +93 -0
- package/dist/intelligence/idp-identity.js +83 -0
- package/dist/intelligence/impl-critic.js +216 -0
- package/dist/intelligence/incident-autopilot.js +622 -0
- package/dist/intelligence/incident-replay.js +162 -0
- package/dist/intelligence/incident-result-cache.js +33 -0
- package/dist/intelligence/incident-similarity.js +58 -0
- package/dist/intelligence/infra-detectors.js +312 -0
- package/dist/intelligence/license.js +185 -0
- package/dist/intelligence/llm-spokesperson.js +62 -0
- package/dist/intelligence/mcp-prompts.js +320 -0
- package/dist/intelligence/mcp-resources.js +171 -0
- package/dist/intelligence/normalize.js +108 -0
- package/dist/intelligence/notifications.js +83 -0
- package/dist/intelligence/oidc-issuer.js +87 -0
- package/dist/intelligence/override-corpus.js +597 -0
- package/dist/intelligence/planning-gate.js +84 -0
- package/dist/intelligence/plans.js +223 -0
- package/dist/intelligence/platt-scaling.js +164 -0
- package/dist/intelligence/policy-proposals.js +84 -0
- package/dist/intelligence/policy-suggester.js +161 -0
- package/dist/intelligence/policy-sync.js +103 -0
- package/dist/intelligence/policy-test-runner.js +35 -0
- package/dist/intelligence/postmortem-parser.js +181 -0
- package/dist/intelligence/postmortem-retrieval.js +224 -0
- package/dist/intelligence/postmortem-store.js +596 -0
- package/dist/intelligence/pr-commenter.js +81 -0
- package/dist/intelligence/pr-policy-gate.js +38 -0
- package/dist/intelligence/pr-shadow-analyzer.js +229 -0
- package/dist/intelligence/process-tree.js +108 -0
- package/dist/intelligence/prompts.js +1 -0
- package/dist/intelligence/replay.js +38 -0
- package/dist/intelligence/repro-steps.js +1 -0
- package/dist/intelligence/resource-extractors.js +0 -0
- package/dist/intelligence/resource-file-context.js +138 -0
- package/dist/intelligence/risk-score.js +22 -0
- package/dist/intelligence/rollback.js +137 -0
- package/dist/intelligence/runbook-updater.js +143 -0
- package/dist/intelligence/sandbox.js +194 -0
- package/dist/intelligence/script-trust.js +104 -0
- package/dist/intelligence/session-metrics.js +67 -0
- package/dist/intelligence/session-threat-tracker.js +231 -0
- package/dist/intelligence/shadow-digest-cron.js +81 -0
- package/dist/intelligence/shadow-log.js +284 -0
- package/dist/intelligence/shell-ast.js +145 -0
- package/dist/intelligence/siem-forward.js +104 -0
- package/dist/intelligence/slack-digest.js +151 -0
- package/dist/intelligence/slack-override-loop.js +217 -0
- package/dist/intelligence/slack-routing.js +67 -0
- package/dist/intelligence/slack.js +900 -0
- package/dist/intelligence/sql-ast.js +31 -0
- package/dist/intelligence/team.js +1 -0
- package/dist/intelligence/telemetry.js +76 -0
- package/dist/intelligence/threshold-optimizer.js +75 -0
- package/dist/intelligence/token-budget.js +66 -0
- package/dist/intelligence/tool-guard.js +708 -0
- package/dist/intelligence/tool-manifest 2.js +74 -0
- package/dist/intelligence/tool-manifest.js +125 -0
- package/dist/intelligence/tools-analysis.js +857 -0
- package/dist/intelligence/tools-arch.js +152 -0
- package/dist/intelligence/tools-autonomy.js +495 -0
- package/dist/intelligence/tools-blast-radius.js +140 -0
- package/dist/intelligence/tools-browser.js +431 -0
- package/dist/intelligence/tools-change-timeline.js +154 -0
- package/dist/intelligence/tools-credentials.js +117 -0
- package/dist/intelligence/tools-datadog.js +233 -0
- package/dist/intelligence/tools-debug-sessions.js +209 -0
- package/dist/intelligence/tools-discovery.js +284 -0
- package/dist/intelligence/tools-infra.js +612 -0
- package/dist/intelligence/tools-intent.js +128 -0
- package/dist/intelligence/tools-memory.js +514 -0
- package/dist/intelligence/tools-runbook.js +951 -0
- package/dist/intelligence/tools-sessions.js +107 -0
- package/dist/intelligence/tools-state.js +232 -0
- package/dist/intelligence/tools-utility.js +430 -0
- package/dist/intelligence/tools-validate.js +215 -0
- package/dist/intelligence/tools.js +1 -0
- package/dist/intelligence/trace-context.js +11 -0
- package/dist/intelligence/unclassified-clusters.js +87 -0
- package/dist/intelligence/usage.js +195 -0
- package/dist/routes/active-authors.js +63 -0
- package/dist/routes/activity-feed.js +21 -0
- package/dist/routes/adr.js +117 -0
- package/dist/routes/agent-activity.js +184 -0
- package/dist/routes/agent-blunders.js +163 -0
- package/dist/routes/agents.js +175 -0
- package/dist/routes/api-keys.js +65 -0
- package/dist/routes/arch.js +75 -0
- package/dist/routes/audit-export.js +93 -0
- package/dist/routes/billing-dashboard.js +158 -0
- package/dist/routes/billing-outcome.js +77 -0
- package/dist/routes/calibration.js +305 -0
- package/dist/routes/ci-gate-history.js +20 -0
- package/dist/routes/ci-gate.js +232 -0
- package/dist/routes/ci.js +293 -0
- package/dist/routes/compliance-report.js +24 -0
- package/dist/routes/confidence.js +30 -0
- package/dist/routes/credentials.js +119 -0
- package/dist/routes/dashboard.js +1407 -0
- package/dist/routes/decision-memory.js +42 -0
- package/dist/routes/demo.js +844 -0
- package/dist/routes/device-auth-stub.js +173 -0
- package/dist/routes/explain-why.js +39 -0
- package/dist/routes/gate-analytics.js +213 -0
- package/dist/routes/gate.js +28 -0
- package/dist/routes/github-webhook.js +364 -0
- package/dist/routes/habituation.js +30 -0
- package/dist/routes/health-integrations.js +243 -0
- package/dist/routes/heartbeats.js +45 -0
- package/dist/routes/hitl.js +364 -0
- package/dist/routes/impact-report.js +645 -0
- package/dist/routes/incident-webhook.js +63 -0
- package/dist/routes/incidents.js +366 -0
- package/dist/routes/layers.js +62 -0
- package/dist/routes/license.js +187 -0
- package/dist/routes/oidc-issuer.js +30 -0
- package/dist/routes/onboarding.js +170 -0
- package/dist/routes/otel.js +67 -0
- package/dist/routes/otlp-receiver.js +179 -0
- package/dist/routes/overrides.js +241 -0
- package/dist/routes/pagerduty.js +258 -0
- package/dist/routes/policies.js +809 -0
- package/dist/routes/policy-nl.js +169 -0
- package/dist/routes/postmortem.js +102 -0
- package/dist/routes/pr-shadow.js +47 -0
- package/dist/routes/rbac.js +61 -0
- package/dist/routes/replay.js +17 -0
- package/dist/routes/risk-report.js +164 -0
- package/dist/routes/runbooks.js +125 -0
- package/dist/routes/safety-test.js +174 -0
- package/dist/routes/sdk.js +222 -0
- package/dist/routes/sensor.js +819 -0
- package/dist/routes/sentry.js +140 -0
- package/dist/routes/sessions.js +43 -0
- package/dist/routes/setup-ui.js +594 -0
- package/dist/routes/shadow-report.js +107 -0
- package/dist/routes/slack-routing.js +171 -0
- package/dist/routes/team-usage.js +71 -0
- package/dist/routes/telemetry.js +28 -0
- package/dist/routes/tenants.js +199 -0
- package/dist/routes/tickets.js +167 -0
- package/dist/routes/validate.js +13 -0
- package/dist/routes/war-room.js +113 -0
- package/dist/scripts/check-arch.js +22 -0
- package/dist/seeds/community-corpus.js +176 -0
- package/dist/seeds/corpus.js +1224 -0
- package/dist/sensor/action-ledger.js +327 -0
- package/dist/sensor/adr-store.js +140 -0
- package/dist/sensor/agent-blunder-store.js +370 -0
- package/dist/sensor/agent-context-store.js +225 -0
- package/dist/sensor/agent-memory-store.js +231 -0
- package/dist/sensor/audit-fetch.js +23 -0
- package/dist/sensor/audit-log.js +273 -0
- package/dist/sensor/buffer-schemas.js +225 -0
- package/dist/sensor/buffer.js +599 -0
- package/dist/sensor/bypass-tracker.js +93 -0
- package/dist/sensor/ci-gate-history.js +70 -0
- package/dist/sensor/cloud-auth.js +183 -0
- package/dist/sensor/commit-context-store.js +235 -0
- package/dist/sensor/docker-log-stream.js +164 -0
- package/dist/sensor/docker-monitor.js +122 -0
- package/dist/sensor/extended-buffer.js +46 -0
- package/dist/sensor/feedback-token.js +41 -0
- package/dist/sensor/file-lock.js +6 -0
- package/dist/sensor/fs-watcher.js +106 -0
- package/dist/sensor/gate-heartbeat.js +122 -0
- package/dist/sensor/git-suspect.js +136 -0
- package/dist/sensor/habituation-store.js +77 -0
- package/dist/sensor/heartbeat-monitor.js +133 -0
- package/dist/sensor/incident-store.js +340 -0
- package/dist/sensor/infra-normalizer.js +513 -0
- package/dist/sensor/ingest.js +339 -0
- package/dist/sensor/jest-reporter.js +52 -0
- package/dist/sensor/k8s-events.js +132 -0
- package/dist/sensor/layer2-store.js +111 -0
- package/dist/sensor/layer3-store.js +230 -0
- package/dist/sensor/layer4-store.js +147 -0
- package/dist/sensor/logger.js +13 -0
- package/dist/sensor/otel-exporter.js +161 -0
- package/dist/sensor/paths.js +71 -0
- package/dist/sensor/policy-history.js +147 -0
- package/dist/sensor/pr-shadow-store.js +83 -0
- package/dist/sensor/process-watcher.js +162 -0
- package/dist/sensor/rbac.js +92 -0
- package/dist/sensor/redact.js +114 -0
- package/dist/sensor/redis-store.js +191 -0
- package/dist/sensor/route-reachability.js +64 -0
- package/dist/sensor/security-utils.js +18 -0
- package/dist/sensor/service-graph.js +170 -0
- package/dist/sensor/service-topology.js +227 -0
- package/dist/sensor/session-history.js +74 -0
- package/dist/sensor/session-persist.js +38 -0
- package/dist/sensor/shadow-promote.js +122 -0
- package/dist/sensor/sourcemap.js +281 -0
- package/dist/sensor/sqlite-store.js +205 -0
- package/dist/sensor/sso.js +164 -0
- package/dist/sensor/vitest-reporter.js +65 -0
- package/dist/sensor/watcher.js +48 -0
- package/dist/storage/interfaces.js +0 -0
- package/dist/storage/pg/pg-action-ledger.js +138 -0
- package/dist/storage/pg/pg-approval-store.js +107 -0
- package/dist/storage/pg/pg-blunder-store.js +193 -0
- package/dist/storage/pg/pg-ci-gate-history.js +83 -0
- package/dist/storage/pg/pg-client.js +24 -0
- package/dist/storage/pg/pg-event-store.js +63 -0
- package/dist/storage/pg/pg-incident-store.js +190 -0
- package/dist/storage/pg/pg-migrations.js +26 -0
- package/dist/storage/pg/pg-override-corpus.js +447 -0
- package/dist/storage/pg/pg-shadow-log.js +129 -0
- package/dist/storage/sqlite/sqlite-action-ledger.js +18 -0
- package/dist/storage/sqlite/sqlite-approval-store.js +37 -0
- package/dist/storage/sqlite/sqlite-blunder-store.js +27 -0
- package/dist/storage/sqlite/sqlite-ci-gate-history.js +19 -0
- package/dist/storage/sqlite/sqlite-event-store.js +29 -0
- package/dist/storage/sqlite/sqlite-incident-store.js +34 -0
- package/dist/storage/sqlite/sqlite-override-corpus.js +75 -0
- package/dist/storage/sqlite/sqlite-shadow-log.js +36 -0
- package/dist/storage/store-factory.js +55 -0
- package/dist/storage/store-registry.js +31 -0
- package/dist/update-checker.js +107 -0
- package/dist/workers/autopilot-worker.js +28 -0
- package/dist/workers/notification-worker.js +28 -0
- package/dist/workers/queues.js +58 -0
- package/dist/workers/validation-worker.js +24 -0
- package/dist/workers/worker-registry.js +18 -0
- package/package.json +123 -0
- package/sdk/mergen-inject.js +260 -0
- package/sdk/node.js +313 -0
- package/sdk/vite-plugin.ts +57 -0
- package/sdk/webpack-plugin.js +106 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { store } from "../sensor/buffer.js";
|
|
3
|
+
import { incidentStore } from "../sensor/incident-store.js";
|
|
4
|
+
import { commitContextStore } from "../sensor/commit-context-store.js";
|
|
5
|
+
import { detectAiCommit } from "./ai-commit.js";
|
|
6
|
+
import { trackCall } from "./tools-state.js";
|
|
7
|
+
function fmtMs(ms) {
|
|
8
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
9
|
+
const m = Math.floor(ms / 6e4);
|
|
10
|
+
const s = Math.round(ms % 6e4 / 1e3);
|
|
11
|
+
return s > 0 ? `${m}m ${s}s` : `${m}m`;
|
|
12
|
+
}
|
|
13
|
+
function isoDate(ts) {
|
|
14
|
+
return new Date(ts).toISOString().slice(0, 16).replace("T", " ");
|
|
15
|
+
}
|
|
16
|
+
function registerChangeTimelineTools(server) {
|
|
17
|
+
server.registerTool(
|
|
18
|
+
"get_change_timeline",
|
|
19
|
+
{
|
|
20
|
+
description: 'Returns a chronological change timeline for a service \u2014 the "what changed?" context dump before diagnosis begins. Pulls deployments, CI runs, incidents, and AI-generated commits from the execution buffer. AI-written commits are flagged so on-call engineers know which changes have no human author to ask. Call this first when a PagerDuty alert fires, before reconstruct_context or triage_incident.',
|
|
21
|
+
inputSchema: {
|
|
22
|
+
service: z.string().optional().describe('Service name to filter (e.g. "api", "payments-worker"). Omit to get all services.'),
|
|
23
|
+
days: z.number().int().min(1).max(30).optional().describe("How many days back to look (default: 7).")
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
async ({ service, days = 7 }) => {
|
|
27
|
+
trackCall("get_change_timeline");
|
|
28
|
+
const windowMs = days * 24 * 60 * 60 * 1e3;
|
|
29
|
+
const since = Date.now() - windowMs;
|
|
30
|
+
const entries = [];
|
|
31
|
+
const ctxWindow = commitContextStore.listByWindow(since, Date.now(), service);
|
|
32
|
+
const ctxBySha = new Map(ctxWindow.map((c) => [c.sha.slice(0, 7), c]));
|
|
33
|
+
const deploys = store.getDeployments(100, void 0, since);
|
|
34
|
+
for (const d of deploys) {
|
|
35
|
+
if (service && d.service && d.service !== service) continue;
|
|
36
|
+
const statusIcon = d.status === "success" ? "\u2705" : d.status === "failure" ? "\u274C" : d.status === "rollback" ? "\u23EA" : "\u25B6";
|
|
37
|
+
const svcLabel = d.service ? ` [${d.service}]` : "";
|
|
38
|
+
const aiSignal = d.actor ? detectAiCommit(d.actor, d.actor) : { detected: false, tool: null };
|
|
39
|
+
const ctx = ctxBySha.get(d.sha.slice(0, 7)) ?? commitContextStore.getBySha(d.sha);
|
|
40
|
+
const prTag = ctx?.prNumber ? ` PR#${ctx.prNumber}` : "";
|
|
41
|
+
const approverTag = ctx?.approvers.length ? ` \u2713 ${ctx.approvers.join(", ")}` : "";
|
|
42
|
+
entries.push({
|
|
43
|
+
ts: d.timestamp,
|
|
44
|
+
kind: "deploy",
|
|
45
|
+
label: `${statusIcon} Deploy${svcLabel} ${d.shortSha ?? d.sha.slice(0, 7)} \u2192 ${d.environment}${d.version ? ` v${d.version}` : ""}${d.actor ? ` by ${d.actor}` : ""}${prTag}${approverTag}`,
|
|
46
|
+
aiGenerated: aiSignal.detected || ctx?.aiGenerated || void 0,
|
|
47
|
+
aiTool: aiSignal.tool ?? ctx?.aiTool
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const ciEvents = store.getCIEvents(100, void 0, since);
|
|
51
|
+
for (const c of ciEvents) {
|
|
52
|
+
const statusIcon = c.status === "success" ? "\u2705" : c.status === "failure" ? "\u274C" : "\u23F8";
|
|
53
|
+
const branchLabel = c.branch ? ` (${c.branch})` : "";
|
|
54
|
+
entries.push({
|
|
55
|
+
ts: c.timestamp,
|
|
56
|
+
kind: "ci",
|
|
57
|
+
label: `${statusIcon} CI ${c.provider.replace("_", " ")} \xB7 ${c.job}${branchLabel} ${c.sha.slice(0, 7)}${c.status === "failure" && c.failedTests?.length ? ` \u2014 ${c.failedTests.length} test(s) failed` : ""}`
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const incidents = incidentStore.list(void 0, 200).filter((i) => {
|
|
61
|
+
if (i.createdAt < since) return false;
|
|
62
|
+
if (service && i.service && i.service !== service) return false;
|
|
63
|
+
return true;
|
|
64
|
+
});
|
|
65
|
+
for (const inc of incidents) {
|
|
66
|
+
const statusIcon = inc.status === "resolved" ? inc.resolvedAutonomously ? "\u{1F916}" : "\u2705" : "\u{1F534}";
|
|
67
|
+
const mttr = inc.resolvedAt ? ` MTTR: ${fmtMs(inc.resolvedAt - inc.createdAt)}` : "";
|
|
68
|
+
const svcLabel = inc.service ? ` [${inc.service}]` : "";
|
|
69
|
+
entries.push({
|
|
70
|
+
ts: inc.createdAt,
|
|
71
|
+
kind: "incident",
|
|
72
|
+
label: `${statusIcon} Incident${svcLabel} \xB7 ${inc.tag || "unknown"} \xB7 conf ${Math.round(inc.confidence * 100)}%${mttr}`
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const errors = store.getLogs(200, "error", since);
|
|
76
|
+
const seenShas = /* @__PURE__ */ new Set();
|
|
77
|
+
for (const e of errors) {
|
|
78
|
+
const gs = e.gitSuspect;
|
|
79
|
+
if (!gs || seenShas.has(gs.sha)) continue;
|
|
80
|
+
if (service && e.url && !e.url.includes(service)) continue;
|
|
81
|
+
const signal = gs.aiGenerated ? { detected: true, tool: null } : detectAiCommit(gs.summary, gs.author);
|
|
82
|
+
if (signal.detected) {
|
|
83
|
+
seenShas.add(gs.sha);
|
|
84
|
+
entries.push({
|
|
85
|
+
ts: e.timestamp,
|
|
86
|
+
kind: "ai_commit",
|
|
87
|
+
label: `\u{1F916} AI commit ${gs.sha.slice(0, 7)} \u2014 "${gs.summary.slice(0, 80)}" by ${gs.author}${signal.tool ? ` (${signal.tool})` : ""}`,
|
|
88
|
+
aiGenerated: true,
|
|
89
|
+
aiTool: signal.tool
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
entries.sort((a, b) => a.ts - b.ts);
|
|
94
|
+
if (entries.length === 0) {
|
|
95
|
+
const corpusNote = incidentStore.list(void 0, 1).length === 0 ? "\n\n_No incident history yet \u2014 trigger an incident or run `mergen-server demo` to seed the corpus._" : `
|
|
96
|
+
|
|
97
|
+
_No events in the last ${days} day${days !== 1 ? "s" : ""}${service ? ` for service "${service}"` : ""}. Try a wider window._`;
|
|
98
|
+
return {
|
|
99
|
+
content: [{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: `## Change Timeline${service ? ` \u2014 ${service}` : ""}
|
|
102
|
+
|
|
103
|
+
_No changes recorded in the last ${days} day${days !== 1 ? "s" : ""}._ ${corpusNote}`
|
|
104
|
+
}]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const aiCount = entries.filter((e) => e.aiGenerated).length;
|
|
108
|
+
const incidentCount = entries.filter((e) => e.kind === "incident").length;
|
|
109
|
+
const deployCount = entries.filter((e) => e.kind === "deploy").length;
|
|
110
|
+
const header = [
|
|
111
|
+
`## Change Timeline${service ? ` \u2014 ${service}` : ""} (last ${days} day${days !== 1 ? "s" : ""})`,
|
|
112
|
+
"",
|
|
113
|
+
`${deployCount} deploy${deployCount !== 1 ? "s" : ""} \xB7 ${incidentCount} incident${incidentCount !== 1 ? "s" : ""} \xB7 ${aiCount} AI-generated commit${aiCount !== 1 ? "s" : ""} flagged`,
|
|
114
|
+
""
|
|
115
|
+
];
|
|
116
|
+
const rows = entries.map((e) => {
|
|
117
|
+
const aiTag = e.aiGenerated ? " \u26A0\uFE0F AI" : "";
|
|
118
|
+
return `- \`${isoDate(e.ts)}\` ${e.label}${aiTag}`;
|
|
119
|
+
});
|
|
120
|
+
const footer = [];
|
|
121
|
+
if (aiCount > 0) {
|
|
122
|
+
footer.push(
|
|
123
|
+
"",
|
|
124
|
+
`> \u26A0\uFE0F **${aiCount} AI-generated commit${aiCount !== 1 ? "s" : ""} in this window.** These have no human author to consult during triage.`,
|
|
125
|
+
`> Call \`reconstruct_context\` for causal analysis or \`explain_service(service: "${service ?? "service-name"}")\` for failure mode history.`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
const prEntries = ctxWindow.filter((c) => c.prTitle && c.prNumber).slice(0, 3);
|
|
129
|
+
if (prEntries.length > 0) {
|
|
130
|
+
footer.push("", "### PR Intent Archive");
|
|
131
|
+
for (const c of prEntries) {
|
|
132
|
+
const issueRefs = c.linkedIssues.slice(0, 3).map((i) => i.ref).join(", ");
|
|
133
|
+
const approvers = c.approvers.length ? `Approved by: ${c.approvers.join(", ")}` : "No recorded approvers";
|
|
134
|
+
const aiTag = c.aiGenerated ? ` \u{1F916} ${c.aiTool ?? "AI"}` : "";
|
|
135
|
+
footer.push(
|
|
136
|
+
`**PR #${c.prNumber}${aiTag}**: ${c.prTitle}`,
|
|
137
|
+
`_Author: @${c.author ?? "?"} \xB7 ${approvers}${issueRefs ? ` \xB7 Refs: ${issueRefs}` : ""}_`,
|
|
138
|
+
c.prBody ? `> ${c.prBody.split("\n").filter((l) => l.trim()).slice(0, 2).join(" ").slice(0, 200)}` : "",
|
|
139
|
+
""
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
content: [{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: [...header, ...rows, ...footer].join("\n")
|
|
147
|
+
}]
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
registerChangeTimelineTools
|
|
154
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { trackCall, withTierGate } from "./tools-state.js";
|
|
3
|
+
import { getTierForTool } from "./tool-manifest.js";
|
|
4
|
+
import { requestEphemeralCredential } from "./credential-broker.js";
|
|
5
|
+
import { resolveAgentIdentity } from "./agent-identity.js";
|
|
6
|
+
function renderCredential(cred) {
|
|
7
|
+
const common = [
|
|
8
|
+
`**Provider:** ${cred.provider}`,
|
|
9
|
+
`**Scope:** ${cred.scope}`,
|
|
10
|
+
`**Expires:** ${cred.expiration.toISOString()}`
|
|
11
|
+
];
|
|
12
|
+
switch (cred.provider) {
|
|
13
|
+
case "aws":
|
|
14
|
+
return [
|
|
15
|
+
...common,
|
|
16
|
+
`**Role:** ${cred.roleArn}`,
|
|
17
|
+
"",
|
|
18
|
+
"```",
|
|
19
|
+
`AWS_ACCESS_KEY_ID=${cred.accessKeyId}`,
|
|
20
|
+
`AWS_SECRET_ACCESS_KEY=${cred.secretAccessKey}`,
|
|
21
|
+
`AWS_SESSION_TOKEN=${cred.sessionToken}`,
|
|
22
|
+
"```"
|
|
23
|
+
];
|
|
24
|
+
case "gcp":
|
|
25
|
+
return [
|
|
26
|
+
...common,
|
|
27
|
+
`**Service account:** ${cred.serviceAccountEmail}`,
|
|
28
|
+
"",
|
|
29
|
+
"```",
|
|
30
|
+
`GCP_ACCESS_TOKEN=${cred.gcpAccessToken}`,
|
|
31
|
+
"```"
|
|
32
|
+
];
|
|
33
|
+
case "azure":
|
|
34
|
+
return [
|
|
35
|
+
...common,
|
|
36
|
+
`**App (client) id:** ${cred.clientId}`,
|
|
37
|
+
"",
|
|
38
|
+
// Unlike AWS/GCP, no raw bearer token is handed out here — the real
|
|
39
|
+
// Azure token stays server-side so Mergen can actually stop honoring
|
|
40
|
+
// it at `expiration` (see azure-broker.ts). Route Azure API calls
|
|
41
|
+
// through the proxy path instead of calling Azure directly.
|
|
42
|
+
`**Proxy path:** \`${cred.proxyPath}\``,
|
|
43
|
+
`Route your Azure API calls through this Mergen server at the path above (e.g. \`GET {mergen-server}${cred.proxyPath}/subscriptions/...\`) instead of calling Azure directly \u2014 Mergen forwards the request with the real credential attached, and stops forwarding once this credential expires.`
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function registerCredentialTools(server) {
|
|
48
|
+
server.registerTool(
|
|
49
|
+
"request_credential",
|
|
50
|
+
{
|
|
51
|
+
description: "Request an ephemeral, task-scoped cloud credential (AWS / GCP / Azure) instead of using a long-lived secret. The credential is minted for THIS verified agent, scoped to a named, operator-defined role/service-account/app (configured in ~/.mergen/credential-scopes.json), and expires automatically. Use it right before a cloud operation and let it expire \u2014 never store it. The scope name maps to a provider + target chosen by an operator; you cannot request arbitrary permissions, only a pre-approved scope. Call `get_status` or GET /credentials/scopes to see which scope names are configured. This request passes through the same policy firewall as every other tool \u2014 an operator can require human approval for prod-scoped requests.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
scope: z.string().min(1).describe('The configured scope name to request (e.g. "deploy-write"). Maps to one provider + role/SA/app.'),
|
|
54
|
+
task_id: z.string().min(1).describe("A short identifier for the task this credential is for \u2014 recorded in the audit trail and (AWS) the STS session name."),
|
|
55
|
+
duration_seconds: z.number().int().positive().optional().describe("Requested lifetime in seconds. Clamped to the scope's configured maximum (and the provider's own hard cap).")
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
withTierGate(getTierForTool("request_credential"), async ({ scope, task_id, duration_seconds }) => {
|
|
59
|
+
trackCall("request_credential");
|
|
60
|
+
const { agentId, verified } = resolveAgentIdentity();
|
|
61
|
+
if (!verified || !agentId) {
|
|
62
|
+
return {
|
|
63
|
+
content: [{
|
|
64
|
+
type: "text",
|
|
65
|
+
text: [
|
|
66
|
+
"\u{1F6AB} **Credential request refused \u2014 no verified agent identity.**",
|
|
67
|
+
"",
|
|
68
|
+
"Ephemeral credentials are only issued to a verified agent. Set `MERGEN_AGENT_TOKEN` for this MCP process (see `mergen-server agent-register`) so Mergen can bind the credential to a known identity."
|
|
69
|
+
].join("\n")
|
|
70
|
+
}],
|
|
71
|
+
isError: true
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const cred = await requestEphemeralCredential({
|
|
76
|
+
agentId,
|
|
77
|
+
taskId: task_id,
|
|
78
|
+
scope,
|
|
79
|
+
durationSeconds: duration_seconds,
|
|
80
|
+
// Cloud-mode tenant scoping: MERGEN_TENANT_ID is the process-wide
|
|
81
|
+
// tenant this MCP server serves. Undefined in local/self-hosted mode,
|
|
82
|
+
// which is exactly the "available regardless of tenant" default.
|
|
83
|
+
callerTenantId: process.env.MERGEN_TENANT_ID
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
content: [{
|
|
87
|
+
type: "text",
|
|
88
|
+
text: [
|
|
89
|
+
"## Ephemeral credential issued",
|
|
90
|
+
"",
|
|
91
|
+
...renderCredential(cred),
|
|
92
|
+
"",
|
|
93
|
+
"_Task-scoped and short-lived \u2014 use it now and let it expire. Do not persist it._"
|
|
94
|
+
].join("\n")
|
|
95
|
+
}]
|
|
96
|
+
};
|
|
97
|
+
} catch (err) {
|
|
98
|
+
return {
|
|
99
|
+
content: [{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: [
|
|
102
|
+
"\u{1F6AB} **Credential request failed.**",
|
|
103
|
+
"",
|
|
104
|
+
err instanceof Error ? err.message : String(err),
|
|
105
|
+
"",
|
|
106
|
+
"Check the scope name against GET /credentials/scopes, and confirm the provider-side federation trust (STS role / GCP Workload Identity Pool / Azure federated credential) is configured."
|
|
107
|
+
].join("\n")
|
|
108
|
+
}],
|
|
109
|
+
isError: true
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
registerCredentialTools
|
|
117
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fetchLatestErrorTrace, fetchSpans, isConfigured } from "../datadog/client.js";
|
|
3
|
+
import { compact } from "../datadog/compactor.js";
|
|
4
|
+
import { getActiveIncident } from "../datadog/incident-state.js";
|
|
5
|
+
import { fingerprintFromFact } from "../datadog/fingerprinter.js";
|
|
6
|
+
import { memoryStore, formatMttr } from "../datadog/memory-store.js";
|
|
7
|
+
import { trackCall, withTierGate } from "./tools-state.js";
|
|
8
|
+
import { getTierForTool } from "./tool-manifest.js";
|
|
9
|
+
function buildPatternHeader(fact) {
|
|
10
|
+
const fingerprint = fingerprintFromFact(fact);
|
|
11
|
+
const stats = memoryStore.benchmarkStats(fingerprint);
|
|
12
|
+
const similar = memoryStore.findSimilar(fingerprint, 3);
|
|
13
|
+
if (!stats || stats.occurrences < 2) return "";
|
|
14
|
+
const p50 = stats.p50MttrMs ? formatMttr(stats.p50MttrMs) : "N/A";
|
|
15
|
+
const lastSeen = stats.lastSeenAt ? `${Math.round((Date.now() - stats.lastSeenAt) / 864e5)}d ago` : "unknown";
|
|
16
|
+
const lines = [
|
|
17
|
+
`## Pattern Memory \u2014 seen ${stats.occurrences} time${stats.occurrences !== 1 ? "s" : ""} before`,
|
|
18
|
+
`**p50 MTTR: ${p50}** \xB7 Most common fix: **${stats.topResolutionType}** (${stats.topResolutionCount}/${stats.occurrences}) \xB7 Last seen: ${lastSeen}`,
|
|
19
|
+
`Fingerprint: \`${fingerprint}\``
|
|
20
|
+
];
|
|
21
|
+
const withFix = similar.filter((r) => r.fixPrTitle || r.fixSummary).slice(0, 2);
|
|
22
|
+
if (withFix.length > 0) {
|
|
23
|
+
lines.push("", "**Previous fixes:**");
|
|
24
|
+
for (const r of withFix) {
|
|
25
|
+
const fix = r.fixPrTitle ? `[${r.fixPrTitle}](${r.fixPrUrl ?? "#"})` : r.fixSummary ?? "";
|
|
26
|
+
const when = `${Math.round((Date.now() - r.firedAt) / 864e5)}d ago`;
|
|
27
|
+
lines.push(`- ${when} \xB7 ${r.resolutionType} \xB7 ${fix}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
lines.push("", "---", "");
|
|
31
|
+
return lines.join("\n");
|
|
32
|
+
}
|
|
33
|
+
const NOT_CONFIGURED_MSG = [
|
|
34
|
+
"## Datadog not configured",
|
|
35
|
+
"",
|
|
36
|
+
"Run the following to connect Mergen to Datadog:",
|
|
37
|
+
"```",
|
|
38
|
+
"mergen-server init",
|
|
39
|
+
"```",
|
|
40
|
+
"",
|
|
41
|
+
"Or set environment variables before starting the server:",
|
|
42
|
+
"```",
|
|
43
|
+
"export DD_API_KEY=<your-api-key>",
|
|
44
|
+
"export DD_APP_KEY=<your-app-key>",
|
|
45
|
+
"mergen-server start",
|
|
46
|
+
"```"
|
|
47
|
+
].join("\n");
|
|
48
|
+
function registerGetIncidentContext(server) {
|
|
49
|
+
registerDatadogTools(server, { onlyGetIncidentContext: true });
|
|
50
|
+
}
|
|
51
|
+
function registerDatadogTools(server, opts) {
|
|
52
|
+
server.registerTool(
|
|
53
|
+
"get_incident_context",
|
|
54
|
+
{
|
|
55
|
+
description: "PRIMARY on-call tool. Fetches the current production incident from Datadog, compacts raw trace telemetry (500KB \u2192 1KB Runtime Fact), and maps the error to your local source code so the AI can propose an immediate fix. If a PagerDuty alert fired recently the context is pre-fetched and returned instantly. Call this first whenever a production error is reported.",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
service: z.string().optional().describe(
|
|
58
|
+
'Datadog service name (e.g. "payment-gateway"). Auto-detected from active PagerDuty alert if omitted.'
|
|
59
|
+
),
|
|
60
|
+
since_minutes: z.number().int().min(1).max(60).optional().describe("Look-back window in minutes (default 10)")
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
async ({ service, since_minutes = 10 }) => {
|
|
64
|
+
trackCall("get_incident_context");
|
|
65
|
+
if (!isConfigured()) {
|
|
66
|
+
return { content: [{ type: "text", text: NOT_CONFIGURED_MSG }] };
|
|
67
|
+
}
|
|
68
|
+
const active = getActiveIncident();
|
|
69
|
+
if (active?.runtimeFact) {
|
|
70
|
+
const ageMin = Math.round((Date.now() - active.firedAt) / 6e4);
|
|
71
|
+
const parts = [
|
|
72
|
+
`## Active Incident: ${active.alertTitle}`,
|
|
73
|
+
`*Fired ${ageMin}m ago \xB7 Service: \`${active.service}\`*`,
|
|
74
|
+
active.alertUrl ? `*PagerDuty: ${active.alertUrl}*` : "",
|
|
75
|
+
""
|
|
76
|
+
];
|
|
77
|
+
const blame = active.blameAttribution;
|
|
78
|
+
if (blame?.topCandidate) {
|
|
79
|
+
const sha8 = blame.topCandidate.sha.slice(0, 8);
|
|
80
|
+
const pct = Math.round(blame.confidence * 100);
|
|
81
|
+
const label = blame.confidenceLabel;
|
|
82
|
+
parts.push("### Causal Attribution");
|
|
83
|
+
parts.push(
|
|
84
|
+
`**Deploy \`${sha8}\` \u2014 ${pct}% confidence [${label}]** ` + (blame.lowConfidence ? "\u26A0\uFE0F Below threshold \u2014 investigate before acting." : "")
|
|
85
|
+
);
|
|
86
|
+
parts.push("");
|
|
87
|
+
parts.push("**Signal breakdown:**");
|
|
88
|
+
for (const [name, sig] of Object.entries(blame.signals)) {
|
|
89
|
+
const bar = sig.available ? `${Math.round(sig.score * 100)}%` : "n/a";
|
|
90
|
+
parts.push(`- **${name}** (${bar} \xD7 ${sig.weight} = +${sig.contribution.toFixed(2)}): ${sig.detail}`);
|
|
91
|
+
}
|
|
92
|
+
parts.push("");
|
|
93
|
+
parts.push(`> ${blame.explanation}`);
|
|
94
|
+
if (blame.changedFiles.length > 0) {
|
|
95
|
+
parts.push("");
|
|
96
|
+
parts.push(`**Files in deploy:** ${blame.changedFiles.slice(0, 8).join(", ")}${blame.changedFiles.length > 8 ? ` (+${blame.changedFiles.length - 8} more)` : ""}`);
|
|
97
|
+
}
|
|
98
|
+
if (blame.topCandidate.prUrl) parts.push(`**PR:** ${blame.topCandidate.prUrl}`);
|
|
99
|
+
parts.push("", "---", "");
|
|
100
|
+
}
|
|
101
|
+
parts.push(active.runtimeFact);
|
|
102
|
+
return {
|
|
103
|
+
content: [{
|
|
104
|
+
type: "text",
|
|
105
|
+
text: parts.filter(Boolean).join("\n")
|
|
106
|
+
}]
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const configuredServices = (process.env.MERGEN_SERVICES ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
110
|
+
const targetService = service ?? active?.service;
|
|
111
|
+
if (!targetService && configuredServices.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
content: [{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: [
|
|
116
|
+
"## No active incident detected",
|
|
117
|
+
"",
|
|
118
|
+
"Provide a `service` name to query Datadog directly:",
|
|
119
|
+
"```",
|
|
120
|
+
'get_incident_context(service: "payment-gateway")',
|
|
121
|
+
"```",
|
|
122
|
+
"",
|
|
123
|
+
"Or configure monitored services with:",
|
|
124
|
+
"`MERGEN_SERVICES=payment-gateway,auth-service node dist/index.js`",
|
|
125
|
+
"",
|
|
126
|
+
"Or connect PagerDuty by adding this webhook URL to your PagerDuty service:",
|
|
127
|
+
"`POST http://127.0.0.1:3000/webhooks/pagerduty`",
|
|
128
|
+
"",
|
|
129
|
+
"PagerDuty will then auto-trigger context fetching on every new incident."
|
|
130
|
+
].join("\n")
|
|
131
|
+
}]
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const servicesToTry = targetService ? [targetService] : configuredServices;
|
|
135
|
+
try {
|
|
136
|
+
const to = /* @__PURE__ */ new Date();
|
|
137
|
+
const from = new Date(to.getTime() - since_minutes * 60 * 1e3);
|
|
138
|
+
let result = null;
|
|
139
|
+
let resolvedService = servicesToTry[0] ?? "unknown";
|
|
140
|
+
for (const svc of servicesToTry) {
|
|
141
|
+
const r = await fetchLatestErrorTrace(svc, since_minutes);
|
|
142
|
+
if (r) {
|
|
143
|
+
result = r;
|
|
144
|
+
resolvedService = svc;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const checkedServices = servicesToTry.join(", ");
|
|
149
|
+
if (!result) {
|
|
150
|
+
return {
|
|
151
|
+
content: [{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: `No error traces found for service(s) \`${checkedServices}\` in the last ${since_minutes} minutes.
|
|
154
|
+
|
|
155
|
+
Either the service is healthy or the service name doesn't match Datadog exactly.
|
|
156
|
+
Check your Datadog APM service list at: https://app.datadoghq.com/apm/services
|
|
157
|
+
|
|
158
|
+
` + (servicesToTry.length > 1 ? `Checked ${servicesToTry.length} services. Add more with MERGEN_SERVICES=svc1,svc2.` : `Set MERGEN_SERVICES=svc1,svc2 to auto-scan multiple services.`)
|
|
159
|
+
}]
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const { fact } = await compact({
|
|
163
|
+
spans: result.spans,
|
|
164
|
+
traceId: result.traceId,
|
|
165
|
+
timeWindow: { from, to }
|
|
166
|
+
});
|
|
167
|
+
const patternHeader = buildPatternHeader(fact);
|
|
168
|
+
return { content: [{ type: "text", text: patternHeader + fact.markdown }] };
|
|
169
|
+
} catch (err) {
|
|
170
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
171
|
+
return {
|
|
172
|
+
content: [{ type: "text", text: `Failed to fetch Datadog data: ${msg}` }],
|
|
173
|
+
isError: true
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
if (opts?.onlyGetIncidentContext) return;
|
|
179
|
+
server.registerTool(
|
|
180
|
+
"get_datadog_trace",
|
|
181
|
+
{
|
|
182
|
+
description: "Fetches and compacts a specific Datadog trace by trace ID. Use this when you already have a trace ID from a Datadog URL, a log line, or a Sentry event. Returns the same 1KB Runtime Fact as get_incident_context.",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
trace_id: z.string().describe("Datadog trace ID (from a URL like /apm/traces?query=trace_id:XXX)"),
|
|
185
|
+
service: z.string().optional().describe("Service name to scope the search (improves speed)"),
|
|
186
|
+
since_minutes: z.number().int().min(1).max(60).optional().describe("How far back to look for this trace (default 15 minutes)")
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
withTierGate(getTierForTool("get_datadog_trace"), async ({ trace_id, service, since_minutes = 15 }) => {
|
|
190
|
+
trackCall("get_datadog_trace");
|
|
191
|
+
if (!isConfigured()) {
|
|
192
|
+
return {
|
|
193
|
+
content: [{ type: "text", text: NOT_CONFIGURED_MSG }],
|
|
194
|
+
isError: true
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
const to = /* @__PURE__ */ new Date();
|
|
199
|
+
const from = new Date(to.getTime() - since_minutes * 60 * 1e3);
|
|
200
|
+
const allSpans = await fetchSpans({ service, from, to, limit: 200 });
|
|
201
|
+
const traceSpans = allSpans.filter(
|
|
202
|
+
(s) => s.traceId === trace_id || s.traceId.endsWith(trace_id) || trace_id.endsWith(s.traceId)
|
|
203
|
+
);
|
|
204
|
+
if (traceSpans.length === 0) {
|
|
205
|
+
return {
|
|
206
|
+
content: [{
|
|
207
|
+
type: "text",
|
|
208
|
+
text: `No spans found for trace \`${trace_id}\` in the last ${since_minutes} minutes.
|
|
209
|
+
|
|
210
|
+
Try increasing \`since_minutes\` or verify the trace ID is correct.`
|
|
211
|
+
}]
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const { fact } = await compact({
|
|
215
|
+
spans: traceSpans,
|
|
216
|
+
traceId: trace_id,
|
|
217
|
+
timeWindow: { from, to }
|
|
218
|
+
});
|
|
219
|
+
return { content: [{ type: "text", text: fact.markdown }] };
|
|
220
|
+
} catch (err) {
|
|
221
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
222
|
+
return {
|
|
223
|
+
content: [{ type: "text", text: `Failed to fetch trace: ${msg}` }],
|
|
224
|
+
isError: true
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
export {
|
|
231
|
+
registerDatadogTools,
|
|
232
|
+
registerGetIncidentContext
|
|
233
|
+
};
|