From 29927367d36701bfb26964efe67d266a21a46de4 Mon Sep 17 00:00:00 2001 From: David Reveman Date: Sat, 24 Oct 2026 10:25:27 -0700 Subject: [PATCH] Rebase the Perfetto integration onto `v59.1` Pin Perfetto to 1e9c61e9844affd7dccd3d42d9ea9f3a3d10cf6d and regenerate the eight integration patches. Adapt AgentProf routing to the redesigned upstream skill, enable the renamed flow plugin, and update TabStrip styling and browser selectors. Exercise flows through commands and rendered controls rather than the removed `trace.flows` property. Make the empty-trace SQL assertion independent of CSV versus agent-mode Markdown output. Validation: clean replay of all eight patches; `npm run check`; `npm test` with a locally built v59.1 Trace Processor; `npm run check:example`; UI build and typecheck; skill SQL format check; full browser smoke suite including four harnesses and 100K operations/1M indexed flows. --- .../src/core/embedder/agentprof_embedder.ts | 2 +- .../dev.agentprof.Agentprof/overview.scss | 4 +- ...agent-profiling-and-isolate-UI-types.patch | 6 +- ...d-agent-plugin-imports-from-the-chec.patch | 8 +-- ...sh-trace-details-from-agent-overview.patch | 4 +- ...prof-embedder-branding-and-focused-d.patch | 16 +++--- ...ks-for-responsive-top-bar-navigation.patch | 10 ++-- ...search-between-equal-navigation-regi.patch | 4 +- ...AgentProf-performance-investigations.patch | 55 +++++++++---------- ...w-embedders-to-provide-their-favicon.patch | 10 ++-- third_party/perfetto.toml | 2 +- tools/check-turns-ui.mjs | 8 +-- tools/check-ui.mjs | 53 +++++++++--------- tools/test_skills_sql.py | 10 ++-- 14 files changed, 98 insertions(+), 94 deletions(-) diff --git a/third_party/overlays/perfetto/ui/src/core/embedder/agentprof_embedder.ts b/third_party/overlays/perfetto/ui/src/core/embedder/agentprof_embedder.ts index b1e2000..d6ecb33 100644 --- a/third_party/overlays/perfetto/ui/src/core/embedder/agentprof_embedder.ts +++ b/third_party/overlays/perfetto/ui/src/core/embedder/agentprof_embedder.ts @@ -25,7 +25,7 @@ export class AgentprofEmbedder implements Embedder { 'dev.perfetto.CoreCommands', 'dev.perfetto.MultiTraceOpen', 'dev.perfetto.Timeline', - 'dev.perfetto.FlowEventsPanel', + 'dev.perfetto.FlowEvents', 'dev.perfetto.TraceInfoPage', 'dev.perfetto.SettingsPage', 'dev.agentprof.Agentprof', diff --git a/third_party/overlays/perfetto/ui/src/plugins/dev.agentprof.Agentprof/overview.scss b/third_party/overlays/perfetto/ui/src/plugins/dev.agentprof.Agentprof/overview.scss index 4e4ac4f..d435fcd 100644 --- a/third_party/overlays/perfetto/ui/src/plugins/dev.agentprof.Agentprof/overview.scss +++ b/third_party/overlays/perfetto/ui/src/plugins/dev.agentprof.Agentprof/overview.scss @@ -13,7 +13,7 @@ .ap-label {font-size: var(--pf-font-size-s); text-transform: uppercase; letter-spacing: .5px; color: var(--pf-color-text-muted);} .ap-tabs {height: auto;} // Keep the divider inside the scroll area so the active tab can cover it. - .ap-tabs > .pf-tabs__tabs { + .ap-tabs > .pf-tab-strip { padding: 0 28px 1px; overflow-x: auto; border-bottom: none; @@ -201,7 +201,7 @@ .ap-session-table > tbody > .ap-session-row {grid-template-columns: minmax(0, 1fr);} .ap-session-details > .ap-session-fact:first-child {grid-column: 1 / -1;} } - @media (max-width: 800px) {.ap-inner {width: 100%;} .ap-header, .ap-content {padding: 16px;} .ap-tabs > .pf-tabs__tabs {padding-inline: 10px;} .ap-headline-row {grid-template-columns: 1fr;}} + @media (max-width: 800px) {.ap-inner {width: 100%;} .ap-header, .ap-content {padding: 16px;} .ap-tabs > .pf-tab-strip {padding-inline: 10px;} .ap-headline-row {grid-template-columns: 1fr;}} } .ap-context { diff --git a/third_party/patches/perfetto/0001-ui-enable-agent-profiling-and-isolate-UI-types.patch b/third_party/patches/perfetto/0001-ui-enable-agent-profiling-and-isolate-UI-types.patch index c531636..d7f6f6c 100644 --- a/third_party/patches/perfetto/0001-ui-enable-agent-profiling-and-isolate-UI-types.patch +++ b/third_party/patches/perfetto/0001-ui-enable-agent-profiling-and-isolate-UI-types.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Agentprof Date: Fri, 25 Sep 2026 13:11:17 -0400 -Subject: [PATCH 1/6] ui: enable agent profiling and isolate UI types +Subject: [PATCH 1/8] ui: enable agent profiling and isolate UI types --- ui/src/core/embedder/default_plugins.ts | 1 + @@ -9,7 +9,7 @@ Subject: [PATCH 1/6] ui: enable agent profiling and isolate UI types 2 files changed, 2 insertions(+) diff --git a/ui/src/core/embedder/default_plugins.ts b/ui/src/core/embedder/default_plugins.ts -index 0008eac4..25faa1de 100644 +index 4370fd8137..394b6085ba 100644 --- a/ui/src/core/embedder/default_plugins.ts +++ b/ui/src/core/embedder/default_plugins.ts @@ -21,6 +21,7 @@ @@ -21,7 +21,7 @@ index 0008eac4..25faa1de 100644 'com.android.AndroidClientServer', 'com.android.AndroidCujs', diff --git a/ui/tsconfig.json b/ui/tsconfig.json -index 710e259b..5b29b1df 100644 +index 710e259b61..5b29b1df57 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -10,6 +10,7 @@ diff --git a/third_party/patches/perfetto/0002-ui-resolve-linked-agent-plugin-imports-from-the-chec.patch b/third_party/patches/perfetto/0002-ui-resolve-linked-agent-plugin-imports-from-the-chec.patch index aa12eb9..80473b4 100644 --- a/third_party/patches/perfetto/0002-ui-resolve-linked-agent-plugin-imports-from-the-chec.patch +++ b/third_party/patches/perfetto/0002-ui-resolve-linked-agent-plugin-imports-from-the-chec.patch @@ -1,17 +1,17 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Agentprof Date: Fri, 25 Sep 2026 16:00:10 -0400 -Subject: [PATCH 2/6] ui: resolve linked agent plugin imports from the checkout +Subject: [PATCH 2/8] ui: resolve linked agent plugin imports from the checkout --- ui/vite.config.mjs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ui/vite.config.mjs b/ui/vite.config.mjs -index 88698c98..f6f9258c 100644 +index a95587e807..8a2029822d 100644 --- a/ui/vite.config.mjs +++ b/ui/vite.config.mjs -@@ -255,6 +255,27 @@ const BUNDLE_CONFIGS = { +@@ -457,6 +457,27 @@ const BUNDLE_CONFIGS = { // frontend index.html drives the module graph. const BUNDLE = process.env.BUNDLE; @@ -39,7 +39,7 @@ index 88698c98..f6f9258c 100644 export default defineConfig(({command}) => { const isBuild = command === 'build'; if (isBuild && !BUNDLE) { -@@ -281,6 +302,7 @@ export default defineConfig(({command}) => { +@@ -483,6 +504,7 @@ export default defineConfig(({command}) => { // magic "publicDir" handling so it doesn't try to serve it at /. publicDir: false, plugins: [ diff --git a/third_party/patches/perfetto/0003-ui-distinguish-trace-details-from-agent-overview.patch b/third_party/patches/perfetto/0003-ui-distinguish-trace-details-from-agent-overview.patch index 4013ea9..7732400 100644 --- a/third_party/patches/perfetto/0003-ui-distinguish-trace-details-from-agent-overview.patch +++ b/third_party/patches/perfetto/0003-ui-distinguish-trace-details-from-agent-overview.patch @@ -1,14 +1,14 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Agentprof Date: Sat, 26 Sep 2026 03:19:57 -0400 -Subject: [PATCH 3/6] ui: distinguish trace details from agent overview +Subject: [PATCH 3/8] ui: distinguish trace details from agent overview --- ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts b/ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts -index df1b70ec..f57a3ddd 100644 +index df1b70ecaf..f57a3ddd8b 100644 --- a/ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts +++ b/ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts @@ -29,7 +29,7 @@ export default class implements PerfettoPlugin { diff --git a/third_party/patches/perfetto/0004-ui-support-Agentprof-embedder-branding-and-focused-d.patch b/third_party/patches/perfetto/0004-ui-support-Agentprof-embedder-branding-and-focused-d.patch index 92d5554..dd385ea 100644 --- a/third_party/patches/perfetto/0004-ui-support-Agentprof-embedder-branding-and-focused-d.patch +++ b/third_party/patches/perfetto/0004-ui-support-Agentprof-embedder-branding-and-focused-d.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Agentprof Date: Sat, 26 Sep 2026 03:49:58 -0400 -Subject: [PATCH 4/6] ui: support Agentprof embedder branding and focused +Subject: [PATCH 4/8] ui: support Agentprof embedder branding and focused defaults --- @@ -14,7 +14,7 @@ Subject: [PATCH 4/6] ui: support Agentprof embedder branding and focused 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/ui/src/core/embedder/create_embedder.ts b/ui/src/core/embedder/create_embedder.ts -index a30050be..25a88c04 100644 +index a30050be97..25a88c046f 100644 --- a/ui/src/core/embedder/create_embedder.ts +++ b/ui/src/core/embedder/create_embedder.ts @@ -13,22 +13,9 @@ @@ -44,7 +44,7 @@ index a30050be..25a88c04 100644 + return new AgentprofEmbedder(); } diff --git a/ui/src/core/embedder/embedder.ts b/ui/src/core/embedder/embedder.ts -index bdde74c3..f485a616 100644 +index bdde74c313..f485a61693 100644 --- a/ui/src/core/embedder/embedder.ts +++ b/ui/src/core/embedder/embedder.ts @@ -12,6 +12,9 @@ @@ -69,7 +69,7 @@ index bdde74c3..f485a616 100644 // should not be enabled for this embedder. readonly analyticsId: string | undefined; diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts -index e6fb1105..97505605 100644 +index e6fb11051d..97505605de 100644 --- a/ui/src/frontend/index.ts +++ b/ui/src/frontend/index.ts @@ -361,7 +361,8 @@ function onCssLoaded(app: AppImpl) { @@ -83,7 +83,7 @@ index e6fb1105..97505605 100644 router.onRouteChanged = routeChange; diff --git a/ui/src/frontend/ui_main.ts b/ui/src/frontend/ui_main.ts -index 7ad231f4..cdc4e2d4 100644 +index 7ad231f40d..cdc4e2d43d 100644 --- a/ui/src/frontend/ui_main.ts +++ b/ui/src/frontend/ui_main.ts @@ -42,12 +42,13 @@ export class UiMain implements m.ClassComponent { @@ -103,7 +103,7 @@ index 7ad231f4..cdc4e2d4 100644 } diff --git a/ui/src/frontend/views/sidebar.ts b/ui/src/frontend/views/sidebar.ts -index e6a7ba5f..dc91320e 100644 +index e6a7ba5fcd..dc91320ef0 100644 --- a/ui/src/frontend/views/sidebar.ts +++ b/ui/src/frontend/views/sidebar.ts @@ -82,7 +82,10 @@ export class Sidebar implements m.ClassComponent { @@ -119,10 +119,10 @@ index e6a7ba5f..dc91320e 100644 m( 'span.pf-sidebar__branding-badge', diff --git a/ui/vite.config.mjs b/ui/vite.config.mjs -index f6f9258c..28175108 100644 +index 8a2029822d..eac6751add 100644 --- a/ui/vite.config.mjs +++ b/ui/vite.config.mjs -@@ -258,12 +258,14 @@ const BUNDLE = process.env.BUNDLE; +@@ -460,12 +460,14 @@ const BUNDLE = process.env.BUNDLE; // Resolve imports in Agentprof's linked source files from their checkout paths. // Preserve pnpm's normal realpath behavior for third-party dependencies. function pluginAgentprofOverlays() { diff --git a/third_party/patches/perfetto/0005-Add-embedder-hooks-for-responsive-top-bar-navigation.patch b/third_party/patches/perfetto/0005-Add-embedder-hooks-for-responsive-top-bar-navigation.patch index c3fde11..aff887b 100644 --- a/third_party/patches/perfetto/0005-Add-embedder-hooks-for-responsive-top-bar-navigation.patch +++ b/third_party/patches/perfetto/0005-Add-embedder-hooks-for-responsive-top-bar-navigation.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Agentprof Date: Sun, 27 Sep 2026 15:30:42 -0400 -Subject: [PATCH 5/6] Add embedder hooks for responsive top-bar navigation +Subject: [PATCH 5/8] Add embedder hooks for responsive top-bar navigation --- ui/src/core/app_impl.ts | 4 ++-- @@ -11,7 +11,7 @@ Subject: [PATCH 5/6] Add embedder hooks for responsive top-bar navigation 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/ui/src/core/app_impl.ts b/ui/src/core/app_impl.ts -index 0bbb751e..eadc946e 100644 +index 715733dea6..8a8b088b75 100644 --- a/ui/src/core/app_impl.ts +++ b/ui/src/core/app_impl.ts @@ -154,11 +154,11 @@ export class AppImpl implements App { @@ -29,7 +29,7 @@ index 0bbb751e..eadc946e 100644 this.analytics = initAnalytics( this.testingMode, diff --git a/ui/src/core/embedder/embedder.ts b/ui/src/core/embedder/embedder.ts -index f485a616..8deef447 100644 +index f485a61693..8deef44734 100644 --- a/ui/src/core/embedder/embedder.ts +++ b/ui/src/core/embedder/embedder.ts @@ -54,6 +54,12 @@ export interface Embedder { @@ -46,7 +46,7 @@ index f485a616..8deef447 100644 // Returns the Google Analytics measurement ID, or undefined if analytics // should not be enabled for this embedder. diff --git a/ui/src/frontend/topbar.scss b/ui/src/frontend/topbar.scss -index 1cf62a32..b848b8f8 100644 +index 1cf62a3251..b848b8f83a 100644 --- a/ui/src/frontend/topbar.scss +++ b/ui/src/frontend/topbar.scss @@ -26,6 +26,42 @@ @@ -93,7 +93,7 @@ index 1cf62a32..b848b8f8 100644 .pf-omnibox--query-mode { padding-left: 48px; diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts -index 88e090c0..a324bd93 100644 +index 88e090c0f9..a324bd9393 100644 --- a/ui/src/frontend/topbar.ts +++ b/ui/src/frontend/topbar.ts @@ -104,16 +104,20 @@ export class Topbar implements m.ClassComponent { diff --git a/third_party/patches/perfetto/0006-Center-embedder-search-between-equal-navigation-regi.patch b/third_party/patches/perfetto/0006-Center-embedder-search-between-equal-navigation-regi.patch index 88d1d67..5d1be3e 100644 --- a/third_party/patches/perfetto/0006-Center-embedder-search-between-equal-navigation-regi.patch +++ b/third_party/patches/perfetto/0006-Center-embedder-search-between-equal-navigation-regi.patch @@ -1,14 +1,14 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Agentprof Date: Sun, 27 Sep 2026 15:49:11 -0400 -Subject: [PATCH 6/6] Center embedder search between equal navigation regions +Subject: [PATCH 6/8] Center embedder search between equal navigation regions --- ui/src/frontend/topbar.scss | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui/src/frontend/topbar.scss b/ui/src/frontend/topbar.scss -index b848b8f8..256973d7 100644 +index b848b8f83a..256973d771 100644 --- a/ui/src/frontend/topbar.scss +++ b/ui/src/frontend/topbar.scss @@ -28,8 +28,11 @@ diff --git a/third_party/patches/perfetto/0007-ai-Route-AgentProf-performance-investigations.patch b/third_party/patches/perfetto/0007-ai-Route-AgentProf-performance-investigations.patch index b0bcd58..77d6f8c 100644 --- a/third_party/patches/perfetto/0007-ai-Route-AgentProf-performance-investigations.patch +++ b/third_party/patches/perfetto/0007-ai-Route-AgentProf-performance-investigations.patch @@ -1,45 +1,44 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Agentprof Date: Wed, 14 Oct 2026 12:41:55 -0700 -Subject: [PATCH 7/7] ai: Route AgentProf performance investigations +Subject: [PATCH 7/8] ai: Route AgentProf performance investigations Add domain routes for agent recordings without changing the shared skill bootstrap. Tests: AgentProf workflow packaging and deterministic trace-query checks. --- - ai/skills/perfetto/SKILL-template.md | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) + ai/skills/perfetto/SKILL-template.md | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ai/skills/perfetto/SKILL-template.md b/ai/skills/perfetto/SKILL-template.md -index 02915e38..395650a0 100644 +index 2f19001b62..4f79f25594 100644 --- a/ai/skills/perfetto/SKILL-template.md +++ b/ai/skills/perfetto/SKILL-template.md -@@ -5,7 +5,9 @@ description: >- - analyzing them. Analysis covers loading a trace, running PerfettoSQL queries, - inspecting available tables/columns/stdlib, and guided investigations such as - Android Java heap dumps (leaks, retention, clustering many dumps, caching to -- cut memory). Routes to the right workflow and reference files. -+ cut memory), and AgentProf agent/harness/model performance (slow turns, -+ model latency, tools/delegation, context pressure, run comparisons and runtime -+ resources). Routes to the right workflow and reference files. +@@ -7,7 +7,9 @@ description: >- + Use for any trace file (.pftrace, .perfetto-trace, .pb, systrace, ftrace, + Chrome JSON) from Android, Linux, Chrome or CUDA/GPU workloads, whether or + not the user says "Perfetto". Bundles trace_processor (PerfettoSQL) plus +- guided workflows for Android memory and GPU analysis. ++ guided workflows for Android memory, GPU analysis, and AgentProf ++ agent/harness/model performance (slow turns, model latency, tools/delegation, ++ context pressure, run comparisons and runtime resources). --- # Perfetto -@@ -87,6 +89,17 @@ Workflows above are self-contained (they carry their own queries); read - > - When asked how to cluster multiple heap dumps or reduce noise across dumps, explain the clustering workflow (preprocessing paths, TF-IDF, K-Means clustering) and recommend running `scripts/cluster_paths.py` directly in your response. - > - When asked for next steps after running clustering or generating `clustered_output.csv`, respond directly recommending running `scripts/summarize_clusters.py clustered_output.csv report.html`. Do NOT attempt shell execution of Python scripts yourself. +@@ -53,7 +55,15 @@ internal, OEM or CI images), use that instead. + - Which GPUs are in the trace: [gpu_info.md]($SKILL_ROOT/workflows/gpu/gpu_info.md) + - Busy vs idle timeline, idle-gap attribution: [timeline_occupancy.md]($SKILL_ROOT/workflows/gpu/timeline_occupancy.md) -+## Agent profiling +-4. **Everything else** (startup, jank, ANRs, CPU, scheduling, counters, ++4. **Agent profiling** (AgentProf recordings from Pi, Claude Code, Codex or Muse) ++ - Slow run or unfamiliar recording: [Investigate a run]($SKILL_ROOT/workflows/agentprof/investigate_agent_run.md). ++ - Response latency and token throughput: [Model performance]($SKILL_ROOT/workflows/agentprof/analyze_model_performance.md). ++ - Slow tools, scripts or child agents: [Tools and delegation]($SKILL_ROOT/workflows/agentprof/analyze_tools_and_delegation.md). ++ - Context growth or compaction: [Context pressure]($SKILL_ROOT/workflows/agentprof/analyze_context_pressure.md). ++ - Before/after or harness/model comparisons: [Compare runs]($SKILL_ROOT/workflows/agentprof/compare_agent_runs.md). ++ - Recorded process CPU/RSS/heap: [Runtime resources]($SKILL_ROOT/workflows/agentprof/correlate_runtime_resources.md). + -+For AgentProf recordings from Pi, Claude Code, Codex or Muse: -+ -+* **Slow run or unfamiliar recording:** [Investigate a run]($SKILL_ROOT/workflows/agentprof/investigate_agent_run.md). -+* **Response latency and token throughput:** [Model performance]($SKILL_ROOT/workflows/agentprof/analyze_model_performance.md). -+* **Slow tools, scripts or child agents:** [Tools and delegation]($SKILL_ROOT/workflows/agentprof/analyze_tools_and_delegation.md). -+* **Context growth or compaction:** [Context pressure]($SKILL_ROOT/workflows/agentprof/analyze_context_pressure.md). -+* **Before/after or harness/model comparisons:** [Compare runs]($SKILL_ROOT/workflows/agentprof/compare_agent_runs.md). -+* **Recorded process CPU/RSS/heap:** [Runtime resources]($SKILL_ROOT/workflows/agentprof/correlate_runtime_resources.md). -+ - ## Finishing any analysis - - Whichever path you took — a guided workflow or ad-hoc queries — end by ++5. **Everything else** (startup, jank, ANRs, CPU, scheduling, counters, + any custom question): read + [querying.md]($SKILL_ROOT/infra-references/querying.md) and write + PerfettoSQL against a warm session. diff --git a/third_party/patches/perfetto/0008-ui-Allow-embedders-to-provide-their-favicon.patch b/third_party/patches/perfetto/0008-ui-Allow-embedders-to-provide-their-favicon.patch index 2975c7d..fa96934 100644 --- a/third_party/patches/perfetto/0008-ui-Allow-embedders-to-provide-their-favicon.patch +++ b/third_party/patches/perfetto/0008-ui-Allow-embedders-to-provide-their-favicon.patch @@ -1,5 +1,5 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: David Reveman +From: Agentprof Date: Fri, 16 Oct 2026 08:52:00 -0700 Subject: [PATCH 8/8] ui: Allow embedders to provide their favicon @@ -13,7 +13,7 @@ Tests: AgentProf build and browser favicon checks. 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ui/build.mjs b/ui/build.mjs -index 8b6ee9d6..cb6db1c8 100644 +index 667f2050b4..26f2c0ff80 100644 --- a/ui/build.mjs +++ b/ui/build.mjs @@ -128,7 +128,7 @@ const RULES = [ @@ -25,7 +25,7 @@ index 8b6ee9d6..cb6db1c8 100644 {r: /ui\/src\/assets\/(data_explorer\/base-page\.json)/, f: copyAssets}, {r: /ui\/src\/assets\/(data_explorer\/examples\/(.*)[.]json)/, f: copyAssets}, {r: /ui\/src\/assets\/(data_explorer\/node_info\/(.*)[.]md)/, f: copyAssets}, -@@ -1374,7 +1374,10 @@ function walk(dir, callback, skipRegex) { +@@ -1368,7 +1368,10 @@ function walk(dir, callback, skipRegex) { if (skipRegex !== undefined && skipRegex.test(child)) continue; if (stat.isDirectory()) { walk(childPath, callback, skipRegex); @@ -38,7 +38,7 @@ index 8b6ee9d6..cb6db1c8 100644 } } diff --git a/ui/src/core/embedder/embedder.ts b/ui/src/core/embedder/embedder.ts -index 8deef447..e8152aab 100644 +index 8deef44734..e8152aabb5 100644 --- a/ui/src/core/embedder/embedder.ts +++ b/ui/src/core/embedder/embedder.ts @@ -51,6 +51,8 @@ export interface BrandingBadge { @@ -51,7 +51,7 @@ index 8deef447..e8152aab 100644 readonly brandLogo?: {readonly src: string; readonly alt: string}; readonly homePage?: m.ComponentTypes<{app: App}>; diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts -index 97505605..21a749b8 100644 +index 97505605de..21a749b8cb 100644 --- a/ui/src/frontend/index.ts +++ b/ui/src/frontend/index.ts @@ -310,7 +310,9 @@ function main() { diff --git a/third_party/perfetto.toml b/third_party/perfetto.toml index 5f5214f..9aaaf79 100644 --- a/third_party/perfetto.toml +++ b/third_party/perfetto.toml @@ -1,4 +1,4 @@ # Upstream Perfetto source used by tools/perfetto. [perfetto] url = "https://github.com/google/perfetto.git" -pin = "fff30e83b5af315d311a3f18626d70b458631fc5" +pin = "1e9c61e9844affd7dccd3d42d9ea9f3a3d10cf6d" diff --git a/tools/check-turns-ui.mjs b/tools/check-turns-ui.mjs index 0aceb2e..e7adc4a 100644 --- a/tools/check-turns-ui.mjs +++ b/tools/check-turns-ui.mjs @@ -14,7 +14,7 @@ const {chromium} = require('@playwright/test'); const url = 'http://127.0.0.1:10000'; export async function checkTurns(page) { - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Turns', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Turns', {exact: true}).click(); await page.locator('.ap-turn').first().waitFor(); assert.equal(await page.locator('.ap-turn-details').count(), 0, 'Details are lazy'); const rows = await page.evaluate(async () => { @@ -96,7 +96,7 @@ export async function checkTurns(page) { await page.waitForURL(/#!\/viewer/); await page.waitForFunction(() => window.app.trace.selection.selection.kind === 'track_event'); await page.getByRole('navigation', {name: 'Main navigation'}).getByRole('link', {name: 'Overview', exact: true}).click(); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Turns', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Turns', {exact: true}).click(); await page.locator('.ap-turn').first().waitFor(); assert.equal(await page.locator('.ap-error').count(), 0, 'Setup survives remount after timeline navigation'); const expandAgain = page.locator('.ap-turn').first().getByRole('button', {name: /^Expand turn /}); @@ -126,7 +126,7 @@ async function checkSetupRetry(page) { return original(sql); }; }); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Turns', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Turns', {exact: true}).click(); await page.getByRole('button', {name: 'Retry turns', exact: true}).first().click(); await page.locator('.ap-turn').first().waitFor(); assert.equal(await page.locator('.ap-error').count(), 0, 'Retry recovers after a partially completed native setup'); @@ -225,7 +225,7 @@ async function main() { await page.getByRole('group', {name: 'Recording agent'}).getByRole('button', {name: agent, exact: true}).click(); await page.locator('.ap-home__example').click(); await page.locator('.ap-session-summary tbody tr').first().waitFor({timeout: 60000}); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Turns', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Turns', {exact: true}).click(); await page.locator('.ap-turn').first().waitFor({timeout: 60000}); await page.locator('.ap-turn').first().getByRole('button', {name: /^Expand turn /}).click(); await page.locator('.ap-turn-details').first().waitFor(); diff --git a/tools/check-ui.mjs b/tools/check-ui.mjs index 9f86c58..83b5b06 100644 --- a/tools/check-ui.mjs +++ b/tools/check-ui.mjs @@ -219,11 +219,11 @@ try { await page.locator('.ap-session-summary tbody tr').waitFor(); await page.screenshot({path: `artifacts/screenshots/agentprof-${route}-example.png`}); await page.getByRole('heading', {name: 'What filled the context?', exact: true}).waitFor(); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Context', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Context', {exact: true}).click(); await page.locator('.ap-context-chart polygon').first().waitFor(); assert.ok(await page.locator('.ap-context-category').count() > 0, `${agent} example includes context composition`); await page.screenshot({path: `artifacts/screenshots/agentprof-${route}-context.png`}); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Summary', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Summary', {exact: true}).click(); if (agent === 'Muse Code') { const manifest = JSON.parse(await readFile('examples/muse-coding/recording.json', 'utf8')); if (manifest.unavailableChildren > 0) await page.getByText(`${manifest.unavailableChildren} child sessions have no retained recording. Their model work and token usage are unavailable.`, {exact: true}).waitFor(); @@ -234,7 +234,7 @@ try { assert.equal(captures, manifest.recordings.length); } if (agent === 'Codex') { - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Tools', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Tools', {exact: true}).click(); await page.locator('.ap-script-row').first().waitFor(); const manifest = JSON.parse(await readFile('examples/codex-coding/recording.json', 'utf8')); assert.equal(await page.locator('.ap-script-row').count(), manifest.recording.scripts); @@ -278,7 +278,7 @@ try { ['Tools', 'Tool summary'], ['Sessions', 'Captured sessions'], ]) { - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText(tab, {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText(tab, {exact: true}).click(); await page.getByRole('heading', {name: heading, exact: true}).waitFor(); const overflow = await page.locator('.ap-page').evaluate(el => ({ page: el.scrollWidth - el.clientWidth, @@ -300,7 +300,7 @@ try { } } } - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Summary', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Summary', {exact: true}).click(); await page.setViewportSize({width: 1440, height: 1000}); await page.screenshot({path: 'artifacts/screenshots/agentprof-example-overview.png'}); await page.reload(); @@ -373,13 +373,16 @@ try { await trace.selection.selectSqlEvent('slice', links[0].source); return links[0]; }); - await page.waitForFunction(link => window.app.trace.flows.connectedFlows.some( - flow => flow.begin.sliceId === link.source && flow.end.sliceId === link.target), delegation); - await page.screenshot({path: 'artifacts/screenshots/agentprof-delegation-flow.png'}); - await page.evaluate(async link => { + await page.waitForFunction(async link => { const trace = window.app.trace; - await trace.selection.selectSqlEvent('slice', link.target, {scrollToSelection: true}); + const selection = trace.selection.selection; + if (selection.kind !== 'track_event') return false; + if (selection.eventId === link.target) return true; + if (selection.eventId !== link.source) throw new Error('Flow navigation selected an unexpected slice'); + await trace.commands.runCommand('dev.perfetto.MoveNextFlow'); + return false; }, delegation); + await page.screenshot({path: 'artifacts/screenshots/agentprof-delegation-flow.png'}); await page.waitForFunction(() => { const trace = window.app.trace; const selection = trace.selection.selection; @@ -403,15 +406,15 @@ try { return Number(result.iter({}).get('count')); }); assert.ok(expectedFlows > 0); - await page.waitForFunction(count => window.app.trace.flows.selectedFlows.length === count, expectedFlows); await page.getByText('Flow Events', {exact: true}).click(); const flowGrid = page.locator('.pf-grid').filter({has: page.getByText('Flow Category', {exact: true})}); + assert.equal(await flowGrid.locator('.pf-grid-cell').nth(1).textContent(), String(expectedFlows)); const showAll = flowGrid.locator('label.pf-checkbox').first(); await showAll.click(); - await page.waitForFunction(() => window.app.trace.flows.visibleCategories.get('_all_') === true); + assert.ok(await showAll.locator('input').isChecked()); await page.screenshot({path: 'artifacts/screenshots/agentprof-flow-events.png'}); await showAll.click(); - await page.waitForFunction(() => window.app.trace.flows.visibleCategories.get('_all_') === false); + assert.ok(!await showAll.locator('input').isChecked()); await page.evaluate(() => window.app.trace.selection.clearSelection()); await nav().getByRole('link', {name: 'Overview', exact: true}).click(); await overview().waitFor(); @@ -498,7 +501,7 @@ try { }; await assertRecordedRuns(recorded.recordings.filter(r => recorded.bundledFiles.includes(r.file))); await assertTokenCounters(16); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Sessions', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Sessions', {exact: true}).click(); await page.getByRole('heading', {name: 'Captured sessions', exact: true}).waitFor(); const capturedRows = page.locator('.ap-session-table:not(.ap-session-table--subagents) > tbody > .ap-session-row'); const capturedRecordings = recorded.recordings.filter(r => recorded.bundledFiles.includes(r.file)); @@ -554,7 +557,7 @@ try { assert.equal(await page.evaluate(() => window.sessionRowClicks), 0); await nav().getByRole('link', {name: 'Overview', exact: true}).click(); await page.getByRole('heading', {name: 'Captured sessions', exact: true}).waitFor(); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Summary', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Summary', {exact: true}).click(); await overview().waitFor(); await assertMainThreads(4); await page.screenshot({path: 'artifacts/screenshots/agentprof-overview.png'}); @@ -622,17 +625,17 @@ try { .filter({has: page.getByText('Incomplete operations', {exact: true})}); await incompleteFact.waitFor(); assert.equal(await incompleteFact.locator('.ap-session-fact__value').textContent(), '2'); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Summary', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Summary', {exact: true}).click(); await page.getByRole('button', {name: /Full recording/}).click(); await page.locator('.pf-menu-item').filter({hasText: 'Full recording'}).click(); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Turns', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Turns', {exact: true}).click(); await page.getByRole('heading', {name: 'Recorded turns', exact: true}).waitFor(); await page.getByRole('button', {name: /^Expand turn /}).first().click(); await page.locator('.ap-turn-event-link').first().click(); await page.getByText('pi-example 1001', {exact: true}).waitFor(); await page.locator('a[href="#!/agentprof"]').click(); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Tools', {exact: true}).waitFor(); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Tools', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Tools', {exact: true}).waitFor(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Tools', {exact: true}).click(); await page.getByRole('button', {name: 'bash', exact: true}).first().click(); await page.getByText('pi-example 1001', {exact: true}).waitFor(); assert.equal(await page.evaluate(() => window.app.trace.currentWorkspace.flatTracks @@ -688,7 +691,7 @@ try { assert.equal(await comparison.locator('tbody tr').filter({hasText: 'model-0'}).locator('td').nth(8).textContent(), 'Not recorded'); assert.equal(await comparison.locator('.ap-spark').count(), 0); assert.equal(await page.locator('.ap-error').count(), 0); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Sessions', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Sessions', {exact: true}).click(); await page.getByRole('heading', {name: 'Captured sessions', exact: true}).waitFor(); const comparisonRows = page.locator('.ap-session-table:not(.ap-session-table--subagents) > tbody > .ap-session-row'); await comparisonRows.first().waitFor(); @@ -698,7 +701,7 @@ try { await classicRow.getByRole('button', {name: /^Show details for session/}).click(); await classicRow.locator('xpath=following-sibling::tr[1]').locator('code').waitFor(); assert.equal(await classicRow.locator('xpath=following-sibling::tr[1]').locator('code').count(), 1); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Summary', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Summary', {exact: true}).click(); const actualStarts = await page.evaluate(async () => { const result = await window.app.trace.engine.query(` SELECT capture, CAST(MIN(ts) AS TEXT) AS start_ns @@ -867,7 +870,7 @@ try { await overview().waitFor({timeout: 60000}); await page.locator('.ap-session-summary .ap-session-label').first().waitFor(); assert.equal(await page.locator('.ap-session-summary tbody tr').count(), 2); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Tools', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Tools', {exact: true}).click(); await page.getByRole('heading', {name: 'Scripted tool use', exact: true}).waitFor(); await page.locator('.ap-script-row').first().waitFor(); const exampleManifest = JSON.parse(await readFile('examples/harness-comparison/recording.json', 'utf8')); @@ -906,7 +909,7 @@ try { await page.locator('input.trace_file').setInputFiles(resolve('artifacts/examples/codemode/scripts.pftrace')); await page.locator('.ap-banner').filter({hasText: 'scripts.pftrace'}).waitFor({timeout: 60000}); await overview().waitFor({timeout: 60000}); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Tools', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Tools', {exact: true}).click(); await page.locator('.ap-script-row').first().waitFor(); assert.equal(await page.locator('.ap-script-row').count(), 2); assert.equal(await page.locator('.ap-script-row .ap-tool-description').first().textContent(), 'Inspect tracing hooks'); @@ -928,7 +931,7 @@ try { assert.equal(await codexCard.locator('.ap-model-name').textContent(), 'fixture-model'); assert.match(await codexCard.innerText(), /260/); await page.screenshot({path: 'artifacts/screenshots/agentprof-codex.png'}); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Tools', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Tools', {exact: true}).click(); await page.locator('.ap-script-row').waitFor(); assert.equal(await page.locator('.ap-script-row .ap-tool-description').textContent(), 'JavaScript · 1 line'); await page.getByRole('button', {name: 'Expand script', exact: true}).click(); @@ -939,7 +942,7 @@ try { await page.waitForFunction(() => window.app.trace.selection.selection.kind === 'track_event'); await nav().getByRole('link', {name: 'Overview', exact: true}).click(); await page.waitForURL(/#!\/agentprof/); - await page.locator('.ap-tabs .pf-tabs__tab-title').getByText('Context', {exact: true}).click(); + await page.locator('.ap-tabs .pf-tab-strip__tab-title').getByText('Context', {exact: true}).click(); await page.getByText('Category breakdown not recorded. Showing total context usage.', {exact: true}).waitFor(); await more(); await page.getByRole('button', {name: 'Close recording', exact: true}).click(); diff --git a/tools/test_skills_sql.py b/tools/test_skills_sql.py index 961abf2..2e186fc 100644 --- a/tools/test_skills_sql.py +++ b/tools/test_skills_sql.py @@ -83,8 +83,9 @@ def check(self, script, condition, setup=SETUP + _FIXTURE): def test_all_resources_on_empty_trace_and_repeat_setup(self): out = self.query(SETUP + SETUP + TURNS + TURNS + - "SELECT COUNT(*) AS captures FROM agentprof_skill_capture_runs;") - self.assertTrue(out.rstrip().endswith("0"), out[-1000:]) + "SELECT CASE WHEN COUNT(*) = 0 THEN 'SKILLS_OK' ELSE 'FAILED' END AS result " + "FROM agentprof_skill_capture_runs;") + self.assertIn("SKILLS_OK", out, out[-1000:]) for resource in QUERIES: with self.subTest(resource=resource.name): self.query_resource(resource) @@ -217,11 +218,12 @@ def test_recordings_execute_each_resource_in_its_own_cli_call(self): def test_empty_results_do_not_hide_batched_output(self): case = SkillsSqlTest("test_all_resources_on_empty_trace_and_repeat_setup") - with patch.object(case, "query", return_value="0") as query: + with patch.object(case, "query", return_value="| result |\n|---|\n| SKILLS_OK |\n") as query: case.test_all_resources_on_empty_trace_and_repeat_setup() self.assertEqual(1 + len(QUERIES), query.call_count) self.assertEqual(SETUP + SETUP + TURNS + TURNS + - "SELECT COUNT(*) AS captures FROM agentprof_skill_capture_runs;", + "SELECT CASE WHEN COUNT(*) = 0 THEN 'SKILLS_OK' ELSE 'FAILED' END AS result " + "FROM agentprof_skill_capture_runs;", query.call_args_list[0].args[0]) self.assertEqual([SETUP + TURNS + resource.read_text() for resource in QUERIES], [call.args[0] for call in query.call_args_list[1:]])