@@ -236,6 +236,106 @@ function GetServices(): CelServices | null {
236236 return ( window as any ) . __CEL_SERVICES__ ?? null ;
237237}
238238
239+ // ============================================================================
240+ // URI helpers for command arguments
241+ // ============================================================================
242+ //
243+ // Stock VS Code commands like `vscode.open` accept either a real `URI`
244+ // instance OR a `URIComponents` POJO with `$mid:1` (the workbench's
245+ // `URI.revive(...)` lifts those at the boundary). We prefer the real
246+ // class when available because:
247+ //
248+ // 1. In-process consumers (search dedup Map, command palette quick
249+ // pick) call `uri.with(...)` / `uri.toString()` directly without
250+ // going through `revive` - same root cause as the search-provider
251+ // `uri.with is not a function` bug.
252+ // 2. Round-tripping a real URI through serialisation is loss-free,
253+ // while a POJO has to be rebuilt by every consumer.
254+ //
255+ // `BuildOpenArg` accepts whatever the caller gives us (string,
256+ // pre-built URI instance, plain UriComponents, or a workspace-folder
257+ // shape with `.uri` nested) and produces a real URI when the bundled
258+ // class is available. Fallback POJO retains `$mid:1` so the few code
259+ // paths that DO call `revive` still work.
260+ function BuildOpenArg ( Source : unknown ) : unknown {
261+ const Ctor = GetServices ( ) ?. URI ;
262+ const ExtractParts = (
263+ Value : unknown ,
264+ ) : {
265+ Scheme : string ;
266+ Authority : string ;
267+ Path : string ;
268+ Query : string ;
269+ Fragment : string ;
270+ } | null => {
271+ if ( Value == null ) return null ;
272+ if ( typeof Value === "string" ) {
273+ const Trimmed = Value . trim ( ) ;
274+ if ( ! Trimmed ) return null ;
275+ if ( Trimmed . includes ( "://" ) ) {
276+ try {
277+ const Parsed = new URL ( Trimmed ) ;
278+ return {
279+ Scheme : Parsed . protocol . replace ( / : $ / , "" ) ,
280+ Authority : Parsed . host ,
281+ Path : decodeURIComponent ( Parsed . pathname ) ,
282+ Query : Parsed . search . replace ( / ^ \? / , "" ) ,
283+ Fragment : Parsed . hash . replace ( / ^ # / , "" ) ,
284+ } ;
285+ } catch {
286+ return null ;
287+ }
288+ }
289+ return {
290+ Scheme : "file" ,
291+ Authority : "" ,
292+ Path : Trimmed ,
293+ Query : "" ,
294+ Fragment : "" ,
295+ } ;
296+ }
297+ if ( typeof Value !== "object" ) return null ;
298+ const Holder = Value as Record < string , unknown > ;
299+ // Workspace-folder-style nested shape.
300+ if ( Holder [ "uri" ] && typeof Holder [ "uri" ] === "object" ) {
301+ return ExtractParts ( Holder [ "uri" ] ) ;
302+ }
303+ const Scheme = String ( Holder [ "scheme" ] ?? "file" ) ;
304+ const Path = String ( Holder [ "path" ] ?? Holder [ "fsPath" ] ?? "" ) ;
305+ if ( ! Path ) return null ;
306+ return {
307+ Scheme,
308+ Authority : String ( Holder [ "authority" ] ?? "" ) ,
309+ Path,
310+ Query : String ( Holder [ "query" ] ?? "" ) ,
311+ Fragment : String ( Holder [ "fragment" ] ?? "" ) ,
312+ } ;
313+ } ;
314+ const Parts = ExtractParts ( Source ) ;
315+ if ( ! Parts ) return Source ;
316+ if ( Ctor ) {
317+ try {
318+ return Ctor . from ( {
319+ scheme : Parts . Scheme ,
320+ authority : Parts . Authority ,
321+ path : Parts . Path ,
322+ query : Parts . Query ,
323+ fragment : Parts . Fragment ,
324+ } ) ;
325+ } catch {
326+ // Fall through to POJO.
327+ }
328+ }
329+ return {
330+ $mid : 1 ,
331+ scheme : Parts . Scheme ,
332+ authority : Parts . Authority ,
333+ path : Parts . Path ,
334+ query : Parts . Query ,
335+ fragment : Parts . Fragment ,
336+ } ;
337+ }
338+
239339// ============================================================================
240340// Output channel state (local mirror of Mountain's channel registry)
241341// ============================================================================
@@ -436,15 +536,7 @@ export async function InstallSkyBridge(): Promise<void> {
436536 const Wb = GetWorkbench ( ) ;
437537 if ( ! Wb ) return ;
438538 Wb . commands
439- . executeCommand (
440- "vscode.open" ,
441- {
442- $mid : 1 ,
443- path : uri ,
444- scheme : uri . startsWith ( "file://" ) ? "file" : "untitled" ,
445- } ,
446- viewColumn ,
447- )
539+ . executeCommand ( "vscode.open" , BuildOpenArg ( uri ) , viewColumn )
448540 . catch ( ( ) => {
449541 // Fallback: generic open
450542 Wb . env . openUri ( { path : uri } ) . catch ( ( ) => { } ) ;
@@ -506,18 +598,7 @@ export async function InstallSkyBridge(): Promise<void> {
506598 if ( Wb && UriValue ) {
507599 await Wb . commands . executeCommand (
508600 "vscode.open" ,
509- {
510- $mid : 1 ,
511- path : typeof UriValue === "string" ? UriValue : UriValue ?. path ,
512- scheme :
513- ( typeof UriValue === "string"
514- ? UriValue
515- : ( UriValue ?. scheme ?? "" )
516- ) . startsWith ?.( "file://" ) ||
517- UriValue ?. scheme === "file"
518- ? "file"
519- : "untitled" ,
520- } ,
601+ BuildOpenArg ( UriValue ) ,
521602 ViewColumn ,
522603 ) ;
523604 }
0 commit comments