memorydetective 1.12.0 → 1.14.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/CHANGELOG.md +65 -1
- package/README.md +15 -5
- package/USAGE.md +29 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/schemaDiscovery.d.ts +88 -0
- package/dist/parsers/schemaDiscovery.js +144 -0
- package/dist/parsers/schemaDiscovery.js.map +1 -0
- package/dist/parsers/xctraceXml.d.ts +5 -0
- package/dist/parsers/xctraceXml.js +3 -0
- package/dist/parsers/xctraceXml.js.map +1 -1
- package/dist/runtime/prompts.js +49 -0
- package/dist/runtime/prompts.js.map +1 -1
- package/dist/tools/analyzeAllocations.d.ts +5 -1
- package/dist/tools/analyzeAllocations.js +17 -1
- package/dist/tools/analyzeAllocations.js.map +1 -1
- package/dist/tools/analyzeAnimationHitches.d.ts +5 -1
- package/dist/tools/analyzeAnimationHitches.js +17 -1
- package/dist/tools/analyzeAnimationHitches.js.map +1 -1
- package/dist/tools/analyzeAppLaunch.d.ts +3 -0
- package/dist/tools/analyzeAppLaunch.js +17 -1
- package/dist/tools/analyzeAppLaunch.js.map +1 -1
- package/dist/tools/analyzeHangs.d.ts +63 -3
- package/dist/tools/analyzeHangs.js +143 -19
- package/dist/tools/analyzeHangs.js.map +1 -1
- package/dist/tools/analyzeNetworkActivity.d.ts +99 -0
- package/dist/tools/analyzeNetworkActivity.js +312 -0
- package/dist/tools/analyzeNetworkActivity.js.map +1 -0
- package/dist/tools/analyzeTimeProfile.d.ts +10 -1
- package/dist/tools/analyzeTimeProfile.js +63 -8
- package/dist/tools/analyzeTimeProfile.js.map +1 -1
- package/dist/tools/captureScenarioState.d.ts +2 -2
- package/dist/tools/countAlive.d.ts +35 -1
- package/dist/tools/countAlive.js +124 -29
- package/dist/tools/countAlive.js.map +1 -1
- package/dist/tools/inspectTrace.js +112 -18
- package/dist/tools/inspectTrace.js.map +1 -1
- package/dist/tools/recordTimeProfile.d.ts +83 -0
- package/dist/tools/recordTimeProfile.js +135 -0
- package/dist/tools/recordTimeProfile.js.map +1 -1
- package/dist/tools/replayScenario.d.ts +4 -4
- package/dist/tools/summarizeTrace.d.ts +147 -0
- package/dist/tools/summarizeTrace.js +424 -0
- package/dist/tools/summarizeTrace.js.map +1 -0
- package/dist/tools/verifyFix.d.ts +27 -0
- package/dist/tools/verifyFix.js +78 -4
- package/dist/tools/verifyFix.js.map +1 -1
- package/dist/types.d.ts +28 -0
- package/package.json +2 -2
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
3
4
|
import { resolve as resolvePath, dirname, isAbsolute as isAbsolutePath, join as joinPath, } from "node:path";
|
|
4
5
|
import { runCommand } from "../runtime/exec.js";
|
|
5
6
|
import { getSecurityFlags, maxRecordingExceededMessage, } from "../runtime/securityFlags.js";
|
|
7
|
+
import { getPlatformAdvisory } from "../runtime/platformCheck.js";
|
|
6
8
|
/**
|
|
7
9
|
* Base shape, exposed so the MCP layer can read `.shape` (ZodEffects from
|
|
8
10
|
* `.superRefine()` doesn't expose shape).
|
|
@@ -91,6 +93,115 @@ const XCTRACE_TIMEOUT_GRACE_SEC = 30;
|
|
|
91
93
|
* when xctrace is wedged.
|
|
92
94
|
*/
|
|
93
95
|
const XCTRACE_GRACEFUL_KILL_MS = 10_000;
|
|
96
|
+
const PREFLIGHT_TIME_LIMIT_SEC = 2;
|
|
97
|
+
const PREFLIGHT_WRAPPER_TIMEOUT_MS = (PREFLIGHT_TIME_LIMIT_SEC + 6) * 1000;
|
|
98
|
+
const PREFLIGHT_GRACEFUL_KILL_MS = 2000;
|
|
99
|
+
/**
|
|
100
|
+
* Returns true when a pre-flight probe should run before the user's
|
|
101
|
+
* actual recording. v1.14 item H.
|
|
102
|
+
*
|
|
103
|
+
* - `MEMORYDETECTIVE_PREFLIGHT_XCTRACE=1` forces preflight on.
|
|
104
|
+
* - `MEMORYDETECTIVE_PREFLIGHT_XCTRACE=0` forces it off.
|
|
105
|
+
* - Default: auto-enabled when host is macOS 26.x AND target is a
|
|
106
|
+
* simulator AND attach mode (`--attach`, not `--launch`). The set of
|
|
107
|
+
* configurations where the regression is known to fire.
|
|
108
|
+
*
|
|
109
|
+
* The `osPlatform` and `osRelease` params are threaded through to
|
|
110
|
+
* `getPlatformAdvisory` so tests can simulate non-macOS-26 hosts even
|
|
111
|
+
* when running on a real macOS 26.x machine.
|
|
112
|
+
*/
|
|
113
|
+
export function shouldPreflightXctrace(input, env = process.env, osPlatform, osRelease) {
|
|
114
|
+
const explicit = env.MEMORYDETECTIVE_PREFLIGHT_XCTRACE;
|
|
115
|
+
if (explicit === "1")
|
|
116
|
+
return true;
|
|
117
|
+
if (explicit === "0")
|
|
118
|
+
return false;
|
|
119
|
+
// Auto: only when the known-broken combination applies.
|
|
120
|
+
const onMacOS26 = getPlatformAdvisory(env, osPlatform, osRelease) != null;
|
|
121
|
+
const isSimTarget = !!input.simulatorId && !input.deviceId;
|
|
122
|
+
const isAttachMode = !input.launchBundleId;
|
|
123
|
+
return onMacOS26 && isSimTarget && isAttachMode;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Runs the 2-second probe. Reuses runCommand's timeout wrapper with the
|
|
127
|
+
* same SIGINT-first / SIGKILL-fallback shape that the full recording
|
|
128
|
+
* uses, so the probe's salvage behavior matches the real path.
|
|
129
|
+
*
|
|
130
|
+
* The output bundle is placed at `<output>.preflight` to keep it
|
|
131
|
+
* recognizable in cleanup tools and not collide with the user's actual
|
|
132
|
+
* output path.
|
|
133
|
+
*/
|
|
134
|
+
export async function preflightXctraceRecord(input, resolvedOutput) {
|
|
135
|
+
const probeOutput = `${resolvedOutput}.preflight`;
|
|
136
|
+
const probeInput = {
|
|
137
|
+
...input,
|
|
138
|
+
durationSec: PREFLIGHT_TIME_LIMIT_SEC,
|
|
139
|
+
output: probeOutput,
|
|
140
|
+
};
|
|
141
|
+
const args = buildXctraceArgs(probeInput);
|
|
142
|
+
const probeOutDir = dirname(probeOutput);
|
|
143
|
+
if (!existsSync(probeOutDir)) {
|
|
144
|
+
mkdirSync(probeOutDir, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
const start = Date.now();
|
|
147
|
+
const result = await runCommand("xcrun", args, {
|
|
148
|
+
timeoutMs: PREFLIGHT_WRAPPER_TIMEOUT_MS,
|
|
149
|
+
timeoutSignal: "SIGINT",
|
|
150
|
+
gracefulKillAfterMs: PREFLIGHT_GRACEFUL_KILL_MS,
|
|
151
|
+
});
|
|
152
|
+
const durationMs = Date.now() - start;
|
|
153
|
+
if (result.timedOut) {
|
|
154
|
+
return {
|
|
155
|
+
healthy: false,
|
|
156
|
+
reason: `Pre-flight probe wedged: 2s recording exceeded ${PREFLIGHT_WRAPPER_TIMEOUT_MS / 1000}s wrapper window without exiting. The same wedge will hit the full recording.`,
|
|
157
|
+
durationMs,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (result.code !== 0) {
|
|
161
|
+
return {
|
|
162
|
+
healthy: false,
|
|
163
|
+
reason: `Pre-flight probe exited non-zero (code ${result.code}): ${(result.stderr || result.stdout || "").slice(0, 200)}`,
|
|
164
|
+
durationMs,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return { healthy: true, durationMs };
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* v1.14 item J. When a `recordTimeProfile` call times out, optionally
|
|
171
|
+
* launch the partial `.trace` in Instruments.app so the user has a GUI
|
|
172
|
+
* escape hatch. Returns `true` when `open -a Instruments <tracePath>`
|
|
173
|
+
* was spawned, `false` otherwise.
|
|
174
|
+
*
|
|
175
|
+
* Gated on `MEMORYDETECTIVE_AUTO_OPEN_INSTRUMENTS=1` to avoid spamming
|
|
176
|
+
* the user's GUI on unattended runs and CI. Also requires the trace
|
|
177
|
+
* bundle to exist on disk (xctrace's SIGINT path may have failed to
|
|
178
|
+
* write anything).
|
|
179
|
+
*
|
|
180
|
+
* The `open` invocation is fire-and-forget: `detached: true` + `unref()`
|
|
181
|
+
* so the recording tool returns immediately. Failures to launch
|
|
182
|
+
* Instruments.app are swallowed; the user is no worse off than without
|
|
183
|
+
* the flag enabled.
|
|
184
|
+
*
|
|
185
|
+
* Exported so the env-gating logic can be tested without spawning
|
|
186
|
+
* Instruments in test runs.
|
|
187
|
+
*/
|
|
188
|
+
export function maybeOpenInInstruments(tracePath) {
|
|
189
|
+
if (process.env.MEMORYDETECTIVE_AUTO_OPEN_INSTRUMENTS !== "1")
|
|
190
|
+
return false;
|
|
191
|
+
if (!existsSync(tracePath))
|
|
192
|
+
return false;
|
|
193
|
+
try {
|
|
194
|
+
const child = spawn("open", ["-a", "Instruments", tracePath], {
|
|
195
|
+
detached: true,
|
|
196
|
+
stdio: "ignore",
|
|
197
|
+
});
|
|
198
|
+
child.unref();
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
94
205
|
/** Pure: build the xctrace argv for the given input. Exposed for testing. */
|
|
95
206
|
export function buildXctraceArgs(input) {
|
|
96
207
|
const args = ["xctrace", "record", "--template", input.template];
|
|
@@ -132,6 +243,28 @@ export async function recordTimeProfile(input) {
|
|
|
132
243
|
// parent.
|
|
133
244
|
mkdirSync(outDir, { recursive: true });
|
|
134
245
|
}
|
|
246
|
+
// v1.14 item H. Pre-flight the xctrace `--time-limit` ignore regression
|
|
247
|
+
// before spending the user's durationSec on a wedge. Gated to macOS 26.x
|
|
248
|
+
// simulator targets in attach mode by default; opt-in / opt-out via
|
|
249
|
+
// MEMORYDETECTIVE_PREFLIGHT_XCTRACE.
|
|
250
|
+
if (shouldPreflightXctrace({ ...input, output })) {
|
|
251
|
+
const probe = await preflightXctraceRecord({ ...input, output }, output);
|
|
252
|
+
if (!probe.healthy) {
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
command: `xcrun ${buildXctraceArgs({ ...input, output }).join(" ")}`,
|
|
256
|
+
output,
|
|
257
|
+
durationSec: input.durationSec,
|
|
258
|
+
template: input.template,
|
|
259
|
+
recordingTimedOut: true,
|
|
260
|
+
workaroundNotice: {
|
|
261
|
+
issue: "xctrace-time-limit-ignored",
|
|
262
|
+
message: `Pre-flight probe detected the xctrace wedge in ${probe.durationMs}ms. Skipping the full ${input.durationSec}s recording so you do not pay the wrapper timeout. ${probe.reason ?? ""}`,
|
|
263
|
+
fallbacks: XCTRACE_TIMEOUT_FALLBACKS,
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
135
268
|
const args = buildXctraceArgs({ ...input, output });
|
|
136
269
|
// External timeout wrapper. xctrace itself receives `--time-limit Ns`, so
|
|
137
270
|
// the normal exit path is xctrace finishing on its own at N seconds. The
|
|
@@ -147,6 +280,7 @@ export async function recordTimeProfile(input) {
|
|
|
147
280
|
gracefulKillAfterMs: XCTRACE_GRACEFUL_KILL_MS,
|
|
148
281
|
});
|
|
149
282
|
if (result.timedOut) {
|
|
283
|
+
const openedInInstrumentsApp = maybeOpenInInstruments(output);
|
|
150
284
|
return {
|
|
151
285
|
ok: false,
|
|
152
286
|
command: `xcrun ${args.join(" ")}`,
|
|
@@ -160,6 +294,7 @@ export async function recordTimeProfile(input) {
|
|
|
160
294
|
message: XCTRACE_TIMEOUT_MESSAGE,
|
|
161
295
|
fallbacks: XCTRACE_TIMEOUT_FALLBACKS,
|
|
162
296
|
},
|
|
297
|
+
openedInInstrumentsApp,
|
|
163
298
|
};
|
|
164
299
|
}
|
|
165
300
|
if (result.code !== 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordTimeProfile.js","sourceRoot":"","sources":["../../src/tools/recordTimeProfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EACL,OAAO,IAAI,WAAW,EACtB,OAAO,EACP,UAAU,IAAI,cAAc,EAC5B,IAAI,IAAI,QAAQ,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"recordTimeProfile.js","sourceRoot":"","sources":["../../src/tools/recordTimeProfile.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,9 +33,9 @@ declare const actionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
|
33
33
|
durationMs: z.ZodDefault<z.ZodNumber>;
|
|
34
34
|
}, "strip", z.ZodTypeAny, {
|
|
35
35
|
type: "swipe";
|
|
36
|
+
durationMs: number;
|
|
36
37
|
from: [number, number];
|
|
37
38
|
to: [number, number];
|
|
38
|
-
durationMs: number;
|
|
39
39
|
}, {
|
|
40
40
|
type: "swipe";
|
|
41
41
|
from: [number, number];
|
|
@@ -85,9 +85,9 @@ export declare const replayScenarioShape: {
|
|
|
85
85
|
durationMs: z.ZodDefault<z.ZodNumber>;
|
|
86
86
|
}, "strip", z.ZodTypeAny, {
|
|
87
87
|
type: "swipe";
|
|
88
|
+
durationMs: number;
|
|
88
89
|
from: [number, number];
|
|
89
90
|
to: [number, number];
|
|
90
|
-
durationMs: number;
|
|
91
91
|
}, {
|
|
92
92
|
type: "swipe";
|
|
93
93
|
from: [number, number];
|
|
@@ -140,9 +140,9 @@ export declare const replayScenarioSchema: z.ZodObject<{
|
|
|
140
140
|
durationMs: z.ZodDefault<z.ZodNumber>;
|
|
141
141
|
}, "strip", z.ZodTypeAny, {
|
|
142
142
|
type: "swipe";
|
|
143
|
+
durationMs: number;
|
|
143
144
|
from: [number, number];
|
|
144
145
|
to: [number, number];
|
|
145
|
-
durationMs: number;
|
|
146
146
|
}, {
|
|
147
147
|
type: "swipe";
|
|
148
148
|
from: [number, number];
|
|
@@ -180,9 +180,9 @@ export declare const replayScenarioSchema: z.ZodObject<{
|
|
|
180
180
|
coords?: [number, number] | undefined;
|
|
181
181
|
} | {
|
|
182
182
|
type: "swipe";
|
|
183
|
+
durationMs: number;
|
|
183
184
|
from: [number, number];
|
|
184
185
|
to: [number, number];
|
|
185
|
-
durationMs: number;
|
|
186
186
|
} | {
|
|
187
187
|
type: "wait";
|
|
188
188
|
seconds: number;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `summarizeTrace`: the trace-to-summary-card-in-one-call feature.
|
|
3
|
+
*
|
|
4
|
+
* Today an agent handed a `.trace` chains inspectTrace + up to 5
|
|
5
|
+
* analyzers + reasons over tens of KB of JSON. That's 6 round-trips
|
|
6
|
+
* and ~$0.10-0.20 in tokens, most of which get thrown away after the
|
|
7
|
+
* agent identifies the one user-visible finding.
|
|
8
|
+
*
|
|
9
|
+
* summarizeTrace does it all in one call:
|
|
10
|
+
*
|
|
11
|
+
* 1. Inspects the TOC via inspectTrace (reuses the v1.11 path).
|
|
12
|
+
* 2. For each populated known schema, runs the matching analyzer in
|
|
13
|
+
* parallel with smart defaults tuned for "what would a user care
|
|
14
|
+
* about?" (Apple's 100ms hitch threshold, top-10 hangs, top-15
|
|
15
|
+
* time-profile symbols, etc).
|
|
16
|
+
* 3. Cross-correlates findings (v1.13 Phase 2): hangs overlapping
|
|
17
|
+
* with hitches, allocation spikes preceding hangs, etc.
|
|
18
|
+
* 4. Produces a structured result PLUS a pre-rendered compact
|
|
19
|
+
* markdown card (< 10 KB target) suitable for direct presentation
|
|
20
|
+
* to the user without further reasoning.
|
|
21
|
+
*
|
|
22
|
+
* Strategic positioning: this is memorydetective's "synthesis over
|
|
23
|
+
* raw-query" play vs trace-MCPs that go deep on single-schema access.
|
|
24
|
+
* See `~/Desktop/internal/v1.9-notelet-retro-market.md` §4.5 for the
|
|
25
|
+
* full framing.
|
|
26
|
+
*/
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
import { type InspectTraceResult } from "./inspectTrace.js";
|
|
29
|
+
import { type AnalyzeHangsResult } from "./analyzeHangs.js";
|
|
30
|
+
import { type AnalyzeAnimationHitchesResult } from "./analyzeAnimationHitches.js";
|
|
31
|
+
import { type AnalyzeTimeProfileResult } from "./analyzeTimeProfile.js";
|
|
32
|
+
import { type AnalyzeAllocationsResult } from "./analyzeAllocations.js";
|
|
33
|
+
import { type AnalyzeAppLaunchResult } from "./analyzeAppLaunch.js";
|
|
34
|
+
export declare const summarizeTraceSchema: z.ZodObject<{
|
|
35
|
+
tracePath: z.ZodString;
|
|
36
|
+
focus: z.ZodDefault<z.ZodEnum<["hangs", "hitches", "allocations", "launch", "all"]>>;
|
|
37
|
+
verbose: z.ZodDefault<z.ZodBoolean>;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
tracePath: string;
|
|
40
|
+
focus: "allocations" | "all" | "hangs" | "hitches" | "launch";
|
|
41
|
+
verbose: boolean;
|
|
42
|
+
}, {
|
|
43
|
+
tracePath: string;
|
|
44
|
+
focus?: "allocations" | "all" | "hangs" | "hitches" | "launch" | undefined;
|
|
45
|
+
verbose?: boolean | undefined;
|
|
46
|
+
}>;
|
|
47
|
+
export type SummarizeTraceInput = z.infer<typeof summarizeTraceSchema>;
|
|
48
|
+
/**
|
|
49
|
+
* Per-analyzer entry on the structured result. `status` distinguishes
|
|
50
|
+
* "ran successfully", "schema absent in trace", and "ran but failed".
|
|
51
|
+
* Callers branching on the summary can decide whether to retry / refine.
|
|
52
|
+
*/
|
|
53
|
+
export interface SummarizeAreaSummary<TResult> {
|
|
54
|
+
status: "ok" | "schema-absent" | "failed";
|
|
55
|
+
/** Why the status is what it is (one sentence). Surfaces SIGSEGV / missing-schema / parser-error reasons. */
|
|
56
|
+
diagnosis: string;
|
|
57
|
+
/** Full analyzer result when status === "ok". Useful when a caller wants to drill in without re-running the analyzer. */
|
|
58
|
+
result?: TResult;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* v1.13 Phase 2: cross-area correlation. Each entry is a finding
|
|
62
|
+
* tying TWO areas together via timestamp overlap. The narrative
|
|
63
|
+
* field is the human-scannable string that goes into the markdown
|
|
64
|
+
* card; the structured fields (`kind`, `confidence`, evidence ids)
|
|
65
|
+
* are what callers can branch on programmatically.
|
|
66
|
+
*/
|
|
67
|
+
export interface Correlation {
|
|
68
|
+
/** Which two areas this correlation ties together. Currently only `hangs+hitches` is supported; `hangs+allocations` and `hitches+allocations` are deferred to v1.14+ because the analyzer doesn't currently expose per-timestamp allocation rows. */
|
|
69
|
+
kind: "hangs+hitches";
|
|
70
|
+
/** `high` when the overlap is substantial (both events > 100ms and the windows overlap significantly); `medium` when one event is short; `low` when timestamps are only adjacent. */
|
|
71
|
+
confidence: "high" | "medium" | "low";
|
|
72
|
+
/** Pre-formatted human-scannable narrative. Goes into the markdown card. */
|
|
73
|
+
narrative: string;
|
|
74
|
+
/** Start time in seconds (for the hang event). Used to rank correlations by user-relevance (earliest first). */
|
|
75
|
+
atSec: number;
|
|
76
|
+
}
|
|
77
|
+
export interface SummarizeTraceResult {
|
|
78
|
+
ok: boolean;
|
|
79
|
+
tracePath: string;
|
|
80
|
+
/** TOC + suggestedNextCalls from inspectTrace. Always present. */
|
|
81
|
+
inspection: InspectTraceResult;
|
|
82
|
+
/** Per-area summaries. Each section is independent; absence of one doesn't fail the call. */
|
|
83
|
+
areas: {
|
|
84
|
+
hangs: SummarizeAreaSummary<AnalyzeHangsResult>;
|
|
85
|
+
hitches: SummarizeAreaSummary<AnalyzeAnimationHitchesResult>;
|
|
86
|
+
timeProfile: SummarizeAreaSummary<AnalyzeTimeProfileResult>;
|
|
87
|
+
allocations: SummarizeAreaSummary<AnalyzeAllocationsResult>;
|
|
88
|
+
appLaunch: SummarizeAreaSummary<AnalyzeAppLaunchResult>;
|
|
89
|
+
};
|
|
90
|
+
/** Cross-area correlations (v1.13 Phase 2). Empty when areas don't have enough data to correlate. */
|
|
91
|
+
correlations: Correlation[];
|
|
92
|
+
/** Headline string: 1-2 sentences naming the biggest user-impact finding across all areas. */
|
|
93
|
+
headline: string;
|
|
94
|
+
/** Pre-rendered markdown summary card. Target < 10 KB at default `verbose: false`. */
|
|
95
|
+
markdown: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Pure: detect hangs whose window overlaps with animation hitches.
|
|
99
|
+
* When a user sees a hang AND a hitch in the same time window,
|
|
100
|
+
* they almost certainly perceived the impact (the main-thread block
|
|
101
|
+
* delayed render commits, dropping frames).
|
|
102
|
+
*
|
|
103
|
+
* The overlap check is symmetric: a hitch can fall within a hang's
|
|
104
|
+
* window OR a hang can fall within a hitch's window. Both directions
|
|
105
|
+
* are treated equally.
|
|
106
|
+
*
|
|
107
|
+
* Confidence:
|
|
108
|
+
*
|
|
109
|
+
* - `high`: both events >= 250ms AND the overlap span >= 100ms.
|
|
110
|
+
* - `medium`: at least one event >= 250ms.
|
|
111
|
+
* - `low`: neither event >= 250ms but the windows touch.
|
|
112
|
+
*
|
|
113
|
+
* Results are sorted by `atSec` ascending so the markdown card lists
|
|
114
|
+
* correlations in trace order.
|
|
115
|
+
*/
|
|
116
|
+
export declare function correlateHangsAndHitches(hangs: Array<{
|
|
117
|
+
startNs: number;
|
|
118
|
+
durationNs: number;
|
|
119
|
+
durationMs: number;
|
|
120
|
+
}>, hitches: Array<{
|
|
121
|
+
startNs: number;
|
|
122
|
+
durationNs: number;
|
|
123
|
+
durationMs: number;
|
|
124
|
+
hitchType?: string;
|
|
125
|
+
}>): Correlation[];
|
|
126
|
+
/**
|
|
127
|
+
* Pure: build all cross-area correlations from per-area summaries.
|
|
128
|
+
* Currently only `hangs+hitches` produces entries; allocation-based
|
|
129
|
+
* correlations need per-timestamp allocation data the existing
|
|
130
|
+
* analyzeAllocations doesn't expose (v1.14+ candidate).
|
|
131
|
+
*/
|
|
132
|
+
export declare function buildCorrelations(areas: SummarizeTraceResult["areas"]): Correlation[];
|
|
133
|
+
/**
|
|
134
|
+
* Pure: produce the one-or-two-sentence headline that goes at the top
|
|
135
|
+
* of the markdown card. Picks the most user-visible finding across
|
|
136
|
+
* all areas. Order of priority: longest hang above 250ms > worst
|
|
137
|
+
* launch phase > worst hitch > largest allocation spike.
|
|
138
|
+
*/
|
|
139
|
+
export declare function buildHeadline(areas: SummarizeTraceResult["areas"]): string;
|
|
140
|
+
/**
|
|
141
|
+
* Pure: assemble the compact markdown summary card. Designed to be
|
|
142
|
+
* <10 KB at default settings. The structured `areas` field carries
|
|
143
|
+
* the full data for callers who need it; this is the human view.
|
|
144
|
+
*/
|
|
145
|
+
export declare function buildMarkdownCard(result: Omit<SummarizeTraceResult, "markdown">, verbose: boolean): string;
|
|
146
|
+
export declare function summarizeTrace(input: SummarizeTraceInput): Promise<SummarizeTraceResult>;
|
|
147
|
+
export declare const SUMMARIZE_AREA_KEYS: readonly ["hangs", "hitches", "timeProfile", "allocations", "appLaunch"];
|