Skip to content

Commit 6c8639b

Browse files
mios-devclaude
andcommitted
fix notepad-window via cmd.exe /c start + native-pattern research doc
Two things in one commit: 1. URGENT FIX: notepad-no-window (operator-flagged 2026-05-18 "FAIL!! NO NOTEPAD"). Broker dispatched via Start-Process inside PowerShell, source data confirmed the launch returned success + "launched via Start-Process" -- but no window appeared. Root cause: mios-launcher.service runs under user@992.service (systemd USER MANAGER session c2 "manager-early"), NOT the operator's interactive desktop session c11 (CLASS=user). When PowerShell.exe is exec'd via WSL interop from c2, Start-Process inherits the user-manager session context -- the spawned process has no window station/desktop, MainWindowHandle stays 0. My prior fix (replacing setsid+nohup with Start-Process) addressed only HALF the problem. The bulletproof Win32-from-WSL pattern is `cmd.exe /c start ""`: `start` uses ShellExecute, which goes through HKEY_CURRENT_USER associations + uses the operator's interactive shell's window station regardless of the launching session. cmd.exe runs via the WSL interop bridge and attaches to session 1 -- the new process surfaces on the operator's desktop. PowerShell stays in the picture for the post-launch placement step (MoveWindow/SetForegroundWindow against an EXISTING hwnd is pure Win32 API, not a process spawn, so session-context doesn't apply). The placement step now finds the new window by process basename (Get-Process -Name <basename> | Where MainWindowHandle -ne 0 | Sort StartTime -Descending | Select -First 1) instead of tracking the Start-Process PID return. Live verification: echo "CAPTURE: mios-windows launch notepad" | nc -U socket -> output "[mios-windows] launched via cmd /c start: C:\Windows\System32\notepad.exe". Get-Process notepad shows Id 23128 SessionId 1 MainWindowHandle 31262892 title "Untitled - Notepad" -- handle != 0, window VISIBLE on operator desktop. 2. RESEARCH DOC: /usr/share/mios/docs/native-os-agent-patterns.md (operator-flagged "what's native to the Whole technology stack GLOBALLY ... Research!!!"). Three-layer composition that's actually 2026 industry standard: Layer 1 -- OpenAI strict function-calling with JSONSchema enums * launch_app(name, args?, position?, monitor?) where position is an enum: center / left / right / top / bottom / top-left / ... / maximize / as-is * Schema enforces valid values -> no SOUL.md ban-lists, no regex post-checks, the model NATIVELY picks the right enum for "launch on the top right". * Counterpart typed verbs: focus_window, move_window, close_window, list_windows, screen_layout, open_url, open_map, show_image, take_screenshot, launch_app_in. Layer 2 -- MCP (Model Context Protocol) server * Open JSON-RPC standard Anthropic introduced Nov 2024, Anthropic + OpenAI + Google DeepMind all support, 500+ public servers, 97M monthly SDK downloads. * mios-mcp-server wraps the typed toolset above. Same MCP server works for OWUI (via mcpo bridge), Claude Desktop, Cursor, any future MCP-aware client -- no per-integration code. Layer 3 -- Anthropic Computer Use fallback * For UIs without a clean API: agent gets screenshot / mouse_click / keyboard_type / scroll, reasons visually, acts. Used ONLY when typed tool doesn't cover the intent. Migration plan in the doc: 4 phases (extend mios_verbs, ship MCP server, drop regex tower, add Computer Use fallback). Doc auto-registered into the "MiOS Documentation" knowledge collection at deploy time (now 34 files; managed_by= mios-knowledge-add so cache-clear preserves it). Attached to the mios-agent model row's meta.knowledge -- the agent itself can now RAG over the architecture spec on future turns. Sources cited in the doc: OpenAI Function Calling guide, OpenAI Structured Outputs, MCP spec + 2026 roadmap, Anthropic Computer Use docs + comparison vs OpenAI CUA. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8b6de56 commit 6c8639b

2 files changed

Lines changed: 340 additions & 44 deletions

File tree

usr/libexec/mios/mios-windows

Lines changed: 74 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -479,30 +479,39 @@ PYEOF
479479
exit 0
480480
fi
481481

482-
# Same-uid-as-broker / no-broker fallback: invoke
483-
# Start-Process via PowerShell DIRECTLY (no setsid+nohup).
484-
# Operator-flagged 2026-05-18: "NOTHING OPENED AT ALL" --
485-
# notepad.exe was exec'd via setsid+nohup, the process ran
486-
# (pid 499978) but MainWindowHandle=0 because
487-
# mios-launcher.service runs under user@992.service (the
488-
# USER MANAGER slice), not the operator's interactive
489-
# Windows session. Direct .exe exec from the manager slice
490-
# spawns a process with no desktop; Start-Process via
491-
# PowerShell always opens in the operator's foreground
492-
# session where a window can render.
482+
# Operator-flagged 2026-05-18 (round 2): Start-Process still
483+
# produced no window. Root cause: mios-launcher.service runs
484+
# under user@992.service (systemd user manager = session c2
485+
# `manager-early`), NOT the operator's interactive desktop
486+
# session c11 (`user`). When PowerShell.exe is exec'd via
487+
# WSL interop from c2, its Start-Process inherits the user-
488+
# manager session context -- new process has no window
489+
# station/desktop, MainWindowHandle stays 0.
490+
#
491+
# Bulletproof Win32-from-WSL pattern: cmd.exe /c start "" ...
492+
# `start` uses ShellExecute, which goes through HKEY_CURRENT_
493+
# USER associations + uses the interactive shell's window
494+
# station regardless of the launching session. cmd.exe runs
495+
# via the WSL interop bridge and attaches to session 1 (the
496+
# operator's interactive). Resulting process surfaces on the
497+
# operator's desktop.
498+
#
499+
# PowerShell is still used for the post-launch place/center
500+
# step -- that's pure Win32 API (MoveWindow / SetForeground
501+
# Window) on an existing hwnd, not a process spawn, so the
502+
# session-context issue doesn't apply.
493503
_target_win="$(wslpath -w "$target" 2>/dev/null)"
494504
[ -z "$_target_win" ] && _target_win="$target"
495-
if [ -x "$PWSH" ]; then
496-
python3 - "$_target_win" "$PWSH" "$@" <<'PYEOF'
505+
if [ -x "$CMD" ] || [ -x /mnt/c/Windows/System32/cmd.exe ]; then
506+
python3 - "$_target_win" "${PWSH:-}" "${CMD:-/mnt/c/Windows/System32/cmd.exe}" "$@" <<'PYEOF'
497507
import shlex, subprocess, sys
498508
target = sys.argv[1]
499509
pwsh = sys.argv[2]
500-
extra_args = sys.argv[3:]
501-
start = f"$p = Start-Process -FilePath {shlex.quote(target)} -PassThru"
502-
if extra_args:
503-
arglist = ", ".join(shlex.quote(a) for a in extra_args)
504-
start = (f"$p = Start-Process -FilePath {shlex.quote(target)} "
505-
f"-ArgumentList {arglist} -PassThru")
510+
cmd_exe = sys.argv[3] or "/mnt/c/Windows/System32/cmd.exe"
511+
extra_args = sys.argv[4:]
512+
# cmd.exe /c start "" "<target>" [args...] is the canonical
513+
# session-1 spawn for any Windows program from WSL.
514+
start_args = [cmd_exe, "/c", "start", "", target] + extra_args
506515
# Operator directive 2026-05-18: "MiOS-Agent(s) MUST also KNOW
507516
# window launch params/args to be able to launch and move
508517
# precisely!!!". Three env-driven knobs the caller can set BEFORE
@@ -559,35 +568,56 @@ elif position == "bottom":
559568
elif position in ("none", "_explicit"):
560569
pass # keep existing $x/$y (no reposition for "none";
561570
# explicit values already in place for "_explicit")
562-
center = (
563-
"; for ($i = 0; $i -lt 50; $i++) { "
564-
" $p = Get-Process -Id $p.Id -ErrorAction SilentlyContinue; "
565-
" if ($p -and $p.MainWindowHandle -ne 0) { break }; "
566-
" Start-Sleep -Milliseconds 100 "
567-
"}; "
568-
"if ($p -and $p.MainWindowHandle -ne 0) { "
569-
" Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue; "
570-
" Add-Type -Name W32D -Namespace MX -MemberDefinition @'\n"
571-
"[System.Runtime.InteropServices.DllImport(\"user32.dll\")] public static extern bool MoveWindow(System.IntPtr h,int x,int y,int w,int n,bool r);\n"
572-
"[System.Runtime.InteropServices.DllImport(\"user32.dll\")] public static extern bool SetForegroundWindow(System.IntPtr h);\n"
573-
"[System.Runtime.InteropServices.DllImport(\"user32.dll\")] public static extern bool GetWindowRect(System.IntPtr h, out RECT r);\n"
574-
"[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct RECT { public int L,T,R,B; }\n"
575-
"'@ -ErrorAction SilentlyContinue; "
576-
"$r = New-Object MX.W32D+RECT; "
577-
"[MX.W32D]::GetWindowRect($p.MainWindowHandle, [ref]$r) | Out-Null; "
578-
+ place_block +
579-
"[MX.W32D]::MoveWindow($p.MainWindowHandle, $x, $y, $w, $h, $true) | Out-Null; "
580-
"[MX.W32D]::SetForegroundWindow($p.MainWindowHandle) | Out-Null "
581-
"}"
582-
)
583-
ps = start + center
571+
572+
# 1. Spawn via cmd.exe /c start "" -- session-1 attach via WSL
573+
# interop + ShellExecute. The "" empty title slot is required
574+
# so `start` doesn't treat the next arg as a window title.
584575
subprocess.Popen(
585-
[pwsh, "-NoProfile", "-NonInteractive",
586-
"-ExecutionPolicy", "Bypass", "-Command", ps],
576+
start_args,
587577
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
588578
stdin=subprocess.DEVNULL, start_new_session=True,
589579
)
590-
print(f"[mios-windows] launched via Start-Process: {target}")
580+
print(f"[mios-windows] launched via cmd /c start: {target}")
581+
582+
# 2. Optional post-launch placement: PowerShell finds the new
583+
# window by process basename + applies MoveWindow/Foreground.
584+
# Skipped when MIOS_LAUNCH_POSITION=none (or no PowerShell).
585+
if pwsh and position != "none":
586+
import time
587+
proc_name = target.rsplit("\\", 1)[-1]
588+
if proc_name.lower().endswith(".exe"):
589+
proc_name = proc_name[:-4]
590+
place_ps = (
591+
f"$pn = '{proc_name}'; "
592+
"$p = $null; "
593+
"for ($i = 0; $i -lt 50; $i++) { "
594+
" $p = Get-Process -Name $pn -ErrorAction SilentlyContinue | "
595+
" Where-Object MainWindowHandle -ne 0 | "
596+
" Sort-Object StartTime -Descending | Select-Object -First 1; "
597+
" if ($p) { break }; "
598+
" Start-Sleep -Milliseconds 150 "
599+
"}; "
600+
"if ($p) { "
601+
" Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue; "
602+
" Add-Type -Name W32D -Namespace MX -MemberDefinition @'\n"
603+
"[System.Runtime.InteropServices.DllImport(\"user32.dll\")] public static extern bool MoveWindow(System.IntPtr h,int x,int y,int w,int n,bool r);\n"
604+
"[System.Runtime.InteropServices.DllImport(\"user32.dll\")] public static extern bool SetForegroundWindow(System.IntPtr h);\n"
605+
"[System.Runtime.InteropServices.DllImport(\"user32.dll\")] public static extern bool GetWindowRect(System.IntPtr h, out RECT r);\n"
606+
"[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct RECT { public int L,T,R,B; }\n"
607+
"'@ -ErrorAction SilentlyContinue; "
608+
"$r = New-Object MX.W32D+RECT; "
609+
"[MX.W32D]::GetWindowRect($p.MainWindowHandle, [ref]$r) | Out-Null; "
610+
+ place_block +
611+
"[MX.W32D]::MoveWindow($p.MainWindowHandle, $x, $y, $w, $h, $true) | Out-Null; "
612+
"[MX.W32D]::SetForegroundWindow($p.MainWindowHandle) | Out-Null "
613+
"}"
614+
)
615+
subprocess.Popen(
616+
[pwsh, "-NoProfile", "-NonInteractive",
617+
"-ExecutionPolicy", "Bypass", "-Command", place_ps],
618+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
619+
stdin=subprocess.DEVNULL, start_new_session=True,
620+
)
591621
PYEOF
592622
else
593623
# Last-resort fallback if PowerShell is unreachable.

0 commit comments

Comments
 (0)