log10x-mcp 1.12.1 → 1.12.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.
Files changed (307) hide show
  1. package/build/index.js +239 -79
  2. package/build/index.js.map +1 -1
  3. package/build/lib/action-intent-parser.d.ts +1 -1
  4. package/build/lib/action-intent-parser.js +1 -1
  5. package/build/lib/advisor/reporter-forwarders.js +4 -4
  6. package/build/lib/advisor/reporter-forwarders.js.map +1 -1
  7. package/build/lib/anchor-dispersion.d.ts +7 -7
  8. package/build/lib/anchor-dispersion.js +7 -7
  9. package/build/lib/anchor-promql.d.ts +35 -0
  10. package/build/lib/anchor-promql.js +44 -0
  11. package/build/lib/anchor-promql.js.map +1 -0
  12. package/build/lib/anchor-suggestions.d.ts +58 -0
  13. package/build/lib/anchor-suggestions.js +87 -0
  14. package/build/lib/anchor-suggestions.js.map +1 -0
  15. package/build/lib/anti-jargon-prose.d.ts +92 -0
  16. package/build/lib/anti-jargon-prose.js +243 -0
  17. package/build/lib/anti-jargon-prose.js.map +1 -0
  18. package/build/lib/api.d.ts +0 -6
  19. package/build/lib/api.js +11 -29
  20. package/build/lib/api.js.map +1 -1
  21. package/build/lib/cap-csv-fetch.d.ts +27 -0
  22. package/build/lib/cap-csv-fetch.js +93 -1
  23. package/build/lib/cap-csv-fetch.js.map +1 -1
  24. package/build/lib/chassis-envelope.d.ts +178 -53
  25. package/build/lib/chassis-envelope.js +131 -11
  26. package/build/lib/chassis-envelope.js.map +1 -1
  27. package/build/lib/cli-output-parser.js +4 -4
  28. package/build/lib/cli-output-parser.js.map +1 -1
  29. package/build/lib/correlate.js +3 -3
  30. package/build/lib/correlate.js.map +1 -1
  31. package/build/lib/cost.d.ts +3 -3
  32. package/build/lib/cost.js +11 -6
  33. package/build/lib/cost.js.map +1 -1
  34. package/build/lib/customer-metrics.d.ts +24 -2
  35. package/build/lib/customer-metrics.js +23 -13
  36. package/build/lib/customer-metrics.js.map +1 -1
  37. package/build/lib/dev-cli.d.ts +1 -1
  38. package/build/lib/dev-cli.js +1 -1
  39. package/build/lib/drift.js +30 -11
  40. package/build/lib/drift.js.map +1 -1
  41. package/build/lib/env-alias-bridge.d.ts +55 -0
  42. package/build/lib/env-alias-bridge.js +133 -0
  43. package/build/lib/env-alias-bridge.js.map +1 -0
  44. package/build/lib/env-config/env-var-bridge.d.ts +53 -0
  45. package/build/lib/env-config/env-var-bridge.js +180 -0
  46. package/build/lib/env-config/env-var-bridge.js.map +1 -0
  47. package/build/lib/env-config/resolve-cluster-config.d.ts +158 -0
  48. package/build/lib/env-config/resolve-cluster-config.js +239 -0
  49. package/build/lib/env-config/resolve-cluster-config.js.map +1 -0
  50. package/build/lib/env-config/resolver.d.ts +62 -0
  51. package/build/lib/env-config/resolver.js +184 -0
  52. package/build/lib/env-config/resolver.js.map +1 -0
  53. package/build/lib/env-config/store-aws-ssm.d.ts +90 -0
  54. package/build/lib/env-config/store-aws-ssm.js +267 -0
  55. package/build/lib/env-config/store-aws-ssm.js.map +1 -0
  56. package/build/lib/env-config/store-azure-ac.d.ts +62 -0
  57. package/build/lib/env-config/store-azure-ac.js +214 -0
  58. package/build/lib/env-config/store-azure-ac.js.map +1 -0
  59. package/build/lib/env-config/store-gcp-sm.d.ts +54 -0
  60. package/build/lib/env-config/store-gcp-sm.js +306 -0
  61. package/build/lib/env-config/store-gcp-sm.js.map +1 -0
  62. package/build/lib/env-config/store-interface.d.ts +50 -0
  63. package/build/lib/env-config/store-interface.js +16 -0
  64. package/build/lib/env-config/store-interface.js.map +1 -0
  65. package/build/lib/env-config/store-k8s.d.ts +144 -0
  66. package/build/lib/env-config/store-k8s.js +483 -0
  67. package/build/lib/env-config/store-k8s.js.map +1 -0
  68. package/build/lib/env-config/store-local-file.d.ts +52 -0
  69. package/build/lib/env-config/store-local-file.js +124 -0
  70. package/build/lib/env-config/store-local-file.js.map +1 -0
  71. package/build/lib/env-config/stores.d.ts +14 -0
  72. package/build/lib/env-config/stores.js +40 -0
  73. package/build/lib/env-config/stores.js.map +1 -0
  74. package/build/lib/env-config/types.d.ts +551 -0
  75. package/build/lib/env-config/types.js +119 -0
  76. package/build/lib/env-config/types.js.map +1 -0
  77. package/build/lib/environments.d.ts +9 -0
  78. package/build/lib/environments.js +55 -4
  79. package/build/lib/environments.js.map +1 -1
  80. package/build/lib/format.d.ts +8 -7
  81. package/build/lib/format.js +23 -16
  82. package/build/lib/format.js.map +1 -1
  83. package/build/lib/investigation-templates.d.ts +1 -1
  84. package/build/lib/investigation-templates.js +24 -4
  85. package/build/lib/investigation-templates.js.map +1 -1
  86. package/build/lib/line-chart.js +3 -3
  87. package/build/lib/line-chart.js.map +1 -1
  88. package/build/lib/mode-detect.js +8 -0
  89. package/build/lib/mode-detect.js.map +1 -1
  90. package/build/lib/not-configured.d.ts +2 -2
  91. package/build/lib/not-configured.js +2 -2
  92. package/build/lib/offload-status.d.ts +2 -2
  93. package/build/lib/offload-status.js +3 -3
  94. package/build/lib/offload-status.js.map +1 -1
  95. package/build/lib/output-types.d.ts +126 -40
  96. package/build/lib/output-types.js +12 -2
  97. package/build/lib/output-types.js.map +1 -1
  98. package/build/lib/pattern-label.d.ts +58 -0
  99. package/build/lib/pattern-label.js +67 -0
  100. package/build/lib/pattern-label.js.map +1 -0
  101. package/build/lib/pattern-reference-resolver.d.ts +82 -0
  102. package/build/lib/pattern-reference-resolver.js +99 -0
  103. package/build/lib/pattern-reference-resolver.js.map +1 -0
  104. package/build/lib/poc-envelope-v2.js +1 -1
  105. package/build/lib/policy-loader.js +37 -22
  106. package/build/lib/policy-loader.js.map +1 -1
  107. package/build/lib/primitive-errors.d.ts +5 -6
  108. package/build/lib/primitive-errors.js +15 -5
  109. package/build/lib/primitive-errors.js.map +1 -1
  110. package/build/lib/product-kb/chunker.d.ts +47 -0
  111. package/build/lib/product-kb/chunker.js +196 -0
  112. package/build/lib/product-kb/chunker.js.map +1 -0
  113. package/build/lib/product-kb/index.d.ts +56 -0
  114. package/build/lib/product-kb/index.js +88 -0
  115. package/build/lib/product-kb/index.js.map +1 -0
  116. package/build/lib/product-kb/loader.d.ts +54 -0
  117. package/build/lib/product-kb/loader.js +249 -0
  118. package/build/lib/product-kb/loader.js.map +1 -0
  119. package/build/lib/product-kb/search.d.ts +149 -0
  120. package/build/lib/product-kb/search.js +373 -0
  121. package/build/lib/product-kb/search.js.map +1 -0
  122. package/build/lib/product-kb/types.d.ts +69 -0
  123. package/build/lib/product-kb/types.js +10 -0
  124. package/build/lib/product-kb/types.js.map +1 -0
  125. package/build/lib/promql.d.ts +45 -7
  126. package/build/lib/promql.js +89 -22
  127. package/build/lib/promql.js.map +1 -1
  128. package/build/lib/rate-resolution.d.ts +86 -0
  129. package/build/lib/rate-resolution.js +176 -0
  130. package/build/lib/rate-resolution.js.map +1 -0
  131. package/build/lib/read-only-guard.d.ts +64 -0
  132. package/build/lib/read-only-guard.js +80 -0
  133. package/build/lib/read-only-guard.js.map +1 -0
  134. package/build/lib/recurring-tick.d.ts +16 -0
  135. package/build/lib/recurring-tick.js +18 -3
  136. package/build/lib/recurring-tick.js.map +1 -1
  137. package/build/lib/resolve-pattern-hash.d.ts +46 -0
  138. package/build/lib/resolve-pattern-hash.js +72 -0
  139. package/build/lib/resolve-pattern-hash.js.map +1 -1
  140. package/build/lib/retriever-api.d.ts +4 -4
  141. package/build/lib/retriever-api.js +16 -18
  142. package/build/lib/retriever-api.js.map +1 -1
  143. package/build/lib/retriever-fidelity.js +7 -3
  144. package/build/lib/retriever-fidelity.js.map +1 -1
  145. package/build/lib/retriever-probe.d.ts +125 -0
  146. package/build/lib/retriever-probe.js +658 -0
  147. package/build/lib/retriever-probe.js.map +1 -0
  148. package/build/lib/retriever-state.d.ts +1 -1
  149. package/build/lib/source-disclosure.d.ts +89 -0
  150. package/build/lib/source-disclosure.js +135 -0
  151. package/build/lib/source-disclosure.js.map +1 -0
  152. package/build/lib/strict-args.d.ts +48 -0
  153. package/build/lib/strict-args.js +83 -0
  154. package/build/lib/strict-args.js.map +1 -0
  155. package/build/lib/tool-errors.js +1 -1
  156. package/build/lib/tool-errors.js.map +1 -1
  157. package/build/lib/top-patterns-extras.d.ts +7 -7
  158. package/build/lib/top-patterns-extras.js +8 -9
  159. package/build/lib/top-patterns-extras.js.map +1 -1
  160. package/build/lib/trend-delta.d.ts +1 -1
  161. package/build/lib/trend-delta.js +1 -1
  162. package/build/tools/advise-install.d.ts +2 -2
  163. package/build/tools/advise-retriever.d.ts +6 -2
  164. package/build/tools/advise-retriever.js +119 -13
  165. package/build/tools/advise-retriever.js.map +1 -1
  166. package/build/tools/backfill-metric.d.ts +11 -10
  167. package/build/tools/backfill-metric.js +25 -17
  168. package/build/tools/backfill-metric.js.map +1 -1
  169. package/build/tools/baseline.d.ts +50 -0
  170. package/build/tools/baseline.js +111 -14
  171. package/build/tools/baseline.js.map +1 -1
  172. package/build/tools/commitment-report.d.ts +99 -11
  173. package/build/tools/commitment-report.js +326 -34
  174. package/build/tools/commitment-report.js.map +1 -1
  175. package/build/tools/configure-engine.d.ts +26 -5
  176. package/build/tools/configure-engine.js +949 -91
  177. package/build/tools/configure-engine.js.map +1 -1
  178. package/build/tools/configure-env.d.ts +8 -8
  179. package/build/tools/configure-env.js +2 -0
  180. package/build/tools/configure-env.js.map +1 -1
  181. package/build/tools/cost-options.d.ts +5 -2
  182. package/build/tools/cost-options.js +83 -42
  183. package/build/tools/cost-options.js.map +1 -1
  184. package/build/tools/create-env.js +2 -0
  185. package/build/tools/create-env.js.map +1 -1
  186. package/build/tools/delete-env.js +2 -0
  187. package/build/tools/delete-env.js.map +1 -1
  188. package/build/tools/dependency-check.js +31 -6
  189. package/build/tools/dependency-check.js.map +1 -1
  190. package/build/tools/dev-restart.js +2 -0
  191. package/build/tools/dev-restart.js.map +1 -1
  192. package/build/tools/discover-join.d.ts +6 -0
  193. package/build/tools/discover-join.js +59 -9
  194. package/build/tools/discover-join.js.map +1 -1
  195. package/build/tools/doctor.js +156 -36
  196. package/build/tools/doctor.js.map +1 -1
  197. package/build/tools/env-config-manage.d.ts +69 -0
  198. package/build/tools/env-config-manage.js +696 -0
  199. package/build/tools/env-config-manage.js.map +1 -0
  200. package/build/tools/env-register.d.ts +245 -0
  201. package/build/tools/env-register.js +223 -0
  202. package/build/tools/env-register.js.map +1 -0
  203. package/build/tools/estimate-savings.d.ts +70 -21
  204. package/build/tools/estimate-savings.js +322 -72
  205. package/build/tools/estimate-savings.js.map +1 -1
  206. package/build/tools/event-lookup.js +105 -40
  207. package/build/tools/event-lookup.js.map +1 -1
  208. package/build/tools/explain-mode.d.ts +2 -0
  209. package/build/tools/explain-mode.js +63 -36
  210. package/build/tools/explain-mode.js.map +1 -1
  211. package/build/tools/extract-templates.js +1 -1
  212. package/build/tools/extract-templates.js.map +1 -1
  213. package/build/tools/find-skew.d.ts +21 -0
  214. package/build/tools/find-skew.js +37 -29
  215. package/build/tools/find-skew.js.map +1 -1
  216. package/build/tools/investigate.d.ts +8 -2
  217. package/build/tools/investigate.js +299 -64
  218. package/build/tools/investigate.js.map +1 -1
  219. package/build/tools/log10x-start.d.ts +1 -1
  220. package/build/tools/log10x-start.js +3 -3
  221. package/build/tools/log10x-start.js.map +1 -1
  222. package/build/tools/measure-compaction.d.ts +2 -2
  223. package/build/tools/measure-compaction.js +67 -21
  224. package/build/tools/measure-compaction.js.map +1 -1
  225. package/build/tools/metric-overlay.d.ts +6 -0
  226. package/build/tools/metric-overlay.js +175 -29
  227. package/build/tools/metric-overlay.js.map +1 -1
  228. package/build/tools/metrics-that-moved.d.ts +19 -0
  229. package/build/tools/metrics-that-moved.js +220 -37
  230. package/build/tools/metrics-that-moved.js.map +1 -1
  231. package/build/tools/offload-manage.d.ts +95 -0
  232. package/build/tools/offload-manage.js +430 -0
  233. package/build/tools/offload-manage.js.map +1 -0
  234. package/build/tools/overflow-contents.d.ts +3 -4
  235. package/build/tools/overflow-contents.js +118 -81
  236. package/build/tools/overflow-contents.js.map +1 -1
  237. package/build/tools/pattern-detail.d.ts +3 -3
  238. package/build/tools/pattern-detail.js +61 -25
  239. package/build/tools/pattern-detail.js.map +1 -1
  240. package/build/tools/pattern-diff.d.ts +55 -0
  241. package/build/tools/pattern-diff.js +652 -0
  242. package/build/tools/pattern-diff.js.map +1 -0
  243. package/build/tools/pattern-examples.d.ts +29 -15
  244. package/build/tools/pattern-examples.js +413 -58
  245. package/build/tools/pattern-examples.js.map +1 -1
  246. package/build/tools/pattern-mitigate.js +69 -29
  247. package/build/tools/pattern-mitigate.js.map +1 -1
  248. package/build/tools/poc-from-local.js +6 -6
  249. package/build/tools/poc-from-local.js.map +1 -1
  250. package/build/tools/poc-from-siem.js +1 -1
  251. package/build/tools/poc-from-siem.js.map +1 -1
  252. package/build/tools/preview-filter.d.ts +10 -0
  253. package/build/tools/preview-filter.js +45 -19
  254. package/build/tools/preview-filter.js.map +1 -1
  255. package/build/tools/product-qa.d.ts +111 -0
  256. package/build/tools/product-qa.js +358 -0
  257. package/build/tools/product-qa.js.map +1 -0
  258. package/build/tools/rank-by-shape-similarity.d.ts +6 -0
  259. package/build/tools/rank-by-shape-similarity.js +179 -26
  260. package/build/tools/rank-by-shape-similarity.js.map +1 -1
  261. package/build/tools/resolve-batch.js +16 -15
  262. package/build/tools/resolve-batch.js.map +1 -1
  263. package/build/tools/retriever-probe.d.ts +30 -0
  264. package/build/tools/retriever-probe.js +247 -0
  265. package/build/tools/retriever-probe.js.map +1 -0
  266. package/build/tools/retriever-query-status.js +157 -47
  267. package/build/tools/retriever-query-status.js.map +1 -1
  268. package/build/tools/retriever-query.d.ts +7 -6
  269. package/build/tools/retriever-query.js +183 -27
  270. package/build/tools/retriever-query.js.map +1 -1
  271. package/build/tools/retriever-register.d.ts +88 -0
  272. package/build/tools/retriever-register.js +239 -0
  273. package/build/tools/retriever-register.js.map +1 -0
  274. package/build/tools/retriever-series.js +177 -15
  275. package/build/tools/retriever-series.js.map +1 -1
  276. package/build/tools/rotate-api-key.js +2 -0
  277. package/build/tools/rotate-api-key.js.map +1 -1
  278. package/build/tools/savings.js +57 -21
  279. package/build/tools/savings.js.map +1 -1
  280. package/build/tools/services.d.ts +10 -1
  281. package/build/tools/services.js +108 -29
  282. package/build/tools/services.js.map +1 -1
  283. package/build/tools/set-gitops-repo.js +2 -0
  284. package/build/tools/set-gitops-repo.js.map +1 -1
  285. package/build/tools/setup-recurring.d.ts +2 -2
  286. package/build/tools/signin.js +3 -0
  287. package/build/tools/signin.js.map +1 -1
  288. package/build/tools/signout.js +2 -0
  289. package/build/tools/signout.js.map +1 -1
  290. package/build/tools/top-patterns.js +193 -41
  291. package/build/tools/top-patterns.js.map +1 -1
  292. package/build/tools/trend.d.ts +1 -1
  293. package/build/tools/trend.js +124 -10
  294. package/build/tools/trend.js.map +1 -1
  295. package/build/tools/update-env.d.ts +1 -1
  296. package/build/tools/update-env.js +5 -3
  297. package/build/tools/update-env.js.map +1 -1
  298. package/build/tools/update-settings.js +3 -1
  299. package/build/tools/update-settings.js.map +1 -1
  300. package/build/tools/whats-changing.d.ts +45 -0
  301. package/build/tools/whats-changing.js +613 -0
  302. package/build/tools/whats-changing.js.map +1 -0
  303. package/build/tools/whats-new.d.ts +44 -0
  304. package/build/tools/whats-new.js +408 -0
  305. package/build/tools/whats-new.js.map +1 -0
  306. package/default-manifest.json +133 -1
  307. package/package.json +7 -5
package/build/index.js CHANGED
@@ -17,7 +17,6 @@ import { StructuredOutputSchema, buildEnvelope, } from './lib/output-types.js';
17
17
  import { detectMode, shouldRegisterTool, formatModeResolution, } from './lib/mode-detect.js';
18
18
  import { makeShapeCoercive } from './lib/input-coerce.js';
19
19
  import { loadEnvironments, resolveEnv, revalidateEnvironments, EnvironmentValidationError } from './lib/environments.js';
20
- import { fetchAnalyzerCost } from './lib/api.js';
21
20
  import { eventLookupSchema, executeEventLookup } from './tools/event-lookup.js';
22
21
  import { savingsSchema, executeSavings } from './tools/savings.js';
23
22
  import { trendSchema, executeTrend } from './tools/trend.js';
@@ -34,9 +33,16 @@ import { investigateSchema, executeInvestigate, } from './tools/investigate.js';
34
33
  import { doctorSchema, executeDoctor, runDoctorChecks, renderDoctorReport } from './tools/doctor.js';
35
34
  import { log } from './lib/log.js';
36
35
  import { describeToolError } from './lib/tool-errors.js';
37
- import { retrieverQuerySchema, executeRetrieverQuery } from './tools/retriever-query.js';
36
+ import { DemoReadOnlyError, isReadOnlyMode } from './lib/read-only-guard.js';
37
+ import { buildDemoReadOnlyEnvelope } from './lib/chassis-envelope.js';
38
+ import { retrieverQuerySchema, executeRetrieverQuery, retrieverNotConfiguredMessage } from './tools/retriever-query.js';
38
39
  import { retrieverSeriesSchema, executeRetrieverSeries } from './tools/retriever-series.js';
39
40
  import { retrieverQueryStatusSchema, executeRetrieverQueryStatus } from './tools/retriever-query-status.js';
41
+ import { retrieverProbeSchema, executeRetrieverProbe } from './tools/retriever-probe.js';
42
+ import { retrieverRegisterSchema, executeRetrieverRegister } from './tools/retriever-register.js';
43
+ import { patternDiffSchema, executePatternDiff } from './tools/pattern-diff.js';
44
+ import { whatsChangingSchema, executeWhatsChanging } from './tools/whats-changing.js';
45
+ import { whatsNewSchema, executeWhatsNew } from './tools/whats-new.js';
40
46
  import { backfillMetricSchema, executeBackfillMetric } from './tools/backfill-metric.js';
41
47
  import { customerMetricsQuerySchema, executeCustomerMetricsQuery, } from './tools/customer-metrics-query.js';
42
48
  import { discoverJoinSchema, executeDiscoverJoin } from './tools/discover-join.js';
@@ -60,14 +66,17 @@ import { updateSettingsSchema, executeUpdateSettings } from './tools/update-sett
60
66
  import { createEnvSchema, executeCreateEnv } from './tools/create-env.js';
61
67
  import { updateEnvSchema, executeUpdateEnv } from './tools/update-env.js';
62
68
  import { deleteEnvSchema, executeDeleteEnv } from './tools/delete-env.js';
69
+ import { envRegisterSchema, executeEnvRegister } from './tools/env-register.js';
63
70
  import { setGitopsRepoSchema, executeSetGitopsRepo } from './tools/set-gitops-repo.js';
71
+ import { destSetSchema, executeDestSet, envValidateSchema, executeEnvValidate, envDiffVsEnvvarsSchema, executeEnvDiffVsEnvvars, } from './tools/env-config-manage.js';
72
+ import { offloadAddSchema, executeOffloadAdd, offloadArchiveSchema, executeOffloadArchive, } from './tools/offload-manage.js';
64
73
  import { rotateApiKeySchema, executeRotateApiKey } from './tools/rotate-api-key.js';
65
74
  import { servicesSchema, executeServices } from './tools/services.js';
66
75
  import { overflowContentsSchema, executeOverflowContents } from './tools/overflow-contents.js';
67
- import { fetchCapCsvForEnv, fetchActionIntentForEnv } from './lib/cap-csv-fetch.js';
76
+ import { fetchCapCsvForEnv, fetchActionIntentForEnv, fetchCapCsvFromConfigMap, fetchActionIntentFromConfigMap, } from './lib/cap-csv-fetch.js';
68
77
  import { findSkewSchema, executeFindSkew } from './tools/find-skew.js';
69
78
  // find_constant_slots, find_uuid_in_body, find_incident_cluster removed
70
- // pre-launch (2026-05-28): produced findings the agent could not act on
79
+ // because they produced findings the agent could not act on
71
80
  // (engine-tokenization config changes need a human + redeploy) OR
72
81
  // overlapped with log10x_investigate's trajectory + chain analysis.
73
82
  import { discoverLabelsSchema, executeDiscoverLabels } from './tools/discover-labels.js';
@@ -76,6 +85,7 @@ import { log10xStartSchema, executeLog10xStart } from './tools/log10x-start.js';
76
85
  import { costOptionsSchema, executeCostOptions } from './tools/cost-options.js';
77
86
  import { explainModeSchema, executeExplainMode } from './tools/explain-mode.js';
78
87
  import { previewFilterSchema, executePreviewFilter } from './tools/preview-filter.js';
88
+ import { productQaSchema, executeProductQa } from './tools/product-qa.js';
79
89
  import { patternDetailSchema, executePatternDetail } from './tools/pattern-detail.js';
80
90
  import { measureCompactionSchema, executeMeasureCompaction } from './tools/measure-compaction.js';
81
91
  import { setupRecurringSchema, executeSetupRecurring } from './tools/setup-recurring.js';
@@ -92,6 +102,10 @@ import { getStatus } from './resources/status.js';
92
102
  let envs;
93
103
  let bootMode;
94
104
  async function initEnvs() {
105
+ // loadEnvironments() now applies the SaaS↔on-prem alias bridge
106
+ // internally so every caller (including tools like doctor that
107
+ // re-load envs on each call) sees the same enriched byNickname map.
108
+ // See src/lib/env-alias-bridge.ts.
95
109
  envs = await loadEnvironments();
96
110
  }
97
111
  /**
@@ -118,8 +132,6 @@ function getEnvs() {
118
132
  }
119
133
  return envs;
120
134
  }
121
- const costCache = new Map();
122
- const COST_REFRESH_MS = 3_600_000; // 1 hour
123
135
  /**
124
136
  * Wrap a tool implementation in a try/catch that produces actionable error
125
137
  * suggestions via `describeToolError(toolName, e)`. Every tool callback
@@ -137,9 +149,9 @@ const COST_REFRESH_MS = 3_600_000; // 1 hour
137
149
  * etc.). When the MCP is in pure-demo mode (no user configuration,
138
150
  * silently landed on the demo backend), these tools short-circuit
139
151
  * with a structured `not_configured` response instead of returning
140
- * demo data the user didn't ask for. Phase 5b — phase 7 will remove
141
- * the silent-demo path entirely; for now we surface the conversation
142
- * starter without breaking the demo-mode walkthrough.
152
+ * demo data the user didn't ask for. We surface the conversation
153
+ * starter without breaking the demo-mode walkthrough; the silent-demo
154
+ * path will be removed in a later release.
143
155
  *
144
156
  * Tools NOT in this set bypass the gate: configure_env (the
145
157
  * onboarding tool itself), doctor (status reporting works in any
@@ -174,9 +186,9 @@ const METRIC_REQUIRING_TOOLS = new Set([
174
186
  const DEMO_PLAYGROUND = /^(1|true|yes)$/i.test(process.env.LOG10X_MCP_DEMO_PLAYGROUND ?? '');
175
187
  async function wrap(toolName, fn) {
176
188
  const started = Date.now();
177
- // Phase 5b gate: if this is a metric-requiring tool and the MCP is
189
+ // Demo-mode gate: if this is a metric-requiring tool and the MCP is
178
190
  // in pure-demo state, redirect to the conversational onboarding flow
179
- // instead of returning silent-demo data — UNLESS this is an intentional
191
+ // instead of returning silent-demo data, UNLESS this is an intentional
180
192
  // demo playground (LOG10X_MCP_DEMO_PLAYGROUND), which wants the demo data
181
193
  // served (the gated tools are exactly the demo's headline surface).
182
194
  if (METRIC_REQUIRING_TOOLS.has(toolName) &&
@@ -189,11 +201,19 @@ async function wrap(toolName, fn) {
189
201
  // not a bare text blob; the agent branches on data.status and the MCP
190
202
  // SDK requires structuredContent for tools that declare an outputSchema
191
203
  // (a text-only return here is rejected as "no structured content").
204
+ // rq-1: retriever_query / retriever_series read the offload bucket (the
205
+ // held-back cohort), not the metrics backend. This wrap gate pre-empts their
206
+ // own correct kind:'retriever' gate, so branch the remediation here: a
207
+ // metrics-backend nag points the agent at the wrong subsystem entirely.
208
+ const isRetrieverTool = toolName === 'log10x_retriever_query' || toolName === 'log10x_retriever_series';
209
+ const ncKind = isRetrieverTool ? 'retriever' : 'metrics_backend';
192
210
  return notConfiguredToolResult(buildNotConfiguredEnvelope({
193
211
  tool: toolName,
194
- kind: 'metrics_backend',
195
- remediation: notConfiguredMessageForTool(toolName),
196
- actions: defaultActionsForKind('metrics_backend'),
212
+ kind: ncKind,
213
+ remediation: isRetrieverTool
214
+ ? retrieverNotConfiguredMessage()
215
+ : notConfiguredMessageForTool(toolName),
216
+ actions: defaultActionsForKind(ncKind),
197
217
  }));
198
218
  }
199
219
  // Mode gate: if the boot-time mode-detect determined this tool is
@@ -325,6 +345,30 @@ async function wrap(toolName, fn) {
325
345
  catch (e) {
326
346
  const raw = e instanceof Error ? e.message : String(e);
327
347
  log.debug(`tool.${toolName}.raw_err`, { msg: raw });
348
+ // Demo read-only gate: a writer tool called `requireWriteAccess()` while
349
+ // the MCP is running with LOG10X_MCP_READ_ONLY=true. Return the canonical
350
+ // demo_read_only envelope (status='error', data.status='demo_read_only',
351
+ // data.error.error_type='demo_read_only') so the agent can branch on the
352
+ // typed discriminant without parsing prose. structuredContent is populated
353
+ // so MCP hosts that honor the 2025-03-26 revision see typed JSON directly.
354
+ if (e instanceof DemoReadOnlyError) {
355
+ log.info(`tool.${toolName}.demo_read_only`, {
356
+ ms: Date.now() - started,
357
+ would_have: e.would_have.slice(0, 200),
358
+ });
359
+ const envelope = buildDemoReadOnlyEnvelope({
360
+ tool: toolName,
361
+ would_have: e.would_have,
362
+ hint: e.hint,
363
+ });
364
+ const structuredContent = envelope;
365
+ return {
366
+ content: [
367
+ { type: 'text', text: JSON.stringify(envelope, null, 2) },
368
+ ],
369
+ structuredContent,
370
+ };
371
+ }
328
372
  // A deliberate "not configured" throw (the loud human-escape-hatch
329
373
  // path, e.g. customer_metrics_query) must not abort the agent's chain
330
374
  // as an opaque error. Convert it to a structured, branchable
@@ -468,19 +512,6 @@ function applyDemoBanner(text) {
468
512
  // Pure demo: lighter footer; the user opted in by not setting a key.
469
513
  return text + `\n\n_(Demo mode — account-scoped tools query the read-only Log10x demo env. Call \`log10x_login_status\` to use your own data.)_`;
470
514
  }
471
- async function getAnalyzerCost(env, override) {
472
- if (override !== undefined)
473
- return override;
474
- const key = env.envId;
475
- const cached = costCache.get(key);
476
- const now = Date.now();
477
- if (cached && (now - cached.fetchedAt) < COST_REFRESH_MS) {
478
- return cached.cost;
479
- }
480
- const cost = await fetchAnalyzerCost(env);
481
- costCache.set(key, { cost, fetchedAt: now });
482
- return cost;
483
- }
484
515
  // ── Server ──
485
516
  const SERVER_INFO = { name: 'log10x', version: readClientVersion() };
486
517
  // Build/version provenance, surfaced on GET /health and in the mcp.boot log so
@@ -526,10 +557,10 @@ CUSTOMER TIER LADDER (determines which tools are available)
526
557
  filebeat / logstash / otel-collector / vector). Filters, samples, and optionally
527
558
  losslessly compacts events in-flight. Replaces the legacy Regulator + Optimizer apps.
528
559
  Same tools as Reporter, plus event modification on the forwarder's path.
529
- 4. Retriever (deployable with or without Reporter/Receiver) — S3 archive with Bloom-filter
530
- index. Product still being shaped.
531
- Adds: log10x_retriever_query (forensic retrieval), log10x_backfill_metric (new metric
532
- backfilled from archive + forward-emission handoff to the Reporter).
560
+ 4. Retriever (deployable with or without Reporter/Receiver). Reads the customer-owned
561
+ overflow S3 bucket the Receiver's offload action writes to, indexed by Bloom filter.
562
+ Adds: log10x_retriever_query (read the offloaded cohort for a pattern, the events the
563
+ Receiver held back from the SIEM). log10x_backfill_metric is deprecated and dark.
533
564
 
534
565
  TOOL ROUTING BY USER INTENT
535
566
 
@@ -572,17 +603,16 @@ Cost investigation:
572
603
  since last week" framing.)
573
604
  - "pipeline savings / ROI" → log10x_savings
574
605
 
575
- Forensic / audit / archive — ANY request for RAW EVENTS from the S3 archive:
576
- - "pull the actual log events", "get me the raw events", "retrieve events from S3",
577
- "fetch events from the archive", "show me what was in the logs during <time window>",
578
- "I need the events themselves, not aggregates" → log10x_retriever_query
579
- - "get me all <pattern> events from 90 days ago" → log10x_retriever_query
580
- - "get all events for customer X filtered by Y, 60d window" → log10x_retriever_query
581
- - "backfill a new metric with 90d of history from the archive" → log10x_backfill_metric
582
- (Critical: when a user asks for raw events OR mentions S3 / archive / cold storage
583
- explicitly, route to retriever_query even if the framing also mentions an incident.
584
- investigate returns aggregate pattern analysis; retriever_query returns actual log
585
- lines. "Post-mortem needs the actual log events" = retriever_query, not investigate.)
606
+ Offloaded cohort: RAW EVENTS the Receiver held back from the SIEM (the overflow bucket):
607
+ - "show me what's being offloaded for <pattern>", "sample the held-back events",
608
+ "pull the raw events the Receiver kept out of Datadog/Splunk",
609
+ "I need the offloaded events themselves, not aggregates" → log10x_retriever_query
610
+ - "sample the offloaded <pattern> events" → log10x_retriever_query
611
+ - "verify the offload decision for customer X filtered by Y" → log10x_retriever_query
612
+ (Critical: retriever_query reads the offload bucket, the cohort the SIEM never received.
613
+ It is not a mirror of indexed history. For events the SIEM still holds, the SIEM is the
614
+ source. investigate returns aggregate pattern analysis; retriever_query returns the
615
+ actual offloaded log lines. Re-ingest from the bucket is customer-driven, not an MCP action.)
586
616
 
587
617
  Root-cause across services (the investigate wedge):
588
618
  - user pastes an error, asks "what's causing the upstream" → log10x_investigate
@@ -627,12 +657,9 @@ NATURAL TOOL CHAINS
627
657
  At reporter/dev tier log10x_cost_options returns 2 modes (observe_only + install_receiver);
628
658
  at receiver/retriever tier it returns 6 modes (drop/sample/compact/tier_down/offload/observe_only).
629
659
 
630
- Forensic retrieval across retention boundaries:
660
+ Inspect the offloaded cohort for a pattern:
631
661
  log10x_event_lookup → log10x_retriever_query
632
662
 
633
- New metric from historical archive:
634
- log10x_top_patterns or log10x_investigate → log10x_backfill_metric
635
-
636
663
  RESPONSE STYLE
637
664
 
638
665
  - For cost questions: show dollar amounts prominently, emphasize before→after deltas, flag new
@@ -696,7 +723,7 @@ Tool responses carry rich label context per series (message_pattern, severity_le
696
723
 
697
724
  3. **Two tiers when the user asks for verification or audit.** A "**Facts:**" / "**Interpretation:**" split is appropriate when the user is auditing or debugging your synthesis. For a normal "show me X" request, write the decoded answer inline — facts and interpretation woven together — and skip the tiering. Default to terse, single-pass prose; reach for the two-tier layout only when warranted.
698
725
 
699
- 4. **Refusal beats guess.** If you don't recognize a \`message_pattern\` token, severity, or label value with high confidence, say "symbol unknown" or "context unclear" and suggest pulling raw events via \`log10x_retriever_query\` (when Retriever is deployed) or running \`log10x_event_lookup\` for a known sample. Do not invent a plausible-sounding identity.
726
+ 4. **Refusal beats guess.** If you don't recognize a \`message_pattern\` token, severity, or label value with high confidence, say "symbol unknown" or "context unclear" and run \`log10x_event_lookup\` for a known sample. When the pattern is under an active offload action, \`log10x_retriever_query\` can sample the held-back events. Do not invent a plausible-sounding identity.
700
727
 
701
728
  5. **No reference to patterns/services/severities outside the response.** The label set in the tool result is the universe. Phrases like "you probably also have…" or "I'd expect to see…" are forbidden — they invite the user to look for problems that aren't in the data.
702
729
 
@@ -841,8 +868,7 @@ function applyToolRegistrations(target, registry) {
841
868
  // ── Tool: log10x_event_lookup ──
842
869
  registerLog10xTool('log10x_event_lookup', eventLookupSchema, (args) => wrap('log10x_event_lookup', async () => {
843
870
  const env = resolveEnv(getEnvs(), args.environment);
844
- const cost = await getAnalyzerCost(env, args.analyzerCost);
845
- return executeEventLookup({ ...args, analyzerCost: cost }, env);
871
+ return executeEventLookup(args, env);
846
872
  }));
847
873
  // ── Tool: log10x_pattern_examples ──
848
874
  //
@@ -855,17 +881,22 @@ registerLog10xTool('log10x_pattern_examples', patternExamplesSchema, (args) => w
855
881
  const env = resolveEnv(getEnvs(), args.environment);
856
882
  return executePatternExamples(args, env);
857
883
  }));
884
+ // ── Tool: log10x_product_qa ──
885
+ //
886
+ // Local docs-corpus lookup. No env / TSDB / SIEM dependency — the
887
+ // corpus is shipped inside the build at build/product-kb/docs (the
888
+ // build script copies it from config/mksite/docs). See
889
+ // src/lib/product-kb/index.ts for path resolution + override env.
890
+ registerLog10xTool('log10x_product_qa', productQaSchema, (args) => wrap('log10x_product_qa', async () => executeProductQa(args)));
858
891
  // ── Tool: log10x_savings ──
859
892
  registerLog10xTool('log10x_savings', savingsSchema, (args) => wrap('log10x_savings', async () => {
860
893
  const env = resolveEnv(getEnvs(), args.environment);
861
- const cost = await getAnalyzerCost(env, args.analyzerCost);
862
- return executeSavings({ ...args, analyzerCost: cost }, env);
894
+ return executeSavings(args, env);
863
895
  }));
864
896
  // ── Tool: log10x_pattern_trend ──
865
897
  registerLog10xTool('log10x_pattern_trend', trendSchema, (args) => wrap('log10x_pattern_trend', async () => {
866
898
  const env = resolveEnv(getEnvs(), args.environment);
867
- const cost = await getAnalyzerCost(env, args.analyzerCost);
868
- return executeTrend({ ...args, analyzerCost: cost }, env);
899
+ return executeTrend(args, env);
869
900
  }));
870
901
  // ── Tool: log10x_dependency_check ──
871
902
  registerLog10xTool('log10x_dependency_check', dependencyCheckSchema, (args) => wrap('log10x_dependency_check', async () => executeDependencyCheck(args)));
@@ -886,24 +917,89 @@ registerLog10xTool('log10x_configure_env', configureEnvSchema, (args) => wrap('l
886
917
  registerLog10xTool('log10x_set_gitops_repo', setGitopsRepoSchema, (args) => wrap('log10x_set_gitops_repo', async () => {
887
918
  return executeSetGitopsRepo(args);
888
919
  }));
920
+ // ── Tool: log10x_dest_set ──
921
+ // Edit the SIEM destination block on an env-config document. Separate
922
+ // from configure_env (which validates the live metrics backend before
923
+ // writing) so the agent can change destination fields without re-supplying
924
+ // backend credentials.
925
+ registerLog10xTool('log10x_dest_set', destSetSchema, (args) => wrap('log10x_dest_set', async () => {
926
+ return executeDestSet(args);
927
+ }));
928
+ // ── Tool: log10x_env_validate ──
929
+ // Schema + cross-field sanity over a stored env-config document. Catches
930
+ // destination/region mismatches, ingest_url shape issues, cluster-vs-offload
931
+ // type misalignments before downstream tools fail with vendor errors.
932
+ registerLog10xTool('log10x_env_validate', envValidateSchema, (args) => wrap('log10x_env_validate', async () => {
933
+ return executeEnvValidate(args);
934
+ }));
935
+ // ── Tool: log10x_env_diff_vs_envvars ──
936
+ // Compare the stored env doc against the LOG10X_* env vars the bridge
937
+ // would have produced. Surfaces "I set the env var but it's being ignored"
938
+ // drift with per-field recommendations.
939
+ registerLog10xTool('log10x_env_diff_vs_envvars', envDiffVsEnvvarsSchema, (args) => wrap('log10x_env_diff_vs_envvars', async () => {
940
+ return executeEnvDiffVsEnvvars(args);
941
+ }));
942
+ // ── Tool: log10x_offload_add ──
943
+ // Append a new entry to the env-config document's offload_destinations[].
944
+ // Multi-target is the documented use case (drain a legacy bucket while a
945
+ // new one is primary); the schema array stays non-empty by default.
946
+ registerLog10xTool('log10x_offload_add', offloadAddSchema, (args) => wrap('log10x_offload_add', async () => {
947
+ return executeOffloadAdd(args);
948
+ }));
949
+ // ── Tool: log10x_offload_archive ──
950
+ // Flip one destination's status to `archived` (kept in the list as a
951
+ // historical reference). Refuses when the target is the last active
952
+ // destination — the Receiver needs at least one active destination to
953
+ // route the dropped slice to.
954
+ registerLog10xTool('log10x_offload_archive', offloadArchiveSchema, (args) => wrap('log10x_offload_archive', async () => {
955
+ return executeOffloadArchive(args);
956
+ }));
889
957
  // ── Tool: log10x_top_patterns ──
890
958
  registerLog10xTool('log10x_top_patterns', topPatternsSchema, (args) => wrap('log10x_top_patterns', async () => {
891
959
  const env = resolveEnv(getEnvs(), args.environment);
892
- const cost = await getAnalyzerCost(env, args.analyzerCost);
893
- return executeTopPatterns({ ...args, analyzerCost: cost }, env);
960
+ return executeTopPatterns(args, env);
961
+ }));
962
+ // ── Tool: log10x_pattern_diff ──
963
+ //
964
+ // Set diff of patterns across a time boundary. Compares pattern presence in
965
+ // two windows and returns new / retired / persistent / re_emerged sets plus
966
+ // co_emergence_clusters (deploy fingerprint via first_seen clustering).
967
+ // Coherent only because log10x pattern_hash is stable across queries;
968
+ // competitors that re-cluster per query can't answer this.
969
+ registerLog10xTool('log10x_pattern_diff', patternDiffSchema, (args) => wrap('log10x_pattern_diff', async () => {
970
+ const env = resolveEnv(getEnvs(), args.environment);
971
+ return executePatternDiff(args, env);
972
+ }));
973
+ // ── Tool: log10x_whats_changing ──
974
+ //
975
+ // Patterns ranked by delta vs a baseline window (not current cost). Restores
976
+ // the capability of the removed log10x_cost_drivers tool
977
+ // using the modern StructuredOutput envelope. Brand-new patterns (no
978
+ // baseline) are excluded — they go to log10x_whats_new.
979
+ registerLog10xTool('log10x_whats_changing', whatsChangingSchema, (args) => wrap('log10x_whats_changing', async () => {
980
+ const env = resolveEnv(getEnvs(), args.environment);
981
+ return executeWhatsChanging(args, env);
982
+ }));
983
+ // ── Tool: log10x_whats_new ──
984
+ //
985
+ // Patterns whose first_seen timestamp falls inside a recency window. Sister
986
+ // tool to whats_changing — new patterns have no baseline, so they get a
987
+ // clean home that doesn't pollute the delta-vs-baseline surface.
988
+ registerLog10xTool('log10x_whats_new', whatsNewSchema, (args) => wrap('log10x_whats_new', async () => {
989
+ const env = resolveEnv(getEnvs(), args.environment);
990
+ return executeWhatsNew(args, env);
894
991
  }));
895
992
  // ── Tool: log10x_services ──
896
993
  registerLog10xTool('log10x_services', servicesSchema, (args) => wrap('log10x_services', async () => {
897
994
  const env = resolveEnv(getEnvs(), args.environment);
898
- const cost = await getAnalyzerCost(env, args.analyzerCost);
899
- return executeServices({ ...args, analyzerCost: cost }, env);
995
+ return executeServices(args, env);
900
996
  }));
901
997
  // ── Tool: log10x_overflow_contents ──
902
998
  //
903
- // Item 6 (cost-cutting close-list v2): the contents view of the
999
+ // The contents view of the
904
1000
  // customer-owned offload bucket. Queries dropped-by-pattern from the
905
1001
  // TSDB, joins to the cap-CSV to filter to action=offload only, and
906
- // routes the agent to log10x_retriever_query for rehydration. See
1002
+ // routes the agent to log10x_retriever_query to inspect the offloaded cohort. See
907
1003
  // the long docstring on src/tools/overflow-contents.ts for the
908
1004
  // design rationale + cap-CSV degraded-mode semantics.
909
1005
  registerLog10xTool('log10x_overflow_contents', overflowContentsSchema, (args) => wrap('log10x_overflow_contents', async () => {
@@ -940,6 +1036,21 @@ registerLog10xTool('log10x_retriever_series', retrieverSeriesSchema, (args) => w
940
1036
  }));
941
1037
  // ── Tool: log10x_retriever_query_status ──
942
1038
  registerLog10xTool('log10x_retriever_query_status', retrieverQueryStatusSchema, (args) => wrap('log10x_retriever_query_status', async () => executeRetrieverQueryStatus(args)));
1039
+ // ── Tool: log10x_retriever_probe ──
1040
+ //
1041
+ // End-to-end probe of the deployed retriever chain. Fires a synthetic query
1042
+ // and asserts every stage (offload bucket freshness, indexer pipeline running,
1043
+ // SQS depths, pod ready, CloudWatch scan/stream events, S3 qr/*.jsonl, MCP
1044
+ // events returned). Returns a structured verdict (green / broken / unknown)
1045
+ // with per-stage asserts and a remedy on the first failure.
1046
+ registerLog10xTool('log10x_retriever_probe', retrieverProbeSchema, (args) => wrap('log10x_retriever_probe', async () => executeRetrieverProbe(args)));
1047
+ // ── Tool: log10x_retriever_register ──
1048
+ //
1049
+ // Write the Retriever endpoint + queue coordinates onto an existing
1050
+ // environment-config document. Requires `log10x_env_register` to have
1051
+ // created the env first — refuses on env_not_found rather than
1052
+ // stamping a partial document the rest of the schema can't satisfy.
1053
+ registerLog10xTool('log10x_retriever_register', retrieverRegisterSchema, (args) => wrap('log10x_retriever_register', async () => executeRetrieverRegister(args)));
943
1054
  // ── Tool: log10x_backfill_metric ──
944
1055
  registerLog10xTool('log10x_backfill_metric', backfillMetricSchema, (args) => wrap('log10x_backfill_metric', async () => {
945
1056
  const env = resolveEnv(getEnvs(), args.environment);
@@ -1028,6 +1139,14 @@ registerLog10xTool('log10x_create_env', createEnvSchema, (args) => wrap('log10x_
1028
1139
  registerLog10xTool('log10x_update_env', updateEnvSchema, (args) => wrap('log10x_update_env', async () => executeUpdateEnv(args, getEnvs())));
1029
1140
  // ── Tool: log10x_delete_env ──
1030
1141
  registerLog10xTool('log10x_delete_env', deleteEnvSchema, (args) => wrap('log10x_delete_env', async () => executeDeleteEnv(args, getEnvs())));
1142
+ // ── Tool: log10x_env_register ──
1143
+ //
1144
+ // Writes a full EnvironmentConfig document (cluster identity, SIEM
1145
+ // destination, offload destinations, streamer + retriever endpoints)
1146
+ // to whichever on-prem store the customer's cloud uses. Distinct from
1147
+ // create_env: that mints account-level identity on the SaaS backend;
1148
+ // this writes the cluster-side descriptor every tool resolves against.
1149
+ registerLog10xTool('log10x_env_register', envRegisterSchema, (args) => wrap('log10x_env_register', async () => executeEnvRegister(args)));
1031
1150
  // ── Tool: log10x_rotate_api_key ──
1032
1151
  registerLog10xTool('log10x_rotate_api_key', rotateApiKeySchema, (args) => wrap('log10x_rotate_api_key', async () => executeRotateApiKey(args, getEnvs())));
1033
1152
  // ── Tool: log10x_customer_metrics_query (v1.4) ──
@@ -1122,7 +1241,10 @@ registerLog10xTool('log10x_advise_retriever', adviseRetrieverSchema, (args) => w
1122
1241
  // ── Tool: log10x_advise_install (install wizard for Reporter / Receiver) ──
1123
1242
  registerLog10xTool('log10x_advise_install', adviseInstallSchema, (args) => wrap('log10x_advise_install', () => executeAdviseInstall(args, getEnvs(), server)));
1124
1243
  // ── Tool: log10x_configure_engine (unified per-pattern action-plan PR author) ──
1125
- registerLog10xTool('log10x_configure_engine', configureEngineSchema, (args) => wrap('log10x_configure_engine', () => executeConfigureEngine(args)));
1244
+ registerLog10xTool('log10x_configure_engine', configureEngineSchema, (args) => wrap('log10x_configure_engine', () => {
1245
+ const env = resolveEnv(getEnvs(), args.environment);
1246
+ return executeConfigureEngine(args, env);
1247
+ }));
1126
1248
  // ── Tool: log10x_estimate_savings (forecast + verify modes) ──
1127
1249
  registerLog10xTool('log10x_estimate_savings', estimateSavingsSchema, (args) => wrap('log10x_estimate_savings', () => {
1128
1250
  const env = resolveEnv(getEnvs(), args.environment);
@@ -1147,11 +1269,10 @@ registerLog10xTool('log10x_baseline', baselineSchema, (args) => wrap('log10x_bas
1147
1269
  // baseline_window is the pre-policy reference.
1148
1270
  // 3. Adapts VerifyResult → WeeklyVerifyResult.
1149
1271
  //
1150
- // Known Item-5 gap (cost-cutting-prioritized-close-list-v2 §1.5):
1151
- // runEstimateVerify queries `[range]` ending at "now", so every weekly
1152
- // loop hits the same live snapshot. The arithmetic-reconciliation patch
1153
- // (Item 5) is where week-specific windows + the per_pattern_breakdown
1154
- // cap-CSV join land. Item 1 only unblocks the not_ready hard-return.
1272
+ // Limitation: runEstimateVerify queries `[range]` ending at "now", so
1273
+ // every weekly loop hits the same live snapshot. Week-specific windows
1274
+ // and the per_pattern_breakdown cap-CSV join are not yet implemented;
1275
+ // this path only unblocks the not_ready hard-return.
1155
1276
  _setVerifyRunner(async ({ commitment, week_start, week_end }) => {
1156
1277
  const env = resolveEnv(getEnvs(), commitment.env);
1157
1278
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
@@ -1159,13 +1280,33 @@ _setVerifyRunner(async ({ commitment, week_start, week_end }) => {
1159
1280
  // Best-effort fetch of action-intent.json (canonical action source) and
1160
1281
  // cap CSV (byte-cap context + legacy action suffix fallback). Both are
1161
1282
  // passed to runEstimateVerify for action-split attribution. On any
1162
- // failure (no gh, no repo, file not found) the respective field is
1163
- // undefined and the verify run falls back to the §E.1 single-bucket
1164
- // attribution in commitment-report.
1165
- const [capCsv, actionIntent] = await Promise.all([
1166
- fetchCapCsvForEnv(env).catch(() => undefined),
1167
- fetchActionIntentForEnv(env).catch(() => undefined),
1168
- ]);
1283
+ // failure the respective field is undefined and the verify run falls
1284
+ // back to single-bucket attribution in commitment-report.
1285
+ //
1286
+ // Delivery channel comes from commitment.delivery_target captured at
1287
+ // apply time:
1288
+ // - kind='configmap' → kubectl get configmap (the kubectl_configmap
1289
+ // delivery path; otel-demo and other k8s-native deploys)
1290
+ // - kind='gitops' or absent → gh api (the original PR-merge path;
1291
+ // also the back-compat path for commitments persisted before
1292
+ // delivery_target was added)
1293
+ // When both channels are wired, gitops wins (PR is the durable source
1294
+ // of truth and matches the source_disclosure label).
1295
+ const target = commitment.delivery_target;
1296
+ let capCsv;
1297
+ let actionIntent;
1298
+ if (target?.kind === 'configmap') {
1299
+ [capCsv, actionIntent] = await Promise.all([
1300
+ fetchCapCsvFromConfigMap(target.name, target.namespace).catch(() => undefined),
1301
+ fetchActionIntentFromConfigMap(target.name, target.namespace).catch(() => undefined),
1302
+ ]);
1303
+ }
1304
+ else {
1305
+ [capCsv, actionIntent] = await Promise.all([
1306
+ fetchCapCsvForEnv(env).catch(() => undefined),
1307
+ fetchActionIntentForEnv(env).catch(() => undefined),
1308
+ ]);
1309
+ }
1169
1310
  const vr = await runEstimateVerify({
1170
1311
  destination: commitment.destination,
1171
1312
  baseline_window: commitment.baseline_window || '7d',
@@ -1204,16 +1345,20 @@ if (process.env.LOG10X_DEV_MODE === 'true') {
1204
1345
  const REGISTERED_TOOLS = [
1205
1346
  { name: 'log10x_start', intent: 'CALL FIRST on any cost / orient / "where do I start" question — returns tier + action menu + must_ask_user question; agent must surface verbatim and wait for user pick before any other tool' },
1206
1347
  { name: 'log10x_event_lookup', intent: 'What is this single log line — resolve to stable identity + cost + AI classification' },
1207
- { name: 'log10x_pattern_examples', intent: 'Recent live events for a pattern from the log analyzer with template-parsed slot values — bounded to 24h, for older use retriever_query' },
1348
+ { name: 'log10x_pattern_examples', intent: 'Recent live events for a pattern from the log analyzer with template-parsed slot values, bounded to 24h. When the pattern is offloaded, retriever_query samples the held-back cohort.' },
1208
1349
  { name: 'log10x_savings', intent: 'Pipeline ROI — how much receiver / retriever are saving in dollars' },
1209
1350
  { name: 'log10x_pattern_trend', intent: 'Time series for a pattern — volume + cost history, spike detection, sparkline' },
1210
1351
  { name: 'log10x_dependency_check', intent: 'Scan SIEM + dashboards + alerts for refs to a pattern before muting / deleting it' },
1211
1352
  { name: 'log10x_top_patterns', intent: 'Top N patterns by current cost, with per-row delta vs comparison_window and newly-emerged section' },
1353
+ { name: 'log10x_pattern_diff', intent: 'Set diff of patterns across a time boundary — new/retired/persistent/re_emerged + co_emergence_clusters (deploy fingerprint). Coherent across boundaries because pattern_hash is stable across queries.' },
1354
+ { name: 'log10x_whats_changing', intent: 'Patterns ranked by delta vs baseline (growth/shrinkage). Brand-new patterns excluded — see log10x_whats_new for those. Restores the deleted log10x_cost_drivers capability.' },
1355
+ { name: 'log10x_whats_new', intent: 'Patterns whose first_seen falls inside a recency window. Separate from delta-vs-baseline because new patterns have no baseline.' },
1212
1356
  { name: 'log10x_investigate', intent: 'Single-call root-cause — causal chain for acute spikes or cohort for drift' },
1213
1357
  { name: 'log10x_resolve_batch', intent: 'Pasted-batch triage — per-pattern variable concentration + next actions' },
1214
- { name: 'log10x_retriever_query', intent: 'Direct archive retrieval by pattern identity (tenx_user_pattern) with JS filter expressions' },
1215
- { name: 'log10x_retriever_series', intent: 'Fidelity-aware time series from the S3 archive — auto-selects exact aggregation vs sampled fan-out' },
1216
- { name: 'log10x_backfill_metric', intent: 'Create a new Datadog / Prometheus metric backfilled from Retriever archive' },
1358
+ { name: 'log10x_retriever_query', intent: 'Read the offloaded cohort for a pattern (tenx_user_pattern) from the customer-owned overflow bucket with JS filter expressions: the events the Receiver held back from the SIEM' },
1359
+ { name: 'log10x_retriever_series', intent: 'Time series over the offloaded cohort in the overflow bucket: auto-selects exact aggregation vs sampled parallel sub-window queries' },
1360
+ { name: 'log10x_retriever_probe', intent: 'End-to-end retriever chain probe — fires a synthetic query and asserts every stage (offload bucket, indexer pipeline, SQS, pod ready, CW scan/stream, S3 jsonl, MCP events). Returns green/broken/unknown with per-stage asserts and remedies.' },
1361
+ { name: 'log10x_backfill_metric', intent: 'Deprecated, kept dark. The live isDropped metric surface answers overflow-volume questions as a TSDB query.' },
1217
1362
  { name: 'log10x_doctor', intent: 'Startup health check — env config, gateway, tier, freshness, Retriever, paste endpoint, cross-pillar enrichment floor' },
1218
1363
  { name: 'log10x_login_status', intent: 'Report credential / env state — identity, env list with permissions, demo-mode upgrade guide if applicable' },
1219
1364
  { name: 'log10x_signin_start', intent: 'Step 1 of Auth0 Device Flow signup/signin: opens browser, returns the user_code + device_code so the model can surface them and chain to log10x_signin_complete' },
@@ -1221,9 +1366,13 @@ const REGISTERED_TOOLS = [
1221
1366
  { name: 'log10x_signout', intent: 'Wipe ~/.log10x/credentials and fall back to demo mode (or lower-priority config); does not revoke the key on the backend' },
1222
1367
  { name: 'log10x_update_settings', intent: 'Update user metadata (analyzer cost, AI provider, etc.) via POST /api/v1/user' },
1223
1368
  { name: 'log10x_create_env', intent: 'Create a new Log10x environment on the account; pairs with log10x_advise_install for end-to-end provision-and-install' },
1224
- { name: 'log10x_update_env', intent: 'Rename an env or change the default — requires backend PUT route (see backend PR #62)' },
1369
+ { name: 'log10x_update_env', intent: 'Rename an env or change the default; requires the backend PUT route' },
1225
1370
  { name: 'log10x_delete_env', intent: 'Delete an env (destructive, irrecoverable) — requires confirm_name matching the env\'s name' },
1226
1371
  { name: 'log10x_set_gitops_repo', intent: 'Write gitops.repo to ~/.log10x/envs.json so configure_engine can author cap-CSV PRs; confirm="set-now" required' },
1372
+ { name: 'log10x_dest_set', intent: 'Update the SIEM destination block (siem_vendor / region / log_group_prefix / ingest_url) on an env-config document; idempotent, validates new doc against schema before write' },
1373
+ { name: 'log10x_env_validate', intent: 'Schema parse + cross-field sanity check on a stored env-config document — region/vendor pairing, ingest_url shape, cluster vs offload type alignment, active-offload presence' },
1374
+ { name: 'log10x_env_diff_vs_envvars', intent: 'Diff stored env-config doc against LOG10X_* env vars — surfaces silent-ignored env vars with per-field remediation; pure-on-prem-store configs return empty diff' },
1375
+ { name: 'log10x_retriever_register', intent: 'Attach the Retriever endpoint + queue coordinates to an existing env-config document. Requires log10x_env_register to have created the env first.' },
1227
1376
  { name: 'log10x_rotate_api_key', intent: 'Rotate the Log10x API key (destructive) — old key invalidated immediately, new one persisted to ~/.log10x/credentials' },
1228
1377
  { name: 'log10x_customer_metrics_query', intent: 'Direct PromQL passthrough to the customer metric backend (escape hatch for cross-pillar investigations)' },
1229
1378
  { name: 'log10x_discover_join', intent: 'Auto-discover the join label between Log10x pattern metrics and the customer metric backend via Jaccard similarity' },
@@ -1235,14 +1384,16 @@ const REGISTERED_TOOLS = [
1235
1384
  { name: 'log10x_poc_from_local', intent: 'Run the POC from local kubectl logs (no SIEM credentials needed); industry-pricing matrix instead of bill prediction' },
1236
1385
  { name: 'log10x_discover_env', intent: 'Read-only probe of k8s + AWS — returns a snapshot_id the advise_* tools consume' },
1237
1386
  { name: 'log10x_advise_install', intent: 'Install wizard for Reporter / Receiver — walks the user through app / forwarder / backends / airgapped / license, then emits a concrete helm plan' },
1238
- { name: 'log10x_advise_retriever', intent: 'Retriever install/verify/teardown plan — standalone S3 + SQS archive + query' },
1387
+ { name: 'log10x_advise_retriever', intent: 'Retriever install/verify/teardown plan: standalone reader over the customer-owned offload S3 bucket (S3 + SQS indexer + query)' },
1239
1388
  { name: 'log10x_configure_engine', intent: 'Unified per-pattern action-plan PR author — resolves a budget to a per-pattern plan (pass | sample | compact | drop | tier_down) under a per-destination cost model and emits a gitops PR.' },
1240
1389
  { name: 'log10x_estimate_savings', intent: 'Two-mode savings estimator — forecast mode projects bytes_out + $/mo for a proposed plan under the per-destination cost model; verify mode counts realized savings from the engine `isDropped` label with cap-hit / drift / new-patterns / leakage attribution.' },
1241
1390
  { name: 'log10x_baseline', intent: 'Pre-flight readiness gate for cost-reduction tools — verifies Reporter age (default 7d), pattern-coverage stability, and absence of acute anomalies; returns structured `not_ready` with the specific gate(s) that failed.' },
1242
1391
  { name: 'log10x_commitment_report', intent: 'CFO-facing weekly aggregate against a commitment record — Bayesian Beta(2,2) confidence prior on realized savings, markdown report suitable for sharing.' },
1243
1392
  { name: 'log10x_pattern_mitigate', intent: 'Return the env-gated mitigation options + exact configs for a pattern (drop @ analyzer, drop @ forwarder, mute @ 10x, compact @ 10x) in user terms with env-capability gating' },
1244
- { name: 'log10x_overflow_contents', intent: 'Contents view of the customer-owned offload S3 bucket — per-pattern bytes, event count, time-first/last-seen, growth-rate; filtered to action=offload via the cap-CSV. Routes the agent to retriever_query for rehydration.' },
1393
+ { name: 'log10x_overflow_contents', intent: 'Contents view of the customer-owned offload S3 bucket: per-pattern bytes, event count, time-first/last-seen, growth-rate; filtered to action=offload via the cap-CSV. Routes the agent to retriever_query to inspect the offloaded cohort.' },
1245
1394
  { name: 'log10x_setup_recurring', intent: 'Progressive wizard to configure a recurring cost-reduction agent — target services, savings %, schedule, scheduler (k8s/GHA/crontab), gitops repo — emits policy.yaml + scheduler manifest' },
1395
+ { name: 'log10x_offload_add', intent: 'Append a new offload destination (s3 / gcs / azure_blob / file) to an env-config document\'s offload_destinations[]. Multi-target offload is allowed; nickname must be unique within the list.' },
1396
+ { name: 'log10x_offload_archive', intent: 'Flip an offload destination\'s status to `archived` and stamp archived_at. Kept in the list as a historical reference. Refuses when the target is the only active destination — the Receiver requires at least one.' },
1246
1397
  ];
1247
1398
  async function handleCliFlags() {
1248
1399
  const args = process.argv.slice(2);
@@ -1487,11 +1638,20 @@ async function main() {
1487
1638
  default_env: loaded.default.nickname,
1488
1639
  demo_mode: loaded.isDemoMode,
1489
1640
  demo_playground: DEMO_PLAYGROUND,
1641
+ read_only_mode: isReadOnlyMode(),
1490
1642
  manifest_loaded: manifest !== null,
1491
1643
  boot_mode: bootMode?.mode,
1492
1644
  boot_mode_backend: bootMode?.detectionPath,
1493
1645
  boot_mode_probe_ms: bootMode?.probeDurationMs,
1494
1646
  });
1647
+ // Read-only playground banner. The structured `mcp.boot` line is
1648
+ // gated behind LOG10X_MCP_LOG_LEVEL (silent by default), so operators
1649
+ // would not see read-only state in stock installs. Force a stderr
1650
+ // banner when the gate is on, regardless of log level.
1651
+ if (isReadOnlyMode()) {
1652
+ // eslint-disable-next-line no-console
1653
+ console.error(`[log10x-mcp] Running in read-only mode (LOG10X_MCP_READ_ONLY=${process.env.LOG10X_MCP_READ_ONLY}). Writer tools will return demo_read_only envelopes instead of executing.`);
1654
+ }
1495
1655
  // Surface mode-detect resolution to stderr at boot so it shows up
1496
1656
  // in MCP-client logs without needing to call `log10x_doctor`.
1497
1657
  if (bootMode) {