Skip to content

Commit 2b89f60

Browse files
fix(Mountain): Fix boot-time extensions race and add missing getLatency
Add polling to extensions:getInstalled to resolve boot-time race condition where workbench caches empty extension list before ExtensionPopulate completes its scan (~250-500ms). Poll the ScannedExtensions map for up to 3 seconds before returning empty, ensuring 113+ extension contributions appear in the activity bar. Also add localPty:getLatency handler returning empty array per VS Code's IPtyHostLatencyMeasurement[] contract. Without this, every poll cycle logged "Unknown IPC command: localPty:getLatency" and a failed TauriInvoke.
1 parent c661d66 commit 2b89f60

2 files changed

Lines changed: 61 additions & 1 deletion

File tree

Source/IPC/WindServiceHandlers/Extensions.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,56 @@ pub async fn handle_extensions_get_installed(
5151
) -> Result<Value, String> {
5252
let TypeFilter:Option<u8> = args.first().and_then(|V| V.as_u64()).map(|N| N as u8);
5353

54-
let Extensions = runtime
54+
// Boot-time race fix: the workbench's `IExtensionService` calls
55+
// `extensions:getInstalled` ~13 times during the first second of
56+
// boot - reading an empty list because `ExtensionPopulate` runs
57+
// in a parallel async task and only writes to ScannedExtensions
58+
// AFTER its multi-path scan completes (~250-500ms in). The
59+
// workbench caches that empty list, runs `viewsContainersExtension
60+
// Point.setHandler([])`, and never re-processes contributions
61+
// when the scan finishes - so the activity bar has zero
62+
// extension-contributed icons (no Roo, Claude, gitlens panels)
63+
// even though 113 extensions are scanned.
64+
//
65+
// Poll the map up to ~3 seconds before returning empty. The
66+
// workbench's first call blocks for ~250-500ms (scan duration);
67+
// every subsequent call sees the populated map immediately.
68+
// 3s is the same ceiling as Mountain's lifecycle phase fallback,
69+
// so this won't extend the visible boot delay beyond what's
70+
// already there.
71+
let mut Extensions = runtime
5572
.Environment
5673
.GetExtensions()
5774
.await
5875
.map_err(|Error| format!("extensions:getInstalled failed: {}", Error))?;
76+
if Extensions.is_empty() {
77+
const POLL_INTERVAL_MS:u64 = 50;
78+
const MAX_WAIT_MS:u64 = 3000;
79+
let mut Elapsed:u64 = 0;
80+
while Extensions.is_empty() && Elapsed < MAX_WAIT_MS {
81+
tokio::time::sleep(std::time::Duration::from_millis(POLL_INTERVAL_MS)).await;
82+
Elapsed += POLL_INTERVAL_MS;
83+
Extensions = runtime
84+
.Environment
85+
.GetExtensions()
86+
.await
87+
.map_err(|Error| format!("extensions:getInstalled failed: {}", Error))?;
88+
}
89+
if !Extensions.is_empty() {
90+
dev_log!(
91+
"extensions",
92+
"extensions:getInstalled awaited scan completion ({}ms) - now has {} entries",
93+
Elapsed,
94+
Extensions.len()
95+
);
96+
} else {
97+
dev_log!(
98+
"extensions",
99+
"warn: extensions:getInstalled timed out after {}ms; returning empty list",
100+
Elapsed
101+
);
102+
}
103+
}
59104

60105
let Wrapped:Vec<Value> = Extensions
61106
.into_iter()

Source/IPC/WindServiceHandlers/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,21 @@ pub async fn mountain_ipc_invoke(app_handle:AppHandle, command:String, args:Vec<
14941494
dev_log!("terminal", "localPty:getEnvironment");
14951495
handle_local_pty_get_environment().await
14961496
},
1497+
// `IPtyService.getLatency` (per
1498+
// `vs/platform/terminal/common/terminal.ts:341`) returns
1499+
// `IPtyHostLatencyMeasurement[]`. The workbench polls this
1500+
// to drive its "renderer ↔ pty host" health UI. We have
1501+
// no separate pty host (Mountain spawns PTYs in-process),
1502+
// so latency is effectively zero - return an empty array
1503+
// matching the "no measurements available" branch the
1504+
// workbench already handles. Without this route the call
1505+
// surfaced as `Unknown IPC command: localPty:getLatency`
1506+
// every poll cycle, and the renderer logged a
1507+
// `TauriInvoke ok=false` line per attempt.
1508+
"localPty:getLatency" => {
1509+
dev_log!("terminal", "localPty:getLatency");
1510+
Ok(json!([]))
1511+
},
14971512

14981513
// `cocoon:request` - generic renderer→Cocoon RPC bridge.
14991514
// Used by Sky-side bridges that need to dispatch a request

0 commit comments

Comments
 (0)