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:]])