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.
Files changed (308) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +96 -0
  3. package/dist/__stubs__/calibration.js +471 -0
  4. package/dist/__stubs__/causal.js +601 -0
  5. package/dist/__stubs__/closed-source.js +150 -0
  6. package/dist/__stubs__/license.js +35 -0
  7. package/dist/__stubs__/plans.js +197 -0
  8. package/dist/app.js +467 -0
  9. package/dist/cli.js +250 -0
  10. package/dist/commands/agent-identity.js +79 -0
  11. package/dist/commands/gate.js +107 -0
  12. package/dist/commands/github.js +632 -0
  13. package/dist/commands/incident.js +644 -0
  14. package/dist/commands/policy.js +710 -0
  15. package/dist/commands/scan.js +243 -0
  16. package/dist/commands/setup.js +1218 -0
  17. package/dist/commands/shared.js +109 -0
  18. package/dist/commands/team.js +809 -0
  19. package/dist/datadog/blame-attribution.js +228 -0
  20. package/dist/datadog/client.js +159 -0
  21. package/dist/datadog/compactor.js +169 -0
  22. package/dist/datadog/fingerprinter.js +34 -0
  23. package/dist/datadog/incident-state.js +21 -0
  24. package/dist/datadog/line-matcher.js +56 -0
  25. package/dist/datadog/memory-store.js +399 -0
  26. package/dist/datadog/otel-trace.js +67 -0
  27. package/dist/index.js +505 -0
  28. package/dist/intelligence/action-risk.js +132 -0
  29. package/dist/intelligence/activity-feed.js +40 -0
  30. package/dist/intelligence/agent-identity.js +115 -0
  31. package/dist/intelligence/agent-pipeline.js +329 -0
  32. package/dist/intelligence/agent-profiles.js +81 -0
  33. package/dist/intelligence/ai-commit.js +16 -0
  34. package/dist/intelligence/approval-analytics.js +42 -0
  35. package/dist/intelligence/approval-events.js +5 -0
  36. package/dist/intelligence/arch-boundaries.js +94 -0
  37. package/dist/intelligence/arch-graph.js +117 -0
  38. package/dist/intelligence/autonomy.js +397 -0
  39. package/dist/intelligence/azure-broker.js +57 -0
  40. package/dist/intelligence/azure-proxy-sessions.js +49 -0
  41. package/dist/intelligence/baseline.js +1 -0
  42. package/dist/intelligence/behavior-baseline.js +134 -0
  43. package/dist/intelligence/billing.js +1 -0
  44. package/dist/intelligence/blast-radius.js +224 -0
  45. package/dist/intelligence/bypass.js +157 -0
  46. package/dist/intelligence/calibration-classifier.js +77 -0
  47. package/dist/intelligence/calibration-git-sync.js +74 -0
  48. package/dist/intelligence/calibration.js +1 -0
  49. package/dist/intelligence/cascade-detector.js +58 -0
  50. package/dist/intelligence/case-study-generator.js +126 -0
  51. package/dist/intelligence/causal-graph.js +126 -0
  52. package/dist/intelligence/causal.js +1 -0
  53. package/dist/intelligence/change-risk.js +102 -0
  54. package/dist/intelligence/compliance-report.js +134 -0
  55. package/dist/intelligence/confidence-report.js +85 -0
  56. package/dist/intelligence/corpus-to-policy.js +140 -0
  57. package/dist/intelligence/credential-broker.js +112 -0
  58. package/dist/intelligence/credential-misuse.js +39 -0
  59. package/dist/intelligence/debug-sessions.js +108 -0
  60. package/dist/intelligence/decision-escalation.js +36 -0
  61. package/dist/intelligence/decision-memory.js +47 -0
  62. package/dist/intelligence/decision-similarity.js +85 -0
  63. package/dist/intelligence/default-safety-tests.js +53 -0
  64. package/dist/intelligence/degradation-watcher.js +108 -0
  65. package/dist/intelligence/detector-plugins.js +70 -0
  66. package/dist/intelligence/detectors.js +1 -0
  67. package/dist/intelligence/device-auth.js +138 -0
  68. package/dist/intelligence/diff-size.js +42 -0
  69. package/dist/intelligence/ebpf-verify.js +45 -0
  70. package/dist/intelligence/enterprise-policy-engine.js +1122 -0
  71. package/dist/intelligence/error-fingerprint.js +1 -0
  72. package/dist/intelligence/execution-gate.js +100 -0
  73. package/dist/intelligence/execution-mode.js +22 -0
  74. package/dist/intelligence/gate-analytics.js +256 -0
  75. package/dist/intelligence/gate-decision.js +64 -0
  76. package/dist/intelligence/gcp-broker.js +50 -0
  77. package/dist/intelligence/git-adr-sync.js +207 -0
  78. package/dist/intelligence/hitl-hold.js +460 -0
  79. package/dist/intelligence/hypothesis-history.js +93 -0
  80. package/dist/intelligence/idp-identity.js +83 -0
  81. package/dist/intelligence/impl-critic.js +216 -0
  82. package/dist/intelligence/incident-autopilot.js +622 -0
  83. package/dist/intelligence/incident-replay.js +162 -0
  84. package/dist/intelligence/incident-result-cache.js +33 -0
  85. package/dist/intelligence/incident-similarity.js +58 -0
  86. package/dist/intelligence/infra-detectors.js +312 -0
  87. package/dist/intelligence/license.js +185 -0
  88. package/dist/intelligence/llm-spokesperson.js +62 -0
  89. package/dist/intelligence/mcp-prompts.js +320 -0
  90. package/dist/intelligence/mcp-resources.js +171 -0
  91. package/dist/intelligence/normalize.js +108 -0
  92. package/dist/intelligence/notifications.js +83 -0
  93. package/dist/intelligence/oidc-issuer.js +87 -0
  94. package/dist/intelligence/override-corpus.js +597 -0
  95. package/dist/intelligence/planning-gate.js +84 -0
  96. package/dist/intelligence/plans.js +223 -0
  97. package/dist/intelligence/platt-scaling.js +164 -0
  98. package/dist/intelligence/policy-proposals.js +84 -0
  99. package/dist/intelligence/policy-suggester.js +161 -0
  100. package/dist/intelligence/policy-sync.js +103 -0
  101. package/dist/intelligence/policy-test-runner.js +35 -0
  102. package/dist/intelligence/postmortem-parser.js +181 -0
  103. package/dist/intelligence/postmortem-retrieval.js +224 -0
  104. package/dist/intelligence/postmortem-store.js +596 -0
  105. package/dist/intelligence/pr-commenter.js +81 -0
  106. package/dist/intelligence/pr-policy-gate.js +38 -0
  107. package/dist/intelligence/pr-shadow-analyzer.js +229 -0
  108. package/dist/intelligence/process-tree.js +108 -0
  109. package/dist/intelligence/prompts.js +1 -0
  110. package/dist/intelligence/replay.js +38 -0
  111. package/dist/intelligence/repro-steps.js +1 -0
  112. package/dist/intelligence/resource-extractors.js +0 -0
  113. package/dist/intelligence/resource-file-context.js +138 -0
  114. package/dist/intelligence/risk-score.js +22 -0
  115. package/dist/intelligence/rollback.js +137 -0
  116. package/dist/intelligence/runbook-updater.js +143 -0
  117. package/dist/intelligence/sandbox.js +194 -0
  118. package/dist/intelligence/script-trust.js +104 -0
  119. package/dist/intelligence/session-metrics.js +67 -0
  120. package/dist/intelligence/session-threat-tracker.js +231 -0
  121. package/dist/intelligence/shadow-digest-cron.js +81 -0
  122. package/dist/intelligence/shadow-log.js +284 -0
  123. package/dist/intelligence/shell-ast.js +145 -0
  124. package/dist/intelligence/siem-forward.js +104 -0
  125. package/dist/intelligence/slack-digest.js +151 -0
  126. package/dist/intelligence/slack-override-loop.js +217 -0
  127. package/dist/intelligence/slack-routing.js +67 -0
  128. package/dist/intelligence/slack.js +900 -0
  129. package/dist/intelligence/sql-ast.js +31 -0
  130. package/dist/intelligence/team.js +1 -0
  131. package/dist/intelligence/telemetry.js +76 -0
  132. package/dist/intelligence/threshold-optimizer.js +75 -0
  133. package/dist/intelligence/token-budget.js +66 -0
  134. package/dist/intelligence/tool-guard.js +708 -0
  135. package/dist/intelligence/tool-manifest 2.js +74 -0
  136. package/dist/intelligence/tool-manifest.js +125 -0
  137. package/dist/intelligence/tools-analysis.js +857 -0
  138. package/dist/intelligence/tools-arch.js +152 -0
  139. package/dist/intelligence/tools-autonomy.js +495 -0
  140. package/dist/intelligence/tools-blast-radius.js +140 -0
  141. package/dist/intelligence/tools-browser.js +431 -0
  142. package/dist/intelligence/tools-change-timeline.js +154 -0
  143. package/dist/intelligence/tools-credentials.js +117 -0
  144. package/dist/intelligence/tools-datadog.js +233 -0
  145. package/dist/intelligence/tools-debug-sessions.js +209 -0
  146. package/dist/intelligence/tools-discovery.js +284 -0
  147. package/dist/intelligence/tools-infra.js +612 -0
  148. package/dist/intelligence/tools-intent.js +128 -0
  149. package/dist/intelligence/tools-memory.js +514 -0
  150. package/dist/intelligence/tools-runbook.js +951 -0
  151. package/dist/intelligence/tools-sessions.js +107 -0
  152. package/dist/intelligence/tools-state.js +232 -0
  153. package/dist/intelligence/tools-utility.js +430 -0
  154. package/dist/intelligence/tools-validate.js +215 -0
  155. package/dist/intelligence/tools.js +1 -0
  156. package/dist/intelligence/trace-context.js +11 -0
  157. package/dist/intelligence/unclassified-clusters.js +87 -0
  158. package/dist/intelligence/usage.js +195 -0
  159. package/dist/routes/active-authors.js +63 -0
  160. package/dist/routes/activity-feed.js +21 -0
  161. package/dist/routes/adr.js +117 -0
  162. package/dist/routes/agent-activity.js +184 -0
  163. package/dist/routes/agent-blunders.js +163 -0
  164. package/dist/routes/agents.js +175 -0
  165. package/dist/routes/api-keys.js +65 -0
  166. package/dist/routes/arch.js +75 -0
  167. package/dist/routes/audit-export.js +93 -0
  168. package/dist/routes/billing-dashboard.js +158 -0
  169. package/dist/routes/billing-outcome.js +77 -0
  170. package/dist/routes/calibration.js +305 -0
  171. package/dist/routes/ci-gate-history.js +20 -0
  172. package/dist/routes/ci-gate.js +232 -0
  173. package/dist/routes/ci.js +293 -0
  174. package/dist/routes/compliance-report.js +24 -0
  175. package/dist/routes/confidence.js +30 -0
  176. package/dist/routes/credentials.js +119 -0
  177. package/dist/routes/dashboard.js +1407 -0
  178. package/dist/routes/decision-memory.js +42 -0
  179. package/dist/routes/demo.js +844 -0
  180. package/dist/routes/device-auth-stub.js +173 -0
  181. package/dist/routes/explain-why.js +39 -0
  182. package/dist/routes/gate-analytics.js +213 -0
  183. package/dist/routes/gate.js +28 -0
  184. package/dist/routes/github-webhook.js +364 -0
  185. package/dist/routes/habituation.js +30 -0
  186. package/dist/routes/health-integrations.js +243 -0
  187. package/dist/routes/heartbeats.js +45 -0
  188. package/dist/routes/hitl.js +364 -0
  189. package/dist/routes/impact-report.js +645 -0
  190. package/dist/routes/incident-webhook.js +63 -0
  191. package/dist/routes/incidents.js +366 -0
  192. package/dist/routes/layers.js +62 -0
  193. package/dist/routes/license.js +187 -0
  194. package/dist/routes/oidc-issuer.js +30 -0
  195. package/dist/routes/onboarding.js +170 -0
  196. package/dist/routes/otel.js +67 -0
  197. package/dist/routes/otlp-receiver.js +179 -0
  198. package/dist/routes/overrides.js +241 -0
  199. package/dist/routes/pagerduty.js +258 -0
  200. package/dist/routes/policies.js +809 -0
  201. package/dist/routes/policy-nl.js +169 -0
  202. package/dist/routes/postmortem.js +102 -0
  203. package/dist/routes/pr-shadow.js +47 -0
  204. package/dist/routes/rbac.js +61 -0
  205. package/dist/routes/replay.js +17 -0
  206. package/dist/routes/risk-report.js +164 -0
  207. package/dist/routes/runbooks.js +125 -0
  208. package/dist/routes/safety-test.js +174 -0
  209. package/dist/routes/sdk.js +222 -0
  210. package/dist/routes/sensor.js +819 -0
  211. package/dist/routes/sentry.js +140 -0
  212. package/dist/routes/sessions.js +43 -0
  213. package/dist/routes/setup-ui.js +594 -0
  214. package/dist/routes/shadow-report.js +107 -0
  215. package/dist/routes/slack-routing.js +171 -0
  216. package/dist/routes/team-usage.js +71 -0
  217. package/dist/routes/telemetry.js +28 -0
  218. package/dist/routes/tenants.js +199 -0
  219. package/dist/routes/tickets.js +167 -0
  220. package/dist/routes/validate.js +13 -0
  221. package/dist/routes/war-room.js +113 -0
  222. package/dist/scripts/check-arch.js +22 -0
  223. package/dist/seeds/community-corpus.js +176 -0
  224. package/dist/seeds/corpus.js +1224 -0
  225. package/dist/sensor/action-ledger.js +327 -0
  226. package/dist/sensor/adr-store.js +140 -0
  227. package/dist/sensor/agent-blunder-store.js +370 -0
  228. package/dist/sensor/agent-context-store.js +225 -0
  229. package/dist/sensor/agent-memory-store.js +231 -0
  230. package/dist/sensor/audit-fetch.js +23 -0
  231. package/dist/sensor/audit-log.js +273 -0
  232. package/dist/sensor/buffer-schemas.js +225 -0
  233. package/dist/sensor/buffer.js +599 -0
  234. package/dist/sensor/bypass-tracker.js +93 -0
  235. package/dist/sensor/ci-gate-history.js +70 -0
  236. package/dist/sensor/cloud-auth.js +183 -0
  237. package/dist/sensor/commit-context-store.js +235 -0
  238. package/dist/sensor/docker-log-stream.js +164 -0
  239. package/dist/sensor/docker-monitor.js +122 -0
  240. package/dist/sensor/extended-buffer.js +46 -0
  241. package/dist/sensor/feedback-token.js +41 -0
  242. package/dist/sensor/file-lock.js +6 -0
  243. package/dist/sensor/fs-watcher.js +106 -0
  244. package/dist/sensor/gate-heartbeat.js +122 -0
  245. package/dist/sensor/git-suspect.js +136 -0
  246. package/dist/sensor/habituation-store.js +77 -0
  247. package/dist/sensor/heartbeat-monitor.js +133 -0
  248. package/dist/sensor/incident-store.js +340 -0
  249. package/dist/sensor/infra-normalizer.js +513 -0
  250. package/dist/sensor/ingest.js +339 -0
  251. package/dist/sensor/jest-reporter.js +52 -0
  252. package/dist/sensor/k8s-events.js +132 -0
  253. package/dist/sensor/layer2-store.js +111 -0
  254. package/dist/sensor/layer3-store.js +230 -0
  255. package/dist/sensor/layer4-store.js +147 -0
  256. package/dist/sensor/logger.js +13 -0
  257. package/dist/sensor/otel-exporter.js +161 -0
  258. package/dist/sensor/paths.js +71 -0
  259. package/dist/sensor/policy-history.js +147 -0
  260. package/dist/sensor/pr-shadow-store.js +83 -0
  261. package/dist/sensor/process-watcher.js +162 -0
  262. package/dist/sensor/rbac.js +92 -0
  263. package/dist/sensor/redact.js +114 -0
  264. package/dist/sensor/redis-store.js +191 -0
  265. package/dist/sensor/route-reachability.js +64 -0
  266. package/dist/sensor/security-utils.js +18 -0
  267. package/dist/sensor/service-graph.js +170 -0
  268. package/dist/sensor/service-topology.js +227 -0
  269. package/dist/sensor/session-history.js +74 -0
  270. package/dist/sensor/session-persist.js +38 -0
  271. package/dist/sensor/shadow-promote.js +122 -0
  272. package/dist/sensor/sourcemap.js +281 -0
  273. package/dist/sensor/sqlite-store.js +205 -0
  274. package/dist/sensor/sso.js +164 -0
  275. package/dist/sensor/vitest-reporter.js +65 -0
  276. package/dist/sensor/watcher.js +48 -0
  277. package/dist/storage/interfaces.js +0 -0
  278. package/dist/storage/pg/pg-action-ledger.js +138 -0
  279. package/dist/storage/pg/pg-approval-store.js +107 -0
  280. package/dist/storage/pg/pg-blunder-store.js +193 -0
  281. package/dist/storage/pg/pg-ci-gate-history.js +83 -0
  282. package/dist/storage/pg/pg-client.js +24 -0
  283. package/dist/storage/pg/pg-event-store.js +63 -0
  284. package/dist/storage/pg/pg-incident-store.js +190 -0
  285. package/dist/storage/pg/pg-migrations.js +26 -0
  286. package/dist/storage/pg/pg-override-corpus.js +447 -0
  287. package/dist/storage/pg/pg-shadow-log.js +129 -0
  288. package/dist/storage/sqlite/sqlite-action-ledger.js +18 -0
  289. package/dist/storage/sqlite/sqlite-approval-store.js +37 -0
  290. package/dist/storage/sqlite/sqlite-blunder-store.js +27 -0
  291. package/dist/storage/sqlite/sqlite-ci-gate-history.js +19 -0
  292. package/dist/storage/sqlite/sqlite-event-store.js +29 -0
  293. package/dist/storage/sqlite/sqlite-incident-store.js +34 -0
  294. package/dist/storage/sqlite/sqlite-override-corpus.js +75 -0
  295. package/dist/storage/sqlite/sqlite-shadow-log.js +36 -0
  296. package/dist/storage/store-factory.js +55 -0
  297. package/dist/storage/store-registry.js +31 -0
  298. package/dist/update-checker.js +107 -0
  299. package/dist/workers/autopilot-worker.js +28 -0
  300. package/dist/workers/notification-worker.js +28 -0
  301. package/dist/workers/queues.js +58 -0
  302. package/dist/workers/validation-worker.js +24 -0
  303. package/dist/workers/worker-registry.js +18 -0
  304. package/package.json +123 -0
  305. package/sdk/mergen-inject.js +260 -0
  306. package/sdk/node.js +313 -0
  307. package/sdk/vite-plugin.ts +57 -0
  308. package/sdk/webpack-plugin.js +106 -0
@@ -0,0 +1,58 @@
1
+ import { incidentStore } from "../sensor/incident-store.js";
2
+ import { serviceGraph } from "../sensor/service-graph.js";
3
+ import logger from "../sensor/logger.js";
4
+ const CASCADE_WINDOW_MS = 2 * 60 * 1e3;
5
+ function detectCascade(newService, newPid) {
6
+ const now = Date.now();
7
+ const cutoff = now - CASCADE_WINDOW_MS;
8
+ const openIncidents = incidentStore.list("open", 200).filter(
9
+ (inc) => inc.createdAt >= cutoff && inc.pid !== newPid
10
+ );
11
+ if (openIncidents.length === 0) {
12
+ return {
13
+ isCascade: false,
14
+ rootService: newService,
15
+ affectedServices: [newService],
16
+ rootPid: null,
17
+ summary: ""
18
+ };
19
+ }
20
+ const affectedServices = [
21
+ .../* @__PURE__ */ new Set([newService, ...openIncidents.map((inc) => inc.service ?? "unknown")])
22
+ ];
23
+ if (affectedServices.length < 2) {
24
+ return {
25
+ isCascade: false,
26
+ rootService: newService,
27
+ affectedServices,
28
+ rootPid: null,
29
+ summary: ""
30
+ };
31
+ }
32
+ const graphData = serviceGraph.toJSON();
33
+ const edges = graphData.edges ?? [];
34
+ let bestRoot = openIncidents[0]?.service ?? newService;
35
+ let bestUpstreamScore = -1;
36
+ for (const candidate of affectedServices) {
37
+ const downstreamCount = edges.filter(
38
+ (e) => e.source === candidate && affectedServices.includes(e.target)
39
+ ).length;
40
+ if (downstreamCount > bestUpstreamScore) {
41
+ bestUpstreamScore = downstreamCount;
42
+ bestRoot = candidate;
43
+ }
44
+ }
45
+ const rootIncident = openIncidents.filter((inc) => inc.service === bestRoot).sort((a, b) => a.createdAt - b.createdAt)[0];
46
+ const rootPid = rootIncident?.pid ?? null;
47
+ const summary = `\u{1F30A} *Cascade detected* \u2014 ${affectedServices.length} services affected within ${CASCADE_WINDOW_MS / 6e4} minutes.
48
+ Likely root: \`${bestRoot}\` \xB7 Affected: ${affectedServices.map((s) => `\`${s}\``).join(", ")}
49
+ Suppressing redundant HITL \u2014 targeting root service for fix.`;
50
+ logger.info(
51
+ { rootService: bestRoot, affectedServices, rootPid },
52
+ "cascade-detector: cascade detected"
53
+ );
54
+ return { isCascade: true, rootService: bestRoot, affectedServices, rootPid, summary };
55
+ }
56
+ export {
57
+ detectCascade
58
+ };
@@ -0,0 +1,126 @@
1
+ import { createHash } from "crypto";
2
+ import { isNonBlockingBlunderType } from "../sensor/agent-blunder-store.js";
3
+ function anonService(name) {
4
+ if (!name) return "svc-unknown";
5
+ const hash = createHash("sha256").update(name).digest("hex").slice(0, 6);
6
+ return `svc-${hash}`;
7
+ }
8
+ function relativeTime(ts) {
9
+ const diffMs = Date.now() - ts;
10
+ const diffMins = Math.floor(diffMs / 6e4);
11
+ const diffHrs = Math.floor(diffMs / 36e5);
12
+ const diffDays = Math.floor(diffMs / 864e5);
13
+ if (diffMins < 60) return `${diffMins} minute${diffMins !== 1 ? "s" : ""} ago`;
14
+ if (diffHrs < 24) return `${diffHrs} hour${diffHrs !== 1 ? "s" : ""} ago`;
15
+ if (diffDays < 60) return `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
16
+ return `${Math.floor(diffDays / 30)} month${Math.floor(diffDays / 30) !== 1 ? "s" : ""} ago`;
17
+ }
18
+ const CREDENTIAL_PATTERNS = [
19
+ /[A-Za-z0-9+/]{32,}={0,2}/,
20
+ // base64-ish
21
+ /(?:AKIA|ASIA)[A-Z0-9]{16}/,
22
+ // AWS access key
23
+ /(?:password|secret|token|key)\s*[:=]\s*\S+/i,
24
+ /Bearer\s+[A-Za-z0-9._-]+/i
25
+ ];
26
+ function sanitizeCommand(cmd) {
27
+ if (!cmd) return null;
28
+ if (CREDENTIAL_PATTERNS.some((re) => re.test(cmd))) {
29
+ return "[command redacted \u2014 contained potential credential]";
30
+ }
31
+ return cmd.replace(/(?:\/[\w.-]+){3,}/g, (match) => {
32
+ const parts = match.split("/").filter(Boolean);
33
+ return "/" + parts.slice(0, 2).join("/") + "/\u2026";
34
+ });
35
+ }
36
+ const BLUNDER_TYPE_LABELS = {
37
+ allowlist_block: "allowlist policy",
38
+ injection_attempt: "prompt injection detection",
39
+ rbac_block: "RBAC role check",
40
+ override_corpus_block: "override corpus (prior human decision)",
41
+ pipeline_block: "governance pipeline",
42
+ planning_gate_block: "planning gate (blast-radius / confidence check)",
43
+ credential_issued: "ephemeral credential broker",
44
+ policy_integrity_failure: "policy file integrity check (signature/schema fallback)"
45
+ };
46
+ const ALTERNATIVES = {
47
+ allowlist_block: "verify the action is on the approved allowlist, or submit an override request",
48
+ injection_attempt: "rephrase the request without instruction-injection patterns",
49
+ rbac_block: "request elevated permissions through the access-control workflow",
50
+ override_corpus_block: "review the prior override decision and open a HITL approval if the context has changed",
51
+ pipeline_block: "reduce the blast radius of the action or increase diagnostic confidence before retrying",
52
+ planning_gate_block: "narrow the action scope and resubmit once confidence exceeds the configured threshold",
53
+ credential_issued: "not applicable \u2014 this is a successful issuance record, not a blocked action",
54
+ policy_integrity_failure: "restore a validly signed/schema-conformant enterprise-policy.json \u2014 Mergen fell back to the default policy"
55
+ };
56
+ function buildNarrative(anonSvc, cmd, blunderType, blockReason) {
57
+ const cmdPhrase = cmd ? `attempted \`${cmd}\`` : "attempted an unrecorded action";
58
+ const policyLabel = BLUNDER_TYPE_LABELS[blunderType] ?? blunderType;
59
+ const alt = ALTERNATIVES[blunderType] ?? "reformulate the request within policy bounds";
60
+ return `An AI agent targeting ${anonSvc} ${cmdPhrase}. Mergen intercepted the call at the ${policyLabel} layer in <1ms \u2014 the agent received a structured error explaining why the action was blocked (${blockReason.slice(0, 120)}${blockReason.length > 120 ? "\u2026" : ""}) and was guided to ${alt}.`;
61
+ }
62
+ function generateCaseStudies(blunders) {
63
+ const seen = /* @__PURE__ */ new Set();
64
+ const cases = [];
65
+ let seq = 1;
66
+ const sorted = blunders.filter((b) => !isNonBlockingBlunderType(b.blunderType)).sort((a, b) => b.recordedAt - a.recordedAt);
67
+ for (const b of sorted) {
68
+ const anonSvc = anonService(b.service);
69
+ const cmd = sanitizeCommand(b.command);
70
+ const dedupeKey = `${b.blunderType}|${cmd ?? ""}|${anonSvc}`;
71
+ if (seen.has(dedupeKey)) continue;
72
+ seen.add(dedupeKey);
73
+ const caseId = `case-${String(seq++).padStart(3, "0")}`;
74
+ const narrative = buildNarrative(anonSvc, cmd, b.blunderType, b.blockReason);
75
+ cases.push({
76
+ caseId,
77
+ recordedRelative: relativeTime(b.recordedAt),
78
+ blunderType: b.blunderType,
79
+ anonymizedService: anonSvc,
80
+ anonymizedCommand: cmd,
81
+ narrative,
82
+ policyTriggered: BLUNDER_TYPE_LABELS[b.blunderType] ?? b.blunderType,
83
+ alternativeSuggested: ALTERNATIVES[b.blunderType] ?? "reformulate within policy bounds"
84
+ });
85
+ }
86
+ return cases;
87
+ }
88
+ function buildReport(blunders) {
89
+ return {
90
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
91
+ totalBlocks: blunders.filter((b) => !isNonBlockingBlunderType(b.blunderType)).length,
92
+ cases: generateCaseStudies(blunders)
93
+ };
94
+ }
95
+ function renderMarkdown(report) {
96
+ const lines = [
97
+ "# Mergen \u2014 Agent Action Case Studies",
98
+ "",
99
+ `> Generated ${report.generatedAt} `,
100
+ `> ${report.totalBlocks} total agent actions blocked \xB7 ${report.cases.length} unique patterns shown`,
101
+ ""
102
+ ];
103
+ for (const c of report.cases) {
104
+ lines.push(`## ${c.caseId} \u2014 ${c.blunderType.replace(/_/g, " ")}`);
105
+ lines.push("");
106
+ lines.push(`**Recorded:** ${c.recordedRelative} `);
107
+ lines.push(`**Service:** \`${c.anonymizedService}\` `);
108
+ if (c.anonymizedCommand) {
109
+ lines.push(`**Command attempted:** \`${c.anonymizedCommand}\` `);
110
+ }
111
+ lines.push(`**Policy triggered:** ${c.policyTriggered} `);
112
+ lines.push("");
113
+ lines.push(c.narrative);
114
+ lines.push("");
115
+ lines.push(`**Agent was guided to:** ${c.alternativeSuggested}`);
116
+ lines.push("");
117
+ lines.push("---");
118
+ lines.push("");
119
+ }
120
+ return lines.join("\n");
121
+ }
122
+ export {
123
+ buildReport,
124
+ generateCaseStudies,
125
+ renderMarkdown
126
+ };
@@ -0,0 +1,126 @@
1
+ import { createHash } from "crypto";
2
+ function stableId(kind, ts, label) {
3
+ const h = createHash("sha256").update(`${kind}:${ts}:${label}`).digest("hex").slice(0, 8);
4
+ return `${kind}:${ts}:${h}`;
5
+ }
6
+ function toNodeKind(kind) {
7
+ switch (kind) {
8
+ case "error":
9
+ return "error";
10
+ case "warn":
11
+ return "warn";
12
+ case "network_fail":
13
+ return "network_fail";
14
+ case "network_ok":
15
+ return "network_ok";
16
+ case "process_exit":
17
+ return "process_exit";
18
+ case "process_log":
19
+ return "process_log";
20
+ default:
21
+ return "state";
22
+ }
23
+ }
24
+ const NETWORK_CAUSE_TAGS = /* @__PURE__ */ new Set([
25
+ "auth_token_not_persisted",
26
+ "token_overwrite_race",
27
+ "failed_request_caused_crash",
28
+ "null_storage_key",
29
+ "empty_network_response"
30
+ ]);
31
+ function buildCausalGraph(chain) {
32
+ const nodes = [];
33
+ const edges = [];
34
+ for (const ev of chain.chain) {
35
+ const kind = toNodeKind(ev.kind);
36
+ const id = stableId(kind, ev.ts, ev.summary);
37
+ const meta = {};
38
+ if (ev.detail) meta.detail = ev.detail;
39
+ if (ev.source) meta.source = ev.source;
40
+ if (kind === "error") {
41
+ const errBlock = chain.errors.find((e) => e.timestamp === ev.ts);
42
+ if (errBlock?.primaryFrame) meta.primaryFrame = errBlock.primaryFrame;
43
+ if (errBlock?.resolvedStack) meta.resolvedStack = errBlock.resolvedStack.slice(0, 600);
44
+ }
45
+ nodes.push({ id, kind, ts: ev.ts, isoTs: ev.isoTs, label: ev.summary.slice(0, 120), metadata: meta });
46
+ }
47
+ if (nodes.length === 0) {
48
+ return { capturedAt: chain.capturedAt, nodes, edges, rootNodes: [], terminalNodes: [], hypotheses: [] };
49
+ }
50
+ for (let i = 1; i < nodes.length; i++) {
51
+ edges.push({ from: nodes[i - 1].id, to: nodes[i].id, kind: "PRECEDED_BY", confidence: 1 });
52
+ }
53
+ const errorNodes = nodes.filter((n) => n.kind === "error");
54
+ const networkFailNodes = nodes.filter((n) => n.kind === "network_fail");
55
+ const stateNodes = nodes.filter((n) => n.kind === "state");
56
+ for (const err of errorNodes) {
57
+ for (const net of networkFailNodes) {
58
+ const msBefore = err.ts - net.ts;
59
+ if (msBefore < 0 || msBefore > 3e4) continue;
60
+ edges.push({
61
+ from: net.id,
62
+ to: err.id,
63
+ kind: "CORRELATED_WITH",
64
+ confidence: parseFloat(Math.max(0.3, 1 - msBefore / 3e4).toFixed(2)),
65
+ metadata: { msBefore }
66
+ });
67
+ }
68
+ }
69
+ for (const netCall of chain.correlatedNetwork) {
70
+ if (!netCall.traceId) continue;
71
+ const urlSlug = netCall.url.slice(0, 70);
72
+ const matchNode = networkFailNodes.find((n) => n.label.includes(urlSlug));
73
+ if (!matchNode) continue;
74
+ for (const err of errorNodes) {
75
+ const edge = edges.find((e) => e.from === matchNode.id && e.to === err.id && e.kind === "CORRELATED_WITH");
76
+ if (edge) {
77
+ edge.kind = "TRACE_JOINED";
78
+ edge.confidence = 1;
79
+ edge.metadata = { ...edge.metadata, traceId: netCall.traceId };
80
+ }
81
+ }
82
+ }
83
+ for (const err of errorNodes) {
84
+ for (const state of stateNodes) {
85
+ if (Math.abs(err.ts - state.ts) <= 5e3) {
86
+ edges.push({ from: state.id, to: err.id, kind: "STATE_AT", confidence: 0.9 });
87
+ }
88
+ }
89
+ }
90
+ for (const h of chain.hypotheses) {
91
+ if (!NETWORK_CAUSE_TAGS.has(h.tag)) continue;
92
+ if (h.confidenceScore < 0.55) continue;
93
+ const best = edges.filter((e) => e.kind === "CORRELATED_WITH" && nodes.find((n) => n.id === e.from)?.kind === "network_fail").sort((a, b) => b.confidence - a.confidence)[0];
94
+ if (best) {
95
+ best.kind = "CAUSED_BY";
96
+ best.confidence = parseFloat(h.confidenceScore.toFixed(2));
97
+ best.metadata = { ...best.metadata, hypothesis: h.tag, fixHint: h.fixHint ?? void 0 };
98
+ }
99
+ }
100
+ const hasIncomingCausal = new Set(
101
+ edges.filter((e) => e.kind !== "PRECEDED_BY").map((e) => e.to)
102
+ );
103
+ const rootNodes = nodes.filter((n) => !hasIncomingCausal.has(n.id)).map((n) => n.id);
104
+ const terminalNodes = nodes.filter((n) => n.kind === "error" || n.kind === "process_exit").map((n) => n.id);
105
+ return {
106
+ capturedAt: chain.capturedAt,
107
+ nodes,
108
+ edges,
109
+ rootNodes,
110
+ terminalNodes,
111
+ hypotheses: chain.hypotheses.map((h) => ({
112
+ tag: h.tag,
113
+ summary: h.summary,
114
+ confidence: h.confidence,
115
+ confidenceScore: h.confidenceScore,
116
+ causalPath: h.causalPath,
117
+ fixHint: h.fixHint,
118
+ evidence: h.evidence,
119
+ pid: h.pid
120
+ })),
121
+ errorFingerprint: chain.errorFingerprint
122
+ };
123
+ }
124
+ export {
125
+ buildCausalGraph
126
+ };
@@ -0,0 +1 @@
1
+ export * from '../__stubs__/causal.js';
@@ -0,0 +1,102 @@
1
+ import path from "path";
2
+ import { buildGraph, getZone } from "./arch-graph.js";
3
+ const CRITICAL_ENTRY_POINTS = /* @__PURE__ */ new Set(["app.ts", "index.ts", "cli.ts"]);
4
+ function scoreChangeRisk(filesModified, srcDir) {
5
+ const graph = buildGraph(srcDir);
6
+ const factors = [];
7
+ let score = 0;
8
+ const fileCount = filesModified.length;
9
+ const fileDelta = fileCount * 8;
10
+ score += fileDelta;
11
+ factors.push({ label: "Files modified", delta: fileDelta, detail: `${fileCount} file(s) \xD7 8` });
12
+ const zones = /* @__PURE__ */ new Set();
13
+ for (const f of filesModified) zones.add(getZone(f));
14
+ zones.delete("other");
15
+ const subsystems = [...zones];
16
+ if (subsystems.length > 1) {
17
+ const delta = (subsystems.length - 1) * 15;
18
+ score += delta;
19
+ factors.push({ label: "Cross-subsystem change", delta, detail: `${subsystems.length} subsystems: ${subsystems.join(", ")}` });
20
+ }
21
+ const publicApiFiles = filesModified.filter((f) => getZone(f) === "routes");
22
+ if (publicApiFiles.length > 0) {
23
+ const delta = publicApiFiles.length * 20;
24
+ score += delta;
25
+ factors.push({ label: "Public API changes", delta, detail: `${publicApiFiles.length} route file(s)` });
26
+ }
27
+ const criticalEntryPoints = filesModified.filter((f) => CRITICAL_ENTRY_POINTS.has(path.basename(f)));
28
+ if (criticalEntryPoints.length > 0) {
29
+ const delta = criticalEntryPoints.length * 25;
30
+ score += delta;
31
+ factors.push({ label: "Critical entry points", delta, detail: criticalEntryPoints.map((f) => path.basename(f)).join(", ") });
32
+ }
33
+ const affectedTestFiles = /* @__PURE__ */ new Set();
34
+ for (const f of filesModified) {
35
+ const dependents = graph.reverse.get(f) ?? /* @__PURE__ */ new Set();
36
+ for (const dep of dependents) {
37
+ if (dep.endsWith(".test.ts")) affectedTestFiles.add(dep);
38
+ }
39
+ }
40
+ const testCount = affectedTestFiles.size;
41
+ if (testCount > 0) {
42
+ const delta = testCount * 5;
43
+ score += delta;
44
+ factors.push({ label: "Tests affected", delta, detail: `${testCount} test file(s) will need verification` });
45
+ }
46
+ const untestedFiles = filesModified.filter((f) => {
47
+ if (f.endsWith(".test.ts") || f.endsWith(".d.ts")) return false;
48
+ const importers = graph.reverse.get(f) ?? /* @__PURE__ */ new Set();
49
+ return ![...importers].some((imp) => imp.endsWith(".test.ts"));
50
+ });
51
+ if (untestedFiles.length > 0) {
52
+ const delta = untestedFiles.length * 10;
53
+ score += delta;
54
+ factors.push({ label: "Untested files", delta, detail: `${untestedFiles.length} file(s) have no test coverage` });
55
+ }
56
+ const clampedScore = Math.min(100, score);
57
+ const level = clampedScore >= 70 ? "HIGH" : clampedScore >= 40 ? "MEDIUM" : "LOW";
58
+ const requiresApproval = clampedScore >= 70;
59
+ const recommendation = requiresApproval ? "HIGH RISK: Get explicit human sign-off before proceeding. File a confidence report and rollback plan first." : level === "MEDIUM" ? "MEDIUM RISK: File a confidence report (report_confidence) and rollback plan (plan_rollback) before proceeding." : "LOW RISK: Proceed. File a confidence report if confidence < 0.8.";
60
+ return {
61
+ score: clampedScore,
62
+ level,
63
+ requiresApproval,
64
+ files: fileCount,
65
+ subsystems,
66
+ publicApiFiles,
67
+ criticalEntryPoints,
68
+ testsAffected: testCount,
69
+ untestedFiles,
70
+ factors,
71
+ recommendation
72
+ };
73
+ }
74
+ function formatRiskReport(r) {
75
+ const badge = r.level === "HIGH" ? "\u{1F534} HIGH" : r.level === "MEDIUM" ? "\u{1F7E1} MEDIUM" : "\u{1F7E2} LOW";
76
+ const lines = [
77
+ `## Change Risk Score: ${r.score}/100 \u2014 ${badge}`,
78
+ "",
79
+ r.requiresApproval ? "> \u26D4 **Human approval required** before making these changes." : r.level === "MEDIUM" ? "> \u26A0 **Review recommended.** File confidence report and rollback plan first." : "> \u2705 Safe to proceed.",
80
+ "",
81
+ `**Files:** ${r.files} | **Subsystems:** ${r.subsystems.join(", ") || "none"} | **Tests affected:** ${r.testsAffected}`,
82
+ "",
83
+ "### Risk factors",
84
+ ""
85
+ ];
86
+ for (const f of r.factors) {
87
+ lines.push(`| +${f.delta} | **${f.label}** | ${f.detail} |`);
88
+ }
89
+ if (r.factors.length === 0) lines.push("*No significant risk factors.*");
90
+ lines.push("");
91
+ if (r.untestedFiles.length > 0) {
92
+ lines.push("### Untested files (add coverage before proceeding)", "");
93
+ for (const f of r.untestedFiles) lines.push(` - \`${path.basename(f)}\``);
94
+ lines.push("");
95
+ }
96
+ lines.push(`**Recommendation:** ${r.recommendation}`);
97
+ return lines.join("\n");
98
+ }
99
+ export {
100
+ formatRiskReport,
101
+ scoreChangeRisk
102
+ };
@@ -0,0 +1,134 @@
1
+ import fs from "fs";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { loadEnterprisePolicy, IMMUTABLE_RULE_IDS } from "./enterprise-policy-engine.js";
5
+ import { listMembers } from "../sensor/rbac.js";
6
+ import { zeroRetentionMode } from "../sensor/paths.js";
7
+ import { fetchBlunderEntries, fetchHttpAuditEntries, fetchChainVerification } from "../sensor/audit-fetch.js";
8
+ async function buildComplianceReport(from, to) {
9
+ const [blunders, httpEntries, chainVerification] = await Promise.all([
10
+ fetchBlunderEntries(from, to),
11
+ fetchHttpAuditEntries(from, to),
12
+ fetchChainVerification()
13
+ ]);
14
+ const policy = loadEnterprisePolicy();
15
+ const members = listMembers();
16
+ const blockedByType = {};
17
+ for (const b of blunders) {
18
+ blockedByType[b.blunderType] = (blockedByType[b.blunderType] ?? 0) + 1;
19
+ }
20
+ const customPiiConfigPresent = fs.existsSync(join(homedir(), ".mergen", "pii-config.json"));
21
+ return {
22
+ generatedAt: Date.now(),
23
+ windowFrom: from,
24
+ windowTo: to,
25
+ security: {
26
+ policyRulesActive: policy.rules.length,
27
+ immutableRulesActive: policy.rules.filter((r) => IMMUTABLE_RULE_IDS.has(r.id)).length,
28
+ policyEnabled: policy.enabled,
29
+ blockedActionsInWindow: blunders.length,
30
+ blockedActionsByType: blockedByType,
31
+ auditChain: {
32
+ valid: chainVerification.valid,
33
+ verified: chainVerification.verified ?? 0,
34
+ truncated: chainVerification.truncated ?? false,
35
+ tamperEvidenceLevel: chainVerification.tamperEvidenceLevel ?? "unknown",
36
+ hmacProtected: chainVerification.hmacProtected ?? false
37
+ },
38
+ rbac: {
39
+ totalMembers: members.length,
40
+ admins: members.filter((m) => m.role === "admin").length,
41
+ responders: members.filter((m) => m.role === "responder").length,
42
+ viewers: members.filter((m) => m.role === "viewer").length
43
+ }
44
+ },
45
+ confidentiality: {
46
+ zeroRetentionMode: zeroRetentionMode(),
47
+ // Always-on regex PII redaction (email, phone, AWS keys, PEM certs, JWTs,
48
+ // credit cards) — not configurable off, per this product's own design.
49
+ piiShieldActive: true,
50
+ customPiiConfigPresent
51
+ },
52
+ availability: {
53
+ applicable: false,
54
+ note: "Not applicable \u2014 Mergen runs on customer infrastructure and does not host customer production systems. Uptime/DR controls for the systems Mergen governs are the customer's own responsibility."
55
+ },
56
+ httpAuditEntryCount: httpEntries.length
57
+ };
58
+ }
59
+ function esc(s) {
60
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
61
+ }
62
+ function renderComplianceHtml(report) {
63
+ const fmt = (ts) => new Date(ts).toISOString();
64
+ const typeRows = Object.entries(report.security.blockedActionsByType).sort((a, b) => b[1] - a[1]).map(([type, count]) => `<tr><td>${esc(type)}</td><td>${count}</td></tr>`).join("");
65
+ return `<!DOCTYPE html>
66
+ <html lang="en">
67
+ <head>
68
+ <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
69
+ <title>Mergen Compliance Report</title>
70
+ <style>
71
+ :root{--bg:#0f1117;--surface:#1a1d26;--border:#2a2d3a;--text:#e2e8f0;--muted:#64748b;
72
+ --green:#22c55e;--yellow:#f59e0b;--red:#ef4444;--blue:#3b82f6;}
73
+ *{box-sizing:border-box}
74
+ body{background:var(--bg);color:var(--text);font-family:-apple-system,ui-sans-serif,sans-serif;font-size:14px;line-height:1.6;margin:0;padding:32px;max-width:860px;margin:0 auto}
75
+ h1{font-size:22px;margin:0 0 4px}
76
+ .sub{color:var(--muted);font-size:13px;margin-bottom:28px}
77
+ section{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:20px 24px;margin-bottom:20px}
78
+ h2{font-size:15px;margin:0 0 12px;letter-spacing:.02em}
79
+ table{width:100%;border-collapse:collapse;font-size:13px}
80
+ td{padding:6px 8px;border-bottom:1px solid var(--border)}
81
+ td:first-child{color:var(--muted)}
82
+ td:last-child{text-align:right;font-weight:600}
83
+ .badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700}
84
+ .badge.ok{background:rgba(34,197,94,.15);color:var(--green)}
85
+ .badge.warn{background:rgba(245,158,11,.15);color:var(--yellow)}
86
+ .badge.bad{background:rgba(239,68,68,.15);color:var(--red)}
87
+ .na{color:var(--muted);font-style:italic}
88
+ footer{color:var(--muted);font-size:11px;margin-top:24px}
89
+ @media print{body{padding:0}}
90
+ </style>
91
+ </head>
92
+ <body>
93
+ <h1>Mergen Compliance Report</h1>
94
+ <div class="sub">Generated ${esc(fmt(report.generatedAt))} \xB7 Window ${esc(fmt(report.windowFrom))} \u2192 ${esc(fmt(report.windowTo))}</div>
95
+
96
+ <section>
97
+ <h2>Security</h2>
98
+ <table>
99
+ <tr><td>Policy enabled</td><td>${report.security.policyEnabled ? '<span class="badge ok">Yes</span>' : '<span class="badge bad">No</span>'}</td></tr>
100
+ <tr><td>Active policy rules</td><td>${report.security.policyRulesActive} (${report.security.immutableRulesActive} immutable)</td></tr>
101
+ <tr><td>Blocked actions in window</td><td>${report.security.blockedActionsInWindow}</td></tr>
102
+ <tr><td>Audit chain</td><td>${report.security.auditChain.valid ? '<span class="badge ok">Valid</span>' : '<span class="badge bad">INVALID</span>'}</td></tr>
103
+ <tr><td>Tamper-evidence level</td><td>${report.security.auditChain.tamperEvidenceLevel === "hmac-sealed" ? '<span class="badge ok">hmac-sealed</span>' : report.security.auditChain.tamperEvidenceLevel === "hash-chain" ? '<span class="badge warn">hash-chain only</span>' : '<span class="badge bad">none</span>'}</td></tr>
104
+ <tr><td>Chain truncated (ring buffer wrapped)</td><td>${report.security.auditChain.truncated ? "Yes" : "No"}</td></tr>
105
+ <tr><td>RBAC members</td><td>${report.security.rbac.totalMembers} (${report.security.rbac.admins} admin, ${report.security.rbac.responders} responder, ${report.security.rbac.viewers} viewer)</td></tr>
106
+ </table>
107
+ ${typeRows ? `<h2 style="margin-top:20px">Blocked actions by type</h2><table>${typeRows}</table>` : ""}
108
+ </section>
109
+
110
+ <section>
111
+ <h2>Confidentiality</h2>
112
+ <table>
113
+ <tr><td>Zero-retention mode</td><td>${report.confidentiality.zeroRetentionMode ? '<span class="badge ok">Enabled</span>' : "Disabled"}</td></tr>
114
+ <tr><td>PII shield (always-on)</td><td><span class="badge ok">Active</span></td></tr>
115
+ <tr><td>Custom PII patterns configured</td><td>${report.confidentiality.customPiiConfigPresent ? "Yes" : "No (default patterns only)"}</td></tr>
116
+ </table>
117
+ </section>
118
+
119
+ <section>
120
+ <h2>Availability</h2>
121
+ <p class="na">${esc(report.availability.note)}</p>
122
+ </section>
123
+
124
+ <footer>
125
+ Control-to-criteria mapping: docs/enterprise-security.md. Raw tamper-evident audit data: GET /audit/export?format=soc2.
126
+ Print this page to PDF for distribution \u2014 no server-side PDF generation is used.
127
+ </footer>
128
+ </body>
129
+ </html>`;
130
+ }
131
+ export {
132
+ buildComplianceReport,
133
+ renderComplianceHtml
134
+ };
@@ -0,0 +1,85 @@
1
+ import { z } from "zod";
2
+ const ConfidenceReportSchema = z.object({
3
+ /** Unique ID assigned on creation. */
4
+ id: z.string().optional(),
5
+ /** 0.0 (no confidence) to 1.0 (fully certain). */
6
+ confidence: z.number().min(0).max(1),
7
+ /** What this report covers — e.g. "Add Stripe webhook handler" */
8
+ scope: z.string().min(1).max(300),
9
+ /** Things the agent is treating as true without verifying. */
10
+ assumptions: z.array(z.string().max(500)).default([]),
11
+ /** Open questions that could affect correctness. */
12
+ unknowns: z.array(z.string().max(500)).default([]),
13
+ /** Source files this change will touch. */
14
+ filesModified: z.array(z.string().max(500)).default([]),
15
+ /** Human-readable explanation for the confidence score. */
16
+ rationale: z.string().max(1e3).default("")
17
+ });
18
+ function confidenceLabel(score) {
19
+ if (score >= 0.9) return "HIGH";
20
+ if (score >= 0.75) return "MEDIUM-HIGH";
21
+ if (score >= 0.6) return "MEDIUM";
22
+ if (score >= 0.4) return "LOW-MEDIUM";
23
+ return "LOW";
24
+ }
25
+ class ConfidenceStore {
26
+ MAX = 100;
27
+ reports = [];
28
+ counter = 0;
29
+ add(input) {
30
+ this.counter += 1;
31
+ const report = {
32
+ ...input,
33
+ id: `CR-${String(this.counter).padStart(4, "0")}`,
34
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
35
+ };
36
+ this.reports.push(report);
37
+ if (this.reports.length > this.MAX) this.reports.shift();
38
+ return report;
39
+ }
40
+ list(limit = 20) {
41
+ return this.reports.slice(-limit).reverse();
42
+ }
43
+ get(id) {
44
+ return this.reports.find((r) => r.id === id);
45
+ }
46
+ }
47
+ const confidenceStore = new ConfidenceStore();
48
+ function formatConfidenceReport(r) {
49
+ const label = confidenceLabel(r.confidence);
50
+ const pct = Math.round(r.confidence * 100);
51
+ const lines = [
52
+ `## Confidence Report ${r.id}`,
53
+ "",
54
+ `**Scope:** ${r.scope}`,
55
+ `**Confidence:** ${pct}% (${label}) | **Filed:** ${r.createdAt}`,
56
+ ""
57
+ ];
58
+ if (r.rationale) {
59
+ lines.push(`**Rationale:** ${r.rationale}`, "");
60
+ }
61
+ if (r.assumptions.length > 0) {
62
+ lines.push("**Assumptions:**");
63
+ for (const a of r.assumptions) lines.push(` - ${a}`);
64
+ lines.push("");
65
+ }
66
+ if (r.unknowns.length > 0) {
67
+ lines.push("**Unknowns:**");
68
+ for (const u of r.unknowns) lines.push(` - ${u}`);
69
+ lines.push("");
70
+ }
71
+ if (r.filesModified.length > 0) {
72
+ lines.push("**Files to modify:**");
73
+ for (const f of r.filesModified) lines.push(` - \`${f}\``);
74
+ lines.push("");
75
+ }
76
+ if (r.confidence < 0.6) {
77
+ lines.push("> \u26A0 **Low confidence** \u2014 resolve unknowns before proceeding or halt and present an impact report.");
78
+ }
79
+ return lines.join("\n");
80
+ }
81
+ export {
82
+ ConfidenceReportSchema,
83
+ confidenceStore,
84
+ formatConfidenceReport
85
+ };