@memlab/mcp-server 2.18.1 → 2.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -16
- package/bin/.eslintrc.js +26 -0
- package/bin/memlab-cli.js +313 -0
- package/dist/anonymized-snapshot.d.ts +75 -0
- package/dist/anonymized-snapshot.d.ts.map +1 -0
- package/dist/anonymized-snapshot.js +101 -0
- package/dist/anonymized-snapshot.js.map +1 -0
- package/dist/artifact-classes.d.ts +86 -0
- package/dist/artifact-classes.d.ts.map +1 -0
- package/dist/artifact-classes.js +261 -0
- package/dist/artifact-classes.js.map +1 -0
- package/dist/capture-mode.d.ts +60 -0
- package/dist/capture-mode.d.ts.map +1 -0
- package/dist/capture-mode.js +39 -0
- package/dist/capture-mode.js.map +1 -0
- package/dist/eval-hints.d.ts +41 -0
- package/dist/eval-hints.d.ts.map +1 -0
- package/dist/eval-hints.js +115 -0
- package/dist/eval-hints.js.map +1 -0
- package/dist/guardrail.d.ts.map +1 -1
- package/dist/guardrail.js +13 -0
- package/dist/guardrail.js.map +1 -1
- package/dist/heap-budget.d.ts +32 -0
- package/dist/heap-budget.d.ts.map +1 -0
- package/dist/heap-budget.js +90 -0
- package/dist/heap-budget.js.map +1 -0
- package/dist/heap-shapes.d.ts +148 -0
- package/dist/heap-shapes.d.ts.map +1 -0
- package/dist/heap-shapes.js +193 -0
- package/dist/heap-shapes.js.map +1 -0
- package/dist/heap-state.d.ts +109 -2
- package/dist/heap-state.d.ts.map +1 -1
- package/dist/heap-state.js +194 -3
- package/dist/heap-state.js.map +1 -1
- package/dist/index.js +102 -1
- package/dist/index.js.map +1 -1
- package/dist/progress.d.ts +25 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +46 -0
- package/dist/progress.js.map +1 -0
- package/dist/react-shapes.d.ts +42 -0
- package/dist/react-shapes.d.ts.map +1 -0
- package/dist/react-shapes.js +149 -0
- package/dist/react-shapes.js.map +1 -0
- package/dist/result-handles.d.ts +11 -0
- package/dist/result-handles.d.ts.map +1 -0
- package/dist/result-handles.js +77 -0
- package/dist/result-handles.js.map +1 -0
- package/dist/server-version.d.ts +19 -0
- package/dist/server-version.d.ts.map +1 -0
- package/dist/server-version.js +19 -0
- package/dist/server-version.js.map +1 -0
- package/dist/snapshot-borrow.d.ts +67 -0
- package/dist/snapshot-borrow.d.ts.map +1 -0
- package/dist/snapshot-borrow.js +113 -0
- package/dist/snapshot-borrow.js.map +1 -0
- package/dist/string-index.d.ts +55 -0
- package/dist/string-index.d.ts.map +1 -0
- package/dist/string-index.js +99 -0
- package/dist/string-index.js.map +1 -0
- package/dist/tool-registry.d.ts +42 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +20 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/tools/analyze-run.d.ts +12 -0
- package/dist/tools/analyze-run.d.ts.map +1 -0
- package/dist/tools/analyze-run.js +404 -0
- package/dist/tools/analyze-run.js.map +1 -0
- package/dist/tools/anonymize-snapshot.d.ts +12 -0
- package/dist/tools/anonymize-snapshot.d.ts.map +1 -0
- package/dist/tools/anonymize-snapshot.js +159 -0
- package/dist/tools/anonymize-snapshot.js.map +1 -0
- package/dist/tools/app-config.d.ts +12 -0
- package/dist/tools/app-config.d.ts.map +1 -0
- package/dist/tools/app-config.js +217 -0
- package/dist/tools/app-config.js.map +1 -0
- package/dist/tools/app-heap.d.ts +30 -0
- package/dist/tools/app-heap.d.ts.map +1 -0
- package/dist/tools/app-heap.js +106 -0
- package/dist/tools/app-heap.js.map +1 -0
- package/dist/tools/artifact-budget.d.ts +12 -0
- package/dist/tools/artifact-budget.d.ts.map +1 -0
- package/dist/tools/artifact-budget.js +219 -0
- package/dist/tools/artifact-budget.js.map +1 -0
- package/dist/tools/async-census.d.ts +12 -0
- package/dist/tools/async-census.d.ts.map +1 -0
- package/dist/tools/async-census.js +243 -0
- package/dist/tools/async-census.js.map +1 -0
- package/dist/tools/auto-investigate.d.ts.map +1 -1
- package/dist/tools/auto-investigate.js +54 -5
- package/dist/tools/auto-investigate.js.map +1 -1
- package/dist/tools/batch.d.ts +12 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +196 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/cache-analysis.d.ts +15 -0
- package/dist/tools/cache-analysis.d.ts.map +1 -1
- package/dist/tools/cache-analysis.js +240 -14
- package/dist/tools/cache-analysis.js.map +1 -1
- package/dist/tools/census-diff.d.ts +55 -0
- package/dist/tools/census-diff.d.ts.map +1 -0
- package/dist/tools/census-diff.js +228 -0
- package/dist/tools/census-diff.js.map +1 -0
- package/dist/tools/chain-walk.d.ts +12 -0
- package/dist/tools/chain-walk.d.ts.map +1 -0
- package/dist/tools/chain-walk.js +184 -0
- package/dist/tools/chain-walk.js.map +1 -0
- package/dist/tools/class-histogram.d.ts.map +1 -1
- package/dist/tools/class-histogram.js +14 -3
- package/dist/tools/class-histogram.js.map +1 -1
- package/dist/tools/collection-diff.d.ts +44 -0
- package/dist/tools/collection-diff.d.ts.map +1 -0
- package/dist/tools/collection-diff.js +343 -0
- package/dist/tools/collection-diff.js.map +1 -0
- package/dist/tools/collection-trend.d.ts +55 -0
- package/dist/tools/collection-trend.d.ts.map +1 -0
- package/dist/tools/collection-trend.js +315 -0
- package/dist/tools/collection-trend.js.map +1 -0
- package/dist/tools/detached-dom.d.ts +5 -0
- package/dist/tools/detached-dom.d.ts.map +1 -1
- package/dist/tools/detached-dom.js +368 -22
- package/dist/tools/detached-dom.js.map +1 -1
- package/dist/tools/dev-artifacts.d.ts +62 -6
- package/dist/tools/dev-artifacts.d.ts.map +1 -1
- package/dist/tools/dev-artifacts.js +685 -24
- package/dist/tools/dev-artifacts.js.map +1 -1
- package/dist/tools/diff-snapshots.d.ts.map +1 -1
- package/dist/tools/diff-snapshots.js +18 -2
- package/dist/tools/diff-snapshots.js.map +1 -1
- package/dist/tools/dom-audit.d.ts +37 -0
- package/dist/tools/dom-audit.d.ts.map +1 -0
- package/dist/tools/dom-audit.js +247 -0
- package/dist/tools/dom-audit.js.map +1 -0
- package/dist/tools/dominator-attribution.d.ts +12 -0
- package/dist/tools/dominator-attribution.d.ts.map +1 -0
- package/dist/tools/dominator-attribution.js +189 -0
- package/dist/tools/dominator-attribution.js.map +1 -0
- package/dist/tools/dominator-chain.d.ts +12 -0
- package/dist/tools/dominator-chain.d.ts.map +1 -0
- package/dist/tools/dominator-chain.js +92 -0
- package/dist/tools/dominator-chain.js.map +1 -0
- package/dist/tools/duplicate-objects.d.ts +12 -0
- package/dist/tools/duplicate-objects.d.ts.map +1 -0
- package/dist/tools/duplicate-objects.js +172 -0
- package/dist/tools/duplicate-objects.js.map +1 -0
- package/dist/tools/duplicated-strings.d.ts.map +1 -1
- package/dist/tools/duplicated-strings.js +49 -40
- package/dist/tools/duplicated-strings.js.map +1 -1
- package/dist/tools/eval-across.d.ts +12 -0
- package/dist/tools/eval-across.d.ts.map +1 -0
- package/dist/tools/eval-across.js +135 -0
- package/dist/tools/eval-across.js.map +1 -0
- package/dist/tools/eval.d.ts +79 -0
- package/dist/tools/eval.d.ts.map +1 -1
- package/dist/tools/eval.js +2523 -197
- package/dist/tools/eval.js.map +1 -1
- package/dist/tools/event-listener-leaks.d.ts.map +1 -1
- package/dist/tools/event-listener-leaks.js +42 -17
- package/dist/tools/event-listener-leaks.js.map +1 -1
- package/dist/tools/event-registry.d.ts.map +1 -1
- package/dist/tools/event-registry.js +42 -8
- package/dist/tools/event-registry.js.map +1 -1
- package/dist/tools/explain-delta.d.ts +12 -0
- package/dist/tools/explain-delta.d.ts.map +1 -0
- package/dist/tools/explain-delta.js +221 -0
- package/dist/tools/explain-delta.js.map +1 -0
- package/dist/tools/find-nodes-by-class.d.ts.map +1 -1
- package/dist/tools/find-nodes-by-class.js +112 -13
- package/dist/tools/find-nodes-by-class.js.map +1 -1
- package/dist/tools/finding-index.d.ts +203 -0
- package/dist/tools/finding-index.d.ts.map +1 -0
- package/dist/tools/finding-index.js +566 -0
- package/dist/tools/finding-index.js.map +1 -0
- package/dist/tools/for-each.d.ts.map +1 -1
- package/dist/tools/for-each.js +15 -3
- package/dist/tools/for-each.js.map +1 -1
- package/dist/tools/get-referrers.d.ts.map +1 -1
- package/dist/tools/get-referrers.js +17 -0
- package/dist/tools/get-referrers.js.map +1 -1
- package/dist/tools/get-value.d.ts +16 -0
- package/dist/tools/get-value.d.ts.map +1 -1
- package/dist/tools/get-value.js +96 -2
- package/dist/tools/get-value.js.map +1 -1
- package/dist/tools/hunt-report.d.ts +12 -0
- package/dist/tools/hunt-report.d.ts.map +1 -0
- package/dist/tools/hunt-report.js +186 -0
- package/dist/tools/hunt-report.js.map +1 -0
- package/dist/tools/hypothesis.d.ts +12 -0
- package/dist/tools/hypothesis.d.ts.map +1 -0
- package/dist/tools/hypothesis.js +280 -0
- package/dist/tools/hypothesis.js.map +1 -0
- package/dist/tools/id-space-audit.d.ts +12 -0
- package/dist/tools/id-space-audit.d.ts.map +1 -0
- package/dist/tools/id-space-audit.js +262 -0
- package/dist/tools/id-space-audit.js.map +1 -0
- package/dist/tools/identify.d.ts +12 -0
- package/dist/tools/identify.d.ts.map +1 -0
- package/dist/tools/identify.js +205 -0
- package/dist/tools/identify.js.map +1 -0
- package/dist/tools/identity-diff.d.ts +12 -0
- package/dist/tools/identity-diff.d.ts.map +1 -0
- package/dist/tools/identity-diff.js +188 -0
- package/dist/tools/identity-diff.js.map +1 -0
- package/dist/tools/intern-opportunities.d.ts.map +1 -1
- package/dist/tools/intern-opportunities.js +116 -50
- package/dist/tools/intern-opportunities.js.map +1 -1
- package/dist/tools/ladder-probe.d.ts +65 -0
- package/dist/tools/ladder-probe.d.ts.map +1 -0
- package/dist/tools/ladder-probe.js +445 -0
- package/dist/tools/ladder-probe.js.map +1 -0
- package/dist/tools/ladder.d.ts +33 -0
- package/dist/tools/ladder.d.ts.map +1 -0
- package/dist/tools/ladder.js +266 -0
- package/dist/tools/ladder.js.map +1 -0
- package/dist/tools/largest-objects.d.ts.map +1 -1
- package/dist/tools/largest-objects.js +6 -2
- package/dist/tools/largest-objects.js.map +1 -1
- package/dist/tools/leak-report.d.ts +35 -0
- package/dist/tools/leak-report.d.ts.map +1 -0
- package/dist/tools/leak-report.js +398 -0
- package/dist/tools/leak-report.js.map +1 -0
- package/dist/tools/load-snapshot.d.ts +29 -0
- package/dist/tools/load-snapshot.d.ts.map +1 -1
- package/dist/tools/load-snapshot.js +294 -23
- package/dist/tools/load-snapshot.js.map +1 -1
- package/dist/tools/map-entries.d.ts +12 -0
- package/dist/tools/map-entries.d.ts.map +1 -0
- package/dist/tools/map-entries.js +139 -0
- package/dist/tools/map-entries.js.map +1 -0
- package/dist/tools/match-object.d.ts +12 -0
- package/dist/tools/match-object.d.ts.map +1 -0
- package/dist/tools/match-object.js +165 -0
- package/dist/tools/match-object.js.map +1 -0
- package/dist/tools/metric.d.ts +13 -0
- package/dist/tools/metric.d.ts.map +1 -0
- package/dist/tools/metric.js +185 -0
- package/dist/tools/metric.js.map +1 -0
- package/dist/tools/module-attribution.d.ts +36 -0
- package/dist/tools/module-attribution.d.ts.map +1 -0
- package/dist/tools/module-attribution.js +235 -0
- package/dist/tools/module-attribution.js.map +1 -0
- package/dist/tools/next-measurement.d.ts +26 -0
- package/dist/tools/next-measurement.d.ts.map +1 -0
- package/dist/tools/next-measurement.js +149 -0
- package/dist/tools/next-measurement.js.map +1 -0
- package/dist/tools/object-cost-breakdown.d.ts.map +1 -1
- package/dist/tools/object-cost-breakdown.js +64 -0
- package/dist/tools/object-cost-breakdown.js.map +1 -1
- package/dist/tools/object-shape.d.ts.map +1 -1
- package/dist/tools/object-shape.js +53 -5
- package/dist/tools/object-shape.js.map +1 -1
- package/dist/tools/population-diff.d.ts +57 -0
- package/dist/tools/population-diff.d.ts.map +1 -0
- package/dist/tools/population-diff.js +338 -0
- package/dist/tools/population-diff.js.map +1 -0
- package/dist/tools/population-vs-owners.d.ts +12 -0
- package/dist/tools/population-vs-owners.d.ts.map +1 -0
- package/dist/tools/population-vs-owners.js +183 -0
- package/dist/tools/population-vs-owners.js.map +1 -0
- package/dist/tools/property-names.d.ts +29 -0
- package/dist/tools/property-names.d.ts.map +1 -0
- package/dist/tools/property-names.js +131 -0
- package/dist/tools/property-names.js.map +1 -0
- package/dist/tools/quick-diagnosis.d.ts.map +1 -1
- package/dist/tools/quick-diagnosis.js +7 -1
- package/dist/tools/quick-diagnosis.js.map +1 -1
- package/dist/tools/react-owners.d.ts +12 -0
- package/dist/tools/react-owners.d.ts.map +1 -0
- package/dist/tools/react-owners.js +167 -0
- package/dist/tools/react-owners.js.map +1 -0
- package/dist/tools/react-update-queues.d.ts +12 -0
- package/dist/tools/react-update-queues.d.ts.map +1 -0
- package/dist/tools/react-update-queues.js +173 -0
- package/dist/tools/react-update-queues.js.map +1 -0
- package/dist/tools/replicate.d.ts +49 -0
- package/dist/tools/replicate.d.ts.map +1 -0
- package/dist/tools/replicate.js +241 -0
- package/dist/tools/replicate.js.map +1 -0
- package/dist/tools/retainer-diff.d.ts +12 -0
- package/dist/tools/retainer-diff.d.ts.map +1 -0
- package/dist/tools/retainer-diff.js +168 -0
- package/dist/tools/retainer-diff.js.map +1 -0
- package/dist/tools/retainer-layers.d.ts +23 -0
- package/dist/tools/retainer-layers.d.ts.map +1 -0
- package/dist/tools/retainer-layers.js +179 -0
- package/dist/tools/retainer-layers.js.map +1 -0
- package/dist/tools/retainer-summary.d.ts +18 -0
- package/dist/tools/retainer-summary.d.ts.map +1 -1
- package/dist/tools/retainer-summary.js +210 -51
- package/dist/tools/retainer-summary.js.map +1 -1
- package/dist/tools/retainer-trace.d.ts.map +1 -1
- package/dist/tools/retainer-trace.js +14 -2
- package/dist/tools/retainer-trace.js.map +1 -1
- package/dist/tools/round-audit.d.ts +66 -0
- package/dist/tools/round-audit.d.ts.map +1 -0
- package/dist/tools/round-audit.js +258 -0
- package/dist/tools/round-audit.js.map +1 -0
- package/dist/tools/round-digest.d.ts +12 -0
- package/dist/tools/round-digest.d.ts.map +1 -0
- package/dist/tools/round-digest.js +190 -0
- package/dist/tools/round-digest.js.map +1 -0
- package/dist/tools/script-census.d.ts +12 -0
- package/dist/tools/script-census.d.ts.map +1 -0
- package/dist/tools/script-census.js +182 -0
- package/dist/tools/script-census.js.map +1 -0
- package/dist/tools/search-nodes.d.ts.map +1 -1
- package/dist/tools/search-nodes.js +5 -1
- package/dist/tools/search-nodes.js.map +1 -1
- package/dist/tools/search-strings.d.ts.map +1 -1
- package/dist/tools/search-strings.js +53 -7
- package/dist/tools/search-strings.js.map +1 -1
- package/dist/tools/sequence-analysis.d.ts +53 -0
- package/dist/tools/sequence-analysis.d.ts.map +1 -1
- package/dist/tools/sequence-analysis.js +313 -89
- package/dist/tools/sequence-analysis.js.map +1 -1
- package/dist/tools/server-status.d.ts.map +1 -1
- package/dist/tools/server-status.js +4 -1
- package/dist/tools/server-status.js.map +1 -1
- package/dist/tools/settle-check.d.ts +12 -0
- package/dist/tools/settle-check.d.ts.map +1 -0
- package/dist/tools/settle-check.js +188 -0
- package/dist/tools/settle-check.js.map +1 -0
- package/dist/tools/shape-histogram.d.ts.map +1 -1
- package/dist/tools/shape-histogram.js +9 -2
- package/dist/tools/shape-histogram.js.map +1 -1
- package/dist/tools/sliced-strings.d.ts.map +1 -1
- package/dist/tools/sliced-strings.js +2 -1
- package/dist/tools/sliced-strings.js.map +1 -1
- package/dist/tools/snapshots.d.ts.map +1 -1
- package/dist/tools/snapshots.js +24 -6
- package/dist/tools/snapshots.js.map +1 -1
- package/dist/tools/sparse-elements.d.ts +12 -0
- package/dist/tools/sparse-elements.d.ts.map +1 -0
- package/dist/tools/sparse-elements.js +215 -0
- package/dist/tools/sparse-elements.js.map +1 -0
- package/dist/tools/stale-collections.d.ts +17 -0
- package/dist/tools/stale-collections.d.ts.map +1 -1
- package/dist/tools/stale-collections.js +28 -0
- package/dist/tools/stale-collections.js.map +1 -1
- package/dist/tools/string-patterns.d.ts.map +1 -1
- package/dist/tools/string-patterns.js +7 -5
- package/dist/tools/string-patterns.js.map +1 -1
- package/dist/tools/tools-index.d.ts +12 -0
- package/dist/tools/tools-index.d.ts.map +1 -0
- package/dist/tools/tools-index.js +492 -0
- package/dist/tools/tools-index.js.map +1 -0
- package/dist/tools/trace-all.d.ts +42 -0
- package/dist/tools/trace-all.d.ts.map +1 -0
- package/dist/tools/trace-all.js +263 -0
- package/dist/tools/trace-all.js.map +1 -0
- package/dist/tools/unit-cost.d.ts +12 -0
- package/dist/tools/unit-cost.d.ts.map +1 -0
- package/dist/tools/unit-cost.js +219 -0
- package/dist/tools/unit-cost.js.map +1 -0
- package/dist/tools/verify-fix.d.ts +23 -0
- package/dist/tools/verify-fix.d.ts.map +1 -0
- package/dist/tools/verify-fix.js +219 -0
- package/dist/tools/verify-fix.js.map +1 -0
- package/dist/tools/weakmap-entries.d.ts.map +1 -1
- package/dist/tools/weakmap-entries.js +6 -1
- package/dist/tools/weakmap-entries.js.map +1 -1
- package/dist/tools/weakref-census.d.ts +15 -0
- package/dist/tools/weakref-census.d.ts.map +1 -0
- package/dist/tools/weakref-census.js +147 -0
- package/dist/tools/weakref-census.js.map +1 -0
- package/dist/tools/what-if.d.ts +12 -0
- package/dist/tools/what-if.d.ts.map +1 -0
- package/dist/tools/what-if.js +285 -0
- package/dist/tools/what-if.js.map +1 -0
- package/dist/utils.d.ts +238 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +507 -15
- package/dist/utils.js.map +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @oncall memory_lab
|
|
9
|
+
*/
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { getCurrentHandle, getMetadataByHandle, getSnapshotByHandle, listSnapshots, } from '../heap-state.js';
|
|
12
|
+
import { clusterByRetainerPath, renderClusterChain, selectPopulation, } from './trace-all.js';
|
|
13
|
+
import { errorResult, formatNumber, markdownTable, toolResult, } from '../utils.js';
|
|
14
|
+
export function registerRetainerDiff(server) {
|
|
15
|
+
server.tool('memlab_retainer_diff', 'A population grew between two snapshots — did it grow along the SAME retention path, or did a new one appear?\n\n' +
|
|
16
|
+
'This is the question a count diff cannot answer and the one that decides what to fix. "42,000 → 61,000 listener records" is consistent with two completely different bugs: an existing owner accumulating faster, or a second owner that did not exist before. A count, a class histogram and a population diff all read identically in the two cases.\n\n' +
|
|
17
|
+
'Clusters the full population by retainer path in BOTH snapshots and reports paths that are new, grew, shrank or vanished. Both snapshots must be resident (load with `keep_previous: true`); the active snapshot is not changed.', {
|
|
18
|
+
before_handle: z
|
|
19
|
+
.string()
|
|
20
|
+
.describe('Handle of the earlier snapshot (memlab_snapshots lists them).'),
|
|
21
|
+
after_handle: z.string().describe('Handle of the later snapshot.'),
|
|
22
|
+
class_name: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Population: every instance of this class.'),
|
|
26
|
+
shape: z
|
|
27
|
+
.array(z.string())
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Population: every object carrying ALL of these properties — the selector that survives minification, and the one to use here since node ids and class names are not comparable across captures.'),
|
|
30
|
+
framework_filter: z
|
|
31
|
+
.boolean()
|
|
32
|
+
.optional()
|
|
33
|
+
.default(true)
|
|
34
|
+
.describe('Collapse V8/framework internals out of the path signature so structurally identical application paths compare equal (default true).'),
|
|
35
|
+
limit: z
|
|
36
|
+
.number()
|
|
37
|
+
.optional()
|
|
38
|
+
.default(10)
|
|
39
|
+
.describe('Rows in the change table (default 10).'),
|
|
40
|
+
max_depth: z
|
|
41
|
+
.number()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('Compare on the first N hops from the GC ROOT instead of the whole path. Defaults to the whole path; use 5-7 when the full-depth signature fragments into roughly one path per object (a per-chat property edge or a per-event listener array does that), which otherwise makes every path look new or vanished.'),
|
|
44
|
+
max_trace: z
|
|
45
|
+
.number()
|
|
46
|
+
.optional()
|
|
47
|
+
.default(200000)
|
|
48
|
+
.describe('Per-snapshot bound on nodes traced (default 200,000).'),
|
|
49
|
+
}, async ({ before_handle, after_handle, class_name, shape, framework_filter, limit, max_depth, max_trace, }) => {
|
|
50
|
+
try {
|
|
51
|
+
if ((class_name == null) === (shape == null || shape.length === 0)) {
|
|
52
|
+
return errorResult(new Error('Pass exactly one of class_name / shape. Node ids are per-capture and cannot select a population in two snapshots, which is why node_ids is not accepted here.'));
|
|
53
|
+
}
|
|
54
|
+
const resolve = (h) => {
|
|
55
|
+
const snap = getSnapshotByHandle(h);
|
|
56
|
+
const meta = getMetadataByHandle(h);
|
|
57
|
+
if (snap == null || meta == null) {
|
|
58
|
+
const available = listSnapshots()
|
|
59
|
+
.map(m => m.handle)
|
|
60
|
+
.join(', ');
|
|
61
|
+
throw new Error(`No resident snapshot with handle "${h}". Resident: ${available === '' ? '(none)' : available}. Load both rungs with memlab_load_snapshot({keep_previous: true}).`);
|
|
62
|
+
}
|
|
63
|
+
if (meta.light) {
|
|
64
|
+
throw new Error(`Snapshot "${h}" was loaded with light: true, which skips the path/dominator pass — there are no retainer paths to compare. Reload it with light: false.`);
|
|
65
|
+
}
|
|
66
|
+
return { snap, meta };
|
|
67
|
+
};
|
|
68
|
+
const before = resolve(before_handle);
|
|
69
|
+
const after = resolve(after_handle);
|
|
70
|
+
if (before_handle === after_handle) {
|
|
71
|
+
return errorResult(new Error('before_handle and after_handle are the same snapshot.'));
|
|
72
|
+
}
|
|
73
|
+
const sel = { className: class_name, shape };
|
|
74
|
+
const b = clusterByRetainerPath(selectPopulation(before.snap, sel), framework_filter, max_trace, max_depth);
|
|
75
|
+
const a = clusterByRetainerPath(selectPopulation(after.snap, sel), framework_filter, max_trace, max_depth);
|
|
76
|
+
if (b.clusters.length === 0 && a.clusters.length === 0) {
|
|
77
|
+
return toolResult('The population is empty (or unreachable) in both snapshots — nothing to compare. Check the selector with `memlab_class_histogram` / `memlab_shape_histogram` on each handle.');
|
|
78
|
+
}
|
|
79
|
+
const beforeByKey = new Map(b.clusters.map(c => [c.key, c]));
|
|
80
|
+
const afterByKey = new Map(a.clusters.map(c => [c.key, c]));
|
|
81
|
+
const rows = [];
|
|
82
|
+
for (const key of new Set([
|
|
83
|
+
...beforeByKey.keys(),
|
|
84
|
+
...afterByKey.keys(),
|
|
85
|
+
])) {
|
|
86
|
+
const bc = beforeByKey.get(key);
|
|
87
|
+
const ac = afterByKey.get(key);
|
|
88
|
+
const representative = ac ?? bc;
|
|
89
|
+
if (representative == null)
|
|
90
|
+
continue;
|
|
91
|
+
const beforeCount = bc?.count ?? 0;
|
|
92
|
+
const afterCount = ac?.count ?? 0;
|
|
93
|
+
if (beforeCount === afterCount)
|
|
94
|
+
continue;
|
|
95
|
+
rows.push({
|
|
96
|
+
key,
|
|
97
|
+
before: beforeCount,
|
|
98
|
+
after: afterCount,
|
|
99
|
+
delta: afterCount - beforeCount,
|
|
100
|
+
chain: renderClusterChain(representative.steps),
|
|
101
|
+
exampleAfter: ac?.exampleIds[0] ?? null,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
rows.sort((x, y) => Math.abs(y.delta) - Math.abs(x.delta));
|
|
105
|
+
const bTotal = b.traced;
|
|
106
|
+
const aTotal = a.traced;
|
|
107
|
+
const label = class_name != null
|
|
108
|
+
? `class \`${class_name}\``
|
|
109
|
+
: `shape \`{${(shape ?? []).join(', ')}}\``;
|
|
110
|
+
const lines = [
|
|
111
|
+
`## Retention paths for ${label}: ${before.meta.fileName} → ${after.meta.fileName}`,
|
|
112
|
+
'',
|
|
113
|
+
`Population: **${formatNumber(bTotal)} → ${formatNumber(aTotal)}** traced objects (${aTotal - bTotal >= 0 ? '+' : ''}${formatNumber(aTotal - bTotal)}), across **${formatNumber(b.clusters.length)} → ${formatNumber(a.clusters.length)}** distinct paths.`,
|
|
114
|
+
];
|
|
115
|
+
if (b.truncated || a.truncated) {
|
|
116
|
+
lines.push('', `⚠ The ${formatNumber(max_trace)}-node trace bound was hit in ${b.truncated && a.truncated ? 'both snapshots' : b.truncated ? 'the BEFORE snapshot' : 'the AFTER snapshot'}, so the counts below are of a partial sweep and the two sides may not be comparable. Raise \`max_trace\`.`);
|
|
117
|
+
}
|
|
118
|
+
if (max_depth == null &&
|
|
119
|
+
Math.max(b.clusters.length, a.clusters.length) >
|
|
120
|
+
Math.max(bTotal, aTotal) * 0.5) {
|
|
121
|
+
lines.push('', `⚠ ${formatNumber(a.clusters.length)} paths for ${formatNumber(aTotal)} objects — the full-depth signature is keying on a per-instance hop, so paths do not line up across captures and almost everything below will read as new or vanished. Re-run with \`max_depth: 5\` (or 6-7) to compare owners rather than instances.`);
|
|
122
|
+
}
|
|
123
|
+
if (rows.length === 0) {
|
|
124
|
+
lines.push('', '**Every path holds exactly the same count in both snapshots.** The population did not change along any retention path — if a total did change, it changed somewhere this selector does not cover.');
|
|
125
|
+
return toolResult(lines.join('\n'));
|
|
126
|
+
}
|
|
127
|
+
const newPaths = rows.filter(r => r.before === 0);
|
|
128
|
+
const grown = rows.filter(r => r.before > 0 && r.delta > 0);
|
|
129
|
+
const gone = rows.filter(r => r.after === 0);
|
|
130
|
+
lines.push('', newPaths.length > 0
|
|
131
|
+
? `**${formatNumber(newPaths.length)} path(s) are NEW**, holding ${formatNumber(newPaths.reduce((s, r) => s + r.after, 0))} object(s) that had no counterpart in the earlier snapshot. A new path is a different bug from an existing one accumulating: the owner did not exist before, so a fix aimed at the pre-existing owner will not touch it.`
|
|
132
|
+
: `**No new paths.** All of the change is existing owners holding more or fewer objects, so the retention MECHANISM is unchanged and the question is what is driving the rate.`);
|
|
133
|
+
if (grown.length > 0 && newPaths.length > 0) {
|
|
134
|
+
lines.push('', `${formatNumber(grown.length)} pre-existing path(s) also grew, by ${formatNumber(grown.reduce((s, r) => s + r.delta, 0))} object(s) in total — both mechanisms are active, so sizing a fix against either one alone will over-promise.`);
|
|
135
|
+
}
|
|
136
|
+
lines.push('', markdownTable(['Change', 'Before', 'After', 'Δ', 'Example'], rows
|
|
137
|
+
.slice(0, limit)
|
|
138
|
+
.map(r => [
|
|
139
|
+
r.before === 0
|
|
140
|
+
? 'NEW'
|
|
141
|
+
: r.after === 0
|
|
142
|
+
? 'gone'
|
|
143
|
+
: r.delta > 0
|
|
144
|
+
? 'grew'
|
|
145
|
+
: 'shrank',
|
|
146
|
+
formatNumber(r.before),
|
|
147
|
+
formatNumber(r.after),
|
|
148
|
+
`${r.delta >= 0 ? '+' : ''}${formatNumber(r.delta)}`,
|
|
149
|
+
r.exampleAfter != null ? `@${r.exampleAfter}` : '—',
|
|
150
|
+
]), new Set([1, 2, 3])));
|
|
151
|
+
if (rows.length > limit) {
|
|
152
|
+
lines.push('', `_${formatNumber(rows.length - limit)} further changed path(s) not shown; raise \`limit\`._`);
|
|
153
|
+
}
|
|
154
|
+
rows.slice(0, Math.min(limit, 5)).forEach((r, i) => {
|
|
155
|
+
lines.push('', `### ${i + 1}. ${r.before === 0 ? 'NEW' : r.after === 0 ? 'gone' : r.delta > 0 ? 'grew' : 'shrank'} ${formatNumber(r.before)} → ${formatNumber(r.after)}`, '', r.chain);
|
|
156
|
+
});
|
|
157
|
+
if (gone.length > 0) {
|
|
158
|
+
lines.push('', `_${formatNumber(gone.length)} path(s) present before and absent after (${formatNumber(gone.reduce((s, x) => s + x.before, 0))} objects). That is usually a genuine release, but it is also what a differently-shaped path looks like after a code change renamed something in it — the signature includes class names._`);
|
|
159
|
+
}
|
|
160
|
+
lines.push('', '_Paths are matched by structural signature, so a rename anywhere along a chain reads as one path vanishing and another appearing. Bytes are not compared here — use `memlab_what_if` on a cluster for what a fix at one owner would free._', '', `Active snapshot unchanged (${getCurrentHandle() ?? 'none'}).`);
|
|
161
|
+
return toolResult(lines.join('\n'));
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
return errorResult(err);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=retainer-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retainer-diff.js","sourceRoot":"","sources":["../../src/tools/retainer-diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @oncall memory_lab
|
|
9
|
+
*/
|
|
10
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
|
+
import type { IHeapSnapshot } from '@memlab/core';
|
|
12
|
+
/**
|
|
13
|
+
* Reachability from the synthetic GC roots with one node treated as a sink:
|
|
14
|
+
* `blocked` is marked reached (so it is not reported as unreachable) but its
|
|
15
|
+
* outgoing edges are never followed. Anything left unmarked is reachable ONLY
|
|
16
|
+
* through `blocked`, i.e. it lives inside that node's retained subtree.
|
|
17
|
+
*
|
|
18
|
+
* Mirrors `computeReachableWithoutDevRoots` in dev-artifacts.ts, which does the
|
|
19
|
+
* same walk with the dev/automation roots as sinks.
|
|
20
|
+
*/
|
|
21
|
+
export declare function computeReachableWithoutNode(snapshot: IHeapSnapshot, blockedId: number): Uint8Array;
|
|
22
|
+
export declare function registerRetainerLayers(server: McpServer): void;
|
|
23
|
+
//# sourceMappingURL=retainer-layers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retainer-layers.d.ts","sourceRoot":"","sources":["../../src/tools/retainer-layers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAuB,aAAa,EAAC,MAAM,cAAc,CAAC;AAatE;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,MAAM,GAChB,UAAU,CAmBZ;AAqBD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6K9D"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @format
|
|
8
|
+
* @oncall memory_lab
|
|
9
|
+
*/
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { getSnapshot } from '../heap-state.js';
|
|
12
|
+
import { errorResult, formatBytes, formatNumber, isNodeWorthInspecting, markdownTable, toolResult, truncateNodeName, } from '../utils.js';
|
|
13
|
+
/**
|
|
14
|
+
* Reachability from the synthetic GC roots with one node treated as a sink:
|
|
15
|
+
* `blocked` is marked reached (so it is not reported as unreachable) but its
|
|
16
|
+
* outgoing edges are never followed. Anything left unmarked is reachable ONLY
|
|
17
|
+
* through `blocked`, i.e. it lives inside that node's retained subtree.
|
|
18
|
+
*
|
|
19
|
+
* Mirrors `computeReachableWithoutDevRoots` in dev-artifacts.ts, which does the
|
|
20
|
+
* same walk with the dev/automation roots as sinks.
|
|
21
|
+
*/
|
|
22
|
+
export function computeReachableWithoutNode(snapshot, blockedId) {
|
|
23
|
+
const reached = new Uint8Array(snapshot.nodes.length);
|
|
24
|
+
const stack = [];
|
|
25
|
+
snapshot.nodes.forEach((node) => {
|
|
26
|
+
if (node.type !== 'synthetic' && node.id > 3)
|
|
27
|
+
return;
|
|
28
|
+
if (reached[node.nodeIndex])
|
|
29
|
+
return;
|
|
30
|
+
reached[node.nodeIndex] = 1;
|
|
31
|
+
if (node.id !== blockedId)
|
|
32
|
+
stack.push(node);
|
|
33
|
+
});
|
|
34
|
+
while (stack.length > 0) {
|
|
35
|
+
const node = stack.pop();
|
|
36
|
+
node.forEachReference((edge) => {
|
|
37
|
+
const to = edge.toNode;
|
|
38
|
+
if (reached[to.nodeIndex])
|
|
39
|
+
return;
|
|
40
|
+
reached[to.nodeIndex] = 1;
|
|
41
|
+
if (to.id !== blockedId)
|
|
42
|
+
stack.push(to);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return reached;
|
|
46
|
+
}
|
|
47
|
+
function groupKey(g) {
|
|
48
|
+
return `${g.fromType}\u0000${g.fromName}\u0000${g.edgeType}\u0000${g.edgeName}`;
|
|
49
|
+
}
|
|
50
|
+
export function registerRetainerLayers(server) {
|
|
51
|
+
server.tool('memlab_retainer_layers', 'Counterfactual retainer analysis: answer "if I cut this edge, would the object still be retained, and by what?". ' +
|
|
52
|
+
"Enumerates the INDEPENDENT retaining edges into a node — the incoming edges whose source is still reachable from a GC root when the target itself is treated as a sink — so back-edges from inside the target's own retained subtree are excluded. " +
|
|
53
|
+
'The object becomes unreachable only when ALL independent retainers are cut, so a count above 1 means any single fix leaves it retained by the others. ' +
|
|
54
|
+
'Use this BEFORE writing a fix: memlab_retainer_trace and memlab_detached_dom --group_by retainer each report ONE path/grouping, which is why a stack of caches holding the same object looks like a single retainer and a partial fix reports a misleading win. ' +
|
|
55
|
+
'Complements memlab_dominator_chain (accountability chain upward) and memlab_retainer_trace (one shortest path).', {
|
|
56
|
+
node_id: z
|
|
57
|
+
.number()
|
|
58
|
+
.int()
|
|
59
|
+
.describe('The numeric ID of the heap node to analyze'),
|
|
60
|
+
max_retainers: z
|
|
61
|
+
.number()
|
|
62
|
+
.int()
|
|
63
|
+
.min(1)
|
|
64
|
+
.optional()
|
|
65
|
+
.default(20)
|
|
66
|
+
.describe('Maximum number of distinct retainer groups to list (default 20). Grouping is by (source class, source type, edge name, edge type).'),
|
|
67
|
+
}, async ({ node_id, max_retainers }) => {
|
|
68
|
+
try {
|
|
69
|
+
const snapshot = getSnapshot();
|
|
70
|
+
const target = snapshot.getNodeById(node_id);
|
|
71
|
+
if (!target) {
|
|
72
|
+
return errorResult(`Node with id ${node_id} not found`);
|
|
73
|
+
}
|
|
74
|
+
const reached = computeReachableWithoutNode(snapshot, target.id);
|
|
75
|
+
// Partition incoming edges into independent retainers (source still
|
|
76
|
+
// reachable with the target blocked) and internal back-edges (source
|
|
77
|
+
// only reachable through the target).
|
|
78
|
+
const groups = new Map();
|
|
79
|
+
let independentEdgeCount = 0;
|
|
80
|
+
let backEdgeCount = 0;
|
|
81
|
+
for (const edge of target.referrers) {
|
|
82
|
+
const from = edge.fromNode;
|
|
83
|
+
// A self-edge (V8 emits them for DOM nodes) cannot retain the target,
|
|
84
|
+
// but the sink walk marks the target itself reached so it would
|
|
85
|
+
// otherwise pass the test below. Count it with the back-edges.
|
|
86
|
+
if (from.id === target.id || !reached[from.nodeIndex]) {
|
|
87
|
+
backEdgeCount++;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
independentEdgeCount++;
|
|
91
|
+
const g = {
|
|
92
|
+
fromName: from.name,
|
|
93
|
+
fromType: from.type,
|
|
94
|
+
edgeName: String(edge.name_or_index),
|
|
95
|
+
edgeType: edge.type,
|
|
96
|
+
};
|
|
97
|
+
const key = groupKey(g);
|
|
98
|
+
const existing = groups.get(key);
|
|
99
|
+
if (existing) {
|
|
100
|
+
existing.count++;
|
|
101
|
+
if (from.retainedSize > existing.maxRetained) {
|
|
102
|
+
existing.maxRetained = from.retainedSize;
|
|
103
|
+
existing.exampleFromId = from.id;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
groups.set(key, {
|
|
108
|
+
...g,
|
|
109
|
+
count: 1,
|
|
110
|
+
exampleFromId: from.id,
|
|
111
|
+
maxRetained: from.retainedSize,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const sorted = [...groups.values()].sort((a, b) => b.maxRetained - a.maxRetained || b.count - a.count);
|
|
116
|
+
const shown = sorted.slice(0, max_retainers);
|
|
117
|
+
const targetLabel = truncateNodeName(target.name, target.type, target.self_size, 60);
|
|
118
|
+
const lines = [
|
|
119
|
+
`## Retainer layers for @${target.id} (${targetLabel})`,
|
|
120
|
+
'',
|
|
121
|
+
`Retained ${formatBytes(target.retainedSize)}. ` +
|
|
122
|
+
`${formatNumber(independentEdgeCount)} independent retaining edge(s) in ${formatNumber(sorted.length)} group(s)` +
|
|
123
|
+
(backEdgeCount > 0
|
|
124
|
+
? `; ${formatNumber(backEdgeCount)} back-edge(s) from inside its own retained subtree excluded.`
|
|
125
|
+
: '.'),
|
|
126
|
+
'',
|
|
127
|
+
];
|
|
128
|
+
if (independentEdgeCount === 0) {
|
|
129
|
+
lines.push(target.type === 'synthetic' || target.id <= 3
|
|
130
|
+
? '**This node is a GC root itself** — it has no external retainer to cut.'
|
|
131
|
+
: "**No independent retainer found.** Nothing outside this object's own subtree references it, so it is already GC-eligible (or reachable only via weak references). It should not be counted toward a leak total — see `memlab_detached_dom --only_with_retainer_path`.");
|
|
132
|
+
return toolResult(lines.join('\n'));
|
|
133
|
+
}
|
|
134
|
+
// The counterfactual verdict. Cutting one independent edge cannot change
|
|
135
|
+
// the reachability of any other independent source, because every such
|
|
136
|
+
// source is reachable on a path that does not pass through the target.
|
|
137
|
+
if (independentEdgeCount === 1) {
|
|
138
|
+
const only = sorted[0];
|
|
139
|
+
lines.push(`**Sole retainer — cutting it frees the object.** \`${only.edgeName}\` (${only.edgeType}) from @${only.exampleFromId} ${only.fromName} (${only.fromType}). ` +
|
|
140
|
+
`Fixing this one reference reclaims ${formatBytes(target.retainedSize)}.`, '');
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
lines.push(`**${formatNumber(independentEdgeCount)} independent retainers — cutting any ONE leaves the object retained by the other ${formatNumber(independentEdgeCount - 1)}.** ` +
|
|
144
|
+
'A fix that removes a single one of these will show little or no memory win, and a before/after measurement that attributes the whole object to the removed retainer will overstate the result. ' +
|
|
145
|
+
'All of them must be cut to reclaim the object.', '');
|
|
146
|
+
}
|
|
147
|
+
lines.push(markdownTable(['#', 'edges', 'from', 'edge', 'example', 'source retains'], shown.map((g, i) => [
|
|
148
|
+
String(i + 1),
|
|
149
|
+
formatNumber(g.count),
|
|
150
|
+
`${g.fromName} (${g.fromType})`,
|
|
151
|
+
`${g.edgeName} (${g.edgeType})`,
|
|
152
|
+
`@${g.exampleFromId}`,
|
|
153
|
+
formatBytes(g.maxRetained),
|
|
154
|
+
])));
|
|
155
|
+
if (sorted.length > shown.length) {
|
|
156
|
+
lines.push('', `_${formatNumber(sorted.length - shown.length)} more retainer group(s) not shown — raise max_retainers to see them._`);
|
|
157
|
+
}
|
|
158
|
+
// A dominator that is not the GC-roots super-node means every path to
|
|
159
|
+
// the target passes through it, so it is a single choke point even when
|
|
160
|
+
// several independent edges exist.
|
|
161
|
+
const dom = target.dominatorNode;
|
|
162
|
+
if (independentEdgeCount > 1 &&
|
|
163
|
+
dom &&
|
|
164
|
+
dom.id !== target.id &&
|
|
165
|
+
dom.name !== '(GC roots)' &&
|
|
166
|
+
isNodeWorthInspecting(dom)) {
|
|
167
|
+
lines.push('', `**Single choke point:** every path still passes through the immediate dominator @${dom.id} ${truncateNodeName(dom.name, dom.type, dom.self_size, 60)} (retains ${formatBytes(dom.retainedSize)}). ` +
|
|
168
|
+
'Cutting there frees the target in one change instead of cutting each retainer. Use `memlab_dominator_chain` to walk further up.');
|
|
169
|
+
}
|
|
170
|
+
lines.push('', '_Independence test: an incoming edge counts only if its source is still reachable from a GC root when the target is treated as a sink. ' +
|
|
171
|
+
"Edges failing that test originate inside the target's own retained subtree and cannot keep it alive._");
|
|
172
|
+
return toolResult(lines.join('\n'));
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
return errorResult(err);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=retainer-layers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retainer-layers.js","sourceRoot":"","sources":["../../src/tools/retainer-layers.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,5 +8,23 @@
|
|
|
8
8
|
* @oncall memory_lab
|
|
9
9
|
*/
|
|
10
10
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
|
+
import type { IHeapNode } from '@memlab/core';
|
|
12
|
+
export interface TraceStep {
|
|
13
|
+
name: string;
|
|
14
|
+
type: string;
|
|
15
|
+
edgeName?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function getRetainerTrace(node: IHeapNode): TraceStep[] | null;
|
|
18
|
+
export declare function traceToKey(steps: TraceStep[], frameworkFilter: boolean): string;
|
|
19
|
+
/**
|
|
20
|
+
* Shorten one hop's label without changing which node it names.
|
|
21
|
+
*
|
|
22
|
+
* Order matters: elide C++ template arguments FIRST (that is where the bytes
|
|
23
|
+
* are and the elision is information-preserving about the outer type), and only
|
|
24
|
+
* then fall back to a blunt truncation.
|
|
25
|
+
*/
|
|
26
|
+
export declare function shortenPath(name: string): string;
|
|
27
|
+
export declare function formatTraceChain(steps: TraceStep[]): string;
|
|
28
|
+
export declare const COMPACT_GAP_MARKER = "\u22EF";
|
|
11
29
|
export declare function registerRetainerSummary(server: McpServer): void;
|
|
12
30
|
//# sourceMappingURL=retainer-summary.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retainer-summary.d.ts","sourceRoot":"","sources":["../../src/tools/retainer-summary.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"retainer-summary.d.ts","sourceRoot":"","sources":["../../src/tools/retainer-summary.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAC,SAAS,EAAY,MAAM,cAAc,CAAC;AAcvD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA6BD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,IAAI,CAuBpE;AAmBD,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,EAAE,EAClB,eAAe,EAAE,OAAO,GACvB,MAAM,CAaR;AAiBD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBhD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAe3D;AAKD,eAAO,MAAM,kBAAkB,WAAM,CAAC;AAgDtC,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4d/D"}
|