@ycheung5/pi-computer-use 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/GLOSSARY.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -2
  4. package/assets/accessibility.cs +208 -0
  5. package/assets/browser-indicators.cs +37 -0
  6. package/assets/capture.cs +215 -0
  7. package/assets/clipboard.cs +197 -0
  8. package/assets/controls.cs +249 -0
  9. package/assets/indicator-preview.png +0 -0
  10. package/assets/indicators.cs +252 -0
  11. package/assets/input-timing.cs +42 -0
  12. package/assets/node-LICENSE.txt +2732 -0
  13. package/assets/ocr.cs +86 -0
  14. package/assets/ownership.cs +59 -0
  15. package/assets/pointer.cs +241 -0
  16. package/assets/puppeteer-LICENSE.txt +202 -0
  17. package/assets/readme-banner.svg +33 -0
  18. package/assets/stop.cs +197 -0
  19. package/assets/text.cs +279 -0
  20. package/assets/wgc.cs +167 -0
  21. package/assets/window-management.cs +217 -0
  22. package/assets/window-marker.cs +23 -0
  23. package/assets/window-references.cs +80 -0
  24. package/assets/windows.cs +127 -0
  25. package/dist/accessibility.d.ts +29 -0
  26. package/dist/accessibility.js +258 -0
  27. package/dist/browser-actions.d.ts +22 -0
  28. package/dist/browser-actions.js +246 -0
  29. package/dist/browser-indicators.d.ts +23 -0
  30. package/dist/browser-indicators.js +69 -0
  31. package/dist/browser-profiles.d.ts +10 -0
  32. package/dist/browser-profiles.js +174 -0
  33. package/dist/browser.d.ts +11 -0
  34. package/dist/browser.js +2071 -0
  35. package/dist/capture.d.ts +74 -0
  36. package/dist/capture.js +199 -0
  37. package/dist/client.d.ts +39 -0
  38. package/dist/client.js +384 -0
  39. package/dist/desktop.d.ts +20 -0
  40. package/dist/desktop.js +42 -0
  41. package/dist/extension.d.ts +4 -0
  42. package/dist/extension.js +409 -0
  43. package/dist/input-timing.d.ts +24 -0
  44. package/dist/input-timing.js +70 -0
  45. package/dist/launcher.d.ts +6 -0
  46. package/dist/launcher.js +85 -0
  47. package/dist/native.d.ts +11 -0
  48. package/dist/native.js +146 -0
  49. package/dist/ocr.d.ts +52 -0
  50. package/dist/ocr.js +246 -0
  51. package/dist/ownership.d.ts +4 -0
  52. package/dist/ownership.js +24 -0
  53. package/dist/pointer.d.ts +128 -0
  54. package/dist/pointer.js +198 -0
  55. package/dist/protocol.d.ts +15 -0
  56. package/dist/protocol.js +1 -0
  57. package/dist/runtime.d.ts +8 -0
  58. package/dist/runtime.js +72 -0
  59. package/dist/settings.d.ts +85 -0
  60. package/dist/settings.js +449 -0
  61. package/dist/stop.d.ts +24 -0
  62. package/dist/stop.js +156 -0
  63. package/dist/text.d.ts +120 -0
  64. package/dist/text.js +212 -0
  65. package/dist/waits.d.ts +19 -0
  66. package/dist/waits.js +262 -0
  67. package/dist/window-management.d.ts +25 -0
  68. package/dist/window-management.js +167 -0
  69. package/dist/worker.d.ts +1 -0
  70. package/dist/worker.js +553 -0
  71. package/docs/README.md +55 -0
  72. package/docs/adr/0001-preserve-reference-behavior.md +9 -0
  73. package/docs/adr/0002-window-lifetime-references.md +11 -0
  74. package/docs/adr/0003-exclusive-desktop-control.md +11 -0
  75. package/docs/adr/0004-pi-first-acceptance.md +9 -0
  76. package/docs/adr/0005-default-apps-and-workspace-reuse.md +7 -0
  77. package/docs/agents/domain.md +9 -0
  78. package/docs/agents/issue-tracker.md +10 -0
  79. package/docs/agents/triage-labels.md +9 -0
  80. package/docs/browser.md +113 -0
  81. package/docs/implementation-plan.md +153 -0
  82. package/docs/indicators.md +53 -0
  83. package/docs/native-interop-research.md +17 -0
  84. package/docs/ownership.md +25 -0
  85. package/docs/pi-extension-research.md +57 -0
  86. package/docs/pointer.md +77 -0
  87. package/docs/runtime.md +42 -0
  88. package/docs/settings.md +33 -0
  89. package/docs/spec.md +230 -0
  90. package/docs/stopping.md +83 -0
  91. package/docs/text.md +99 -0
  92. package/docs/tickets.md +85 -0
  93. package/docs/validation.md +1420 -0
  94. package/docs/windows.md +171 -0
  95. package/package.json +134 -4
@@ -0,0 +1,7 @@
1
+ ---
2
+ status: accepted
3
+ ---
4
+
5
+ # Follow Windows defaults and reuse the user's workspace
6
+
7
+ On 2026-11-02 the user chose Windows default apps and a new task tab/document in the intended existing window, with coherent foreground handling, because separate browser launches and repeated focus changes disrupted normal use. This supersedes the specification's dedicated-browser-profile default: existing profile acceptance remains valid for the explicit dedicated mode, while ordinary-app reuse may require native UI rather than the current browser protocol channel. Requirements live in the [spec](../spec.md#default-apps-workspaces-and-foreground); #30 owns implementation and acceptance, with editor-focus behavior shared with #26.
@@ -0,0 +1,9 @@
1
+ # Domain docs
2
+
3
+ This is a single-context project.
4
+
5
+ Read the root [GLOSSARY.md](../../GLOSSARY.md) for domain terminology and relevant decisions in [docs/adr/](../adr/) before exploring or changing the affected behavior. Use the glossary's vocabulary and surface conflicts with existing ADRs explicitly.
6
+
7
+ If a domain document is absent, proceed; create new terms or decisions through domain-modeling only when needed.
8
+
9
+ Use [docs/README.md](../README.md) to find task-specific documentation.
@@ -0,0 +1,10 @@
1
+ # Issue tracker: GitHub
2
+
3
+ Repository: ycheung5/pi-computer-use
4
+ URL: https://github.com/ycheung5/pi-computer-use
5
+
6
+ Issues and specs are published as GitHub issues. Use connected GitHub tools when available. With the GitHub CLI, explicitly pass `--repo ycheung5/pi-computer-use`. Use `--body-file` for multiline issue bodies.
7
+
8
+ Read issue bodies, labels and comments before changing them. Search existing issues before creating one to avoid duplicates. Use the [configured triage labels](triage-labels.md).
9
+
10
+ PRs as a request surface: no.
@@ -0,0 +1,9 @@
1
+ # Triage labels
2
+
3
+ | Role | GitHub label |
4
+ | --- | --- |
5
+ | Needs evaluation | needs-triage |
6
+ | Needs more information | needs-info |
7
+ | Ready for agent implementation | ready-for-agent |
8
+ | Requires human implementation | ready-for-human |
9
+ | Will not be actioned | wontfix |
@@ -0,0 +1,113 @@
1
+ # Browser read channel
2
+
3
+ Ordinary browsing follows the [default-app/workspace policy](spec.md#default-apps-workspaces-and-foreground): resolve the Windows default app and reuse its intended window through native UI with a separate task tab. Pi guidance and [handler/foreground support](windows.md#default-apps-and-workspaces) implement that route; scoped native Chrome reuse passed, while other browser/app and ordinary-agent workflow coverage remains. The dedicated protocol channel below is an explicit alternative, and its earlier acceptance does not prove ordinary-browser reuse.
4
+
5
+ Pi's normal tools route to the Windows browser worker when given `browserTab` or a browser element ID. `set_value` with `elementId: new-tab` opens a URL in a dedicated browser context; `<tab>:address` navigates it. `list_windows` annotates connected browser/tab IDs. Snapshot, accessibility_tree, find, element_info and screenshot work in this channel. Snapshot emits its PNG as an image block, with base64 omitted from text metadata.
6
+
7
+ Browser `element_info` includes disabled and read-only state for applicable controls, including native disabled state and disabled/inert DOM-ancestor and element-local ARIA states. Ancestor checks do not cross shadow roots, and inherited ARIA read-only state from a containing grid is not inferred. Read these observations before choosing an action; they do not grant permission to bypass a restricted control.
8
+
9
+ Installed Chrome and Edge are discovered from Windows default-browser registration, App Paths and conventional locations. The worker uses pinned puppeteer-core 25.16.0 with explicit installed executables, CDP, no managed browser downloads and no daily profiles. Temporary profiles and an isolated browser context are created by the worker. Background target creation is explicit. The Chromium channel supports reading/navigation and guarded element interactions, page text/key input, scrolling, explicit waits, JavaScript dialogs, popups, upload/download metadata and tab closure. Experimental installed Firefox uses a separate WebDriver BiDi channel described below. Dedicated kept profiles are opt-in as described below. Browser drag remains unsupported as in the reference.
10
+
11
+ Browser `type_text` and common editing keypresses refuse a focused element with explicit `aria-readonly="true"` or a focused element in a disabled/inert context. Directional keys are also refused for native value controls such as number, range, date and select, and equivalent explicit ARIA roles; text-field navigation, selection and dialog keys remain available. The typing and keyboard guards inspect the focused element through open shadow roots and same-origin iframes, accumulating disabled/inert state from the host or frame at each boundary. Closed shadow roots and cross-origin frames remain opaque: existing keypress behavior is retained, and `type_text` still refuses an opaque focused frame. The guard does not infer inherited ARIA grid state, custom widget behavior, or prevent arbitrary page scripts reacting to keys. HTML `readonly` blocks `set_value` only for input types and textareas where the attribute applies. An ignored attribute on a checkbox or radio does not make it read-only; explicit `aria-readonly` is still respected.
12
+
13
+ ## Identity and coordinates
14
+
15
+ Tab IDs are salted per launch; element IDs are scoped to one tab document. Navigation clears observed element handles; disconnection withdraws tabs. Re-observe after navigation/reconnect rather than reusing old IDs. Never mix native HWND/PID/title or desktop x/y with browser references. Browser image bounds use page pixels and carry their actual width, height and imageScale; they are not desktop screen coordinates.
16
+
17
+ Browser focus checks the active element in the target's document or shadow root. An open-shadow editor can therefore report successful focus when the page document itself reports its host as active. A headless Edge fixture exercises focus and text entry through Pi tools; this does not establish focus behavior for closed shadow roots or cross-origin frames.
18
+
19
+ ## Validation
20
+
21
+ Run `npm run build`, then `npm run test:browser -- edge` or `npm run test:browser -- chrome` from Windows Node or WSL2. Append `--headless` for a hidden test. The fixture starts its HTTP server on Windows and uses only disposable profiles. It sends no physical input and does not attach to daily browsers. It verifies Pi tool calls, tree/find/inspection, scaled screenshots, unchanged foreground, stale-ID rejection and disconnect/reconnect. Keep the desktop steady during the visible foreground check.
22
+
23
+ Corrected named-browser validation in this workspace: installed Edge headless read/lifecycle and full actions, installed Chrome headless full actions and visible read/lifecycle from WSL2, with returned executable, version and actual process headless flags asserted. Earlier fixture runs did not forward disposable settings through WSLENV and could select the personal browser setting; their requested browser labels alone are not reliable evidence. The fixtures only read settings and used temporary profiles, so daily browser profiles were not accessed. Windows process launches require sandbox escalation in this development environment; that is not an end-user extension dependency. Portable worker tests verify malformed/unknown requests without claiming native browser proof.
24
+
25
+ The consolidated #16 tool acceptance uses those named Chrome/Edge action fixtures, the paired advanced navigation/dialog/known-byte transfer and cancellation results, and the native-Windows Edge profile fixture. The latter uses registered Pi tools from a native Windows Node controller, not a real model or Pi CLI UI. A current Chrome headless read/guard/lifecycle recheck also passed the shared read-only, nested-focus, stale-ID and explicit-reconnect cases. Real-agent recovery remains #36; interactive sign-in, visible native-dialog/display behavior and physical stopping retain their separate release gates.
26
+
27
+ Run `npm run test:browser -- edge --headless --startup-failure` for the missing launch-failure case through registered Pi tools. The Windows fixture compiles an owned executable that writes its PID and exits 42 without children; it is selected instead of an installed browser. Pi returns a launch error with `BROWSER_PROFILE_RETAINED` and no browser session. The runner independently observes that executable and its worker exited before the fixture removes its retained profile, then verifies removal. An unconfirmed failure retains the owned fixture directory. This tests honest failure/retention and scoped cleanup, not a promise that arbitrary failed browser launches can be cleaned automatically. No production change was required.
28
+
29
+ Historical optional form comparison (not a release prerequisite): `npm run test:browser:comparison -- /path/to/dsh-computer-use-win edge 3` under the live-test lock. It reuses the local action fixture with independent submitted-payload verification, isolated headless profiles and explicit executable selection. See [paired form evidence and limitations](validation.md#paired-browser-form-comparison) for the red/green cases, metadata differences, tested version and pending scope. This complements the existing Pi-only runners; it does not establish real-model or visible-browser acceptance.
30
+
31
+ ## Configuration and limits
32
+
33
+ Use `/computer-settings` to select browserApp and browserVisible in personal settings. These preferences apply to the explicitly selected dedicated channel, which uses Chrome/Edge preference and installed-browser fallback. Ordinary app/window reuse uses Windows association resolution and does not consult these dedicated-browser preferences or silently substitute its supported browser. `PI_CU_BROWSER_EXECUTABLE` is an optional local executable-path override, not a model tool parameter. Fixtures use isolated settings files and never modify personal settings.
34
+
35
+ The Chromium outline comes from the accessibility tree. Traversal returns explicit truncation at budgets, but fetching an underlying AX tree or native browser protocol command can outlast the traversal budget; worker deadlines remain the outer safeguard. Page text is untrusted. An outline/page change proves neither user intent nor objective success. Browsers may activate for native dialogs; interaction behavior is validated in its own slice.
36
+
37
+ The optional real-Pi `partial` scenario exercises [recovery from a genuine truncated outline](validation.md#scoped-partial-observation-recovery) using the existing local form. It leaves production budgets unchanged and lets the agent choose its reads; exact server payloads and observed-control chronology determine acceptance. See [runner prerequisites and commands](validation.md#reproduce-automated-checks).
38
+
39
+ The optional `cancel` case verifies [actual Pi cancellation of a held navigation](validation.md#scoped-held-navigation-cancellation), including the original worker reply and released acknowledgment. It intentionally leaves the requested confirmation incomplete.
40
+
41
+ Own browser processes close gracefully on worker disposal or idle retirement. Temporary profile directories are removed only after their browser process is confirmed dead; kept profiles survive. Unknown or live orphan ownership is retained and reported rather than stolen.
42
+
43
+ Source: [Puppeteer installation](https://pptr.dev/guides/installation), [background page options](https://pptr.dev/api/puppeteer.createpageoptions), [accessibility snapshots](https://pptr.dev/api/puppeteer.accessibility.snapshot). Puppeteer is Apache-2.0 licensed; the package includes its license notice.
44
+
45
+ ## Chromium interactions
46
+
47
+ Use observed browser element IDs for click, double-click, hover, focus, invoke and field values; browserTab selects the page for typing, keypress, scrolling and waits. Inputs stay inside the browser protocol. Target/disabled/readonly/occlusion checks run in a separate browser world so page code cannot simply replace the guard's DOM methods. The Windows worker serializes calls, retains operation ownership and honors stop/cancellation before subsequent inputs.
48
+
49
+ Ordinary browser actions return a bounded fresh outline/page-change report with `verification: sent`; this is evidence to inspect, not proof of the user's objective. Explicit waits return observed, timeout or inconclusive when budgets prevent proving absence. A post-action observation failure is recorded separately without replaying the action. Pending navigation is reported as still loading rather than navigated successfully.
50
+
51
+ An open JavaScript dialog blocks other page reads/actions. `keypress` Enter accepts it and Escape dismisses it; `type_text` supplies a prompt answer before Enter. Browser-window shortcuts such as Ctrl+L/T are refused; use address/new-tab tools. Background operation is the default; explicit activate_window is allowed to activate. Native JavaScript dialogs may affect foreground in visible browsers, matching the reference limitation.
52
+
53
+ File inputs take existing absolute Windows file paths, one per line or separated by `|`. An intercepted chooser reports an observed input ID instead of opening the OS file dialog. Chromium downloads report their state/name/file metadata under the package's Windows downloads directory; action-triggered popups return new tab IDs. None of these operations uses the user's daily browser profile.
54
+
55
+ Run `npm run test:browser:actions -- edge` or `-- chrome` after building. The default action fixture is headless; append `--visible` for the visible-browser check. It exercises text/chords, occlusion (including a page override of elementFromPoint), disabled/readonly controls, select/checkbox, dialogs, scroll/waits, transfers, popup/close and operation cancellation recovery through the Pi tool/worker interfaces. Live suites are serialized externally with the shared lab lock when multiple development agents use the same desktop.
56
+
57
+ Action validation uses the executable/version/headless assertions above; see corrected evidence and Firefox proof below. Physical keyboard/mouse input was not used.
58
+
59
+
60
+ ## Paired reference baseline
61
+
62
+ Historical optional utility: the paired runner's `--advanced` mode for navigation, tab identity, JavaScript dialogs, exact file transfers, owned-browser disconnect/reconnect and passive wait cancellation. Use its separate `--profiles` mode for synthetic-cookie persistence, live ownership and temporary isolation/cleanup. Commands, measured outcomes and remaining boundaries are in [advanced-browser validation](validation.md#paired-advanced-browser-comparison). These compare public tool/backends, without invoking a model or the DSH application host. A successful dispatched action is followed by a new observation or the fixture application's own receipt; uncertain actions are not retried.
63
+
64
+ Headless JavaScript dialog handling and an intercepted file chooser do not establish visible OS-dialog or foreground behavior. Dedicated synthetic-cookie persistence does not establish real interactive sign-in. Desktop state changes during passive tests are retained with unproven attribution; they do not automatically become tool failures or preservation claims. Reference Chromium kept-profile sharing and Pi's ownership refusals remain distinct observed results; both experimental Firefox backends refused a live contender.
65
+
66
+ ## Experimental installed Firefox
67
+
68
+ Select `browserApp: firefox`; automatic Firefox-family launches are always headless, and settings normalize browserVisible to false. An OS Firefox default alone does not enable this experimental channel. Discovery checks the Firefox default ProgID command first, then registered App Paths and conventional Firefox locations. Both application.ini and browser/application.ini are accepted only with Firefox's App.ID in the App section; names alone do not establish engine compatibility. `PI_CU_BROWSER_EXECUTABLE` can select an unmodified Firefox-family executable explicitly. No managed or patched Firefox is downloaded by the extension.
69
+
70
+ The Firefox outline is explicitly `browser-dom-heuristic`, not a native accessibility tree. Roles and names are approximations from DOM controls, labels and ARIA. Elements remain document-scoped, with isolated-realm guards, ownership, stop/cancellation, dialogs, waits and post-action outlines shared with Chromium. The adapter's narrow isolated-realm seam uses a pinned Puppeteer internal interface because the public BiDi API does not expose accessibility or an isolated guard world; upgrades require live guard validation.
71
+
72
+ Reference limitations remain explicit: iframe contents, closed shadow roots, canvas semantics, file upload/download, browser drag and visible Firefox focus/sign-in flows are unsupported. Firefox screenshots capture the viewport and explicitly report that maxWidth is ignored. Forks and ESR versions require their own validation. Automatic crash replay is never attempted; a caller can explicitly create a new tab after a disconnected session has been withdrawn.
73
+
74
+ `PI_CU_BROWSER_BIDI_URL` explicitly connects to a loopback `ws:` endpoint with no credentials or fragment (root path becomes /session). It discovers existing tabs and uses the endpoint's existing default context, including its cookies. Disposal disconnects without closing the external browser or its existing/new tabs. Do not configure daily-profile endpoints. Run `npm run test:browser:firefox:endpoint` from Windows Node with PI_CU_BROWSER_EXECUTABLE to validate external process preservation in an owned fixture. An optional reference-checkout argument compares the same owned endpoint through reference MCP. The fixture checks pre-existing/new tabs, synthetic-cookie sharing, no owned profile creation and process preservation after disconnect; real sign-in and daily profiles are excluded.
75
+
76
+ Proof in this workspace used an official unmodified Mozilla Firefox 144.0 Windows binary extracted into a disposable fixture directory, without running its installer or changing browser registration. WSL2 Pi tool tests passed DOM outline/find/inspection, viewport screenshots, stale IDs, explicit reconnect, CJK/emoji/multiline values, password-value redaction, guarded actions, text/chords, scroll/waits, all three JavaScript dialogs, popup/close, transfer refusal and operation cancellation/recovery. The full action suite also passed from a native Windows Pi host. A native Windows endpoint fixture rejected a non-loopback URL and preserved its externally owned process on valid disconnect. No Firefox forks/ESR or visible/sign-in workflows are claimed validated. No physical desktop input was used.
77
+
78
+ On 2026-10-31, the existing Firefox read/lifecycle and full action suites passed again from WSL2 against production sources at `d7a9da5`, with the same official 144.0 executable. The first read run stopped because the shared fixture expected Chromium-style date roles; its Firefox date control is a `textbox` in the documented DOM heuristic. The fixture now accepts that role only for this Firefox date case, retaining the editing-refusal and unchanged-value assertions; no production behavior changed. This current result and the prior native-host/paired evidence support #17's supported-workflow, profile-isolation, stale-ID, reconnect and cancellation criteria within the experimental scope. Independently, no fixture Firefox process remained after disposal, but the temporary action profile and its lock were retained; this is a cleanup finding under #18, not a successful temporary-profile removal. Its cause remains unproved. The later shared cooperative-shutdown repair has named Edge controls; a [focused post-repair headless Firefox disposal recheck](validation.md#headless-firefox-temporary-profile-disposal) now also passed exact owned-process/profile cleanup. This does not establish the cause of the historical residue. This finding does not invalidate the separate [paired Firefox kept-cookie restart and temporary-removal passes](validation.md#paired-advanced-browser-comparison); another kept-cookie restart test would duplicate accepted coverage. Real-model recovery remains #36 and interactive sign-in remains #18.
79
+
80
+ For installed Firefox run `npm run test:browser -- firefox --headless` and `npm run test:browser:actions -- firefox`. An optional explicit executable is forwarded by WSL fixtures; each fixture asserts the actual launched executable/version/headless flags. Serialize live suites with the shared lab lock when multiple agents use the desktop.
81
+
82
+ ## Browser presentation
83
+
84
+ See [browser cues](indicators.md#browser-cues) for owned Chromium frame/tab/click markers, image suppression and manual visual acceptance. Experimental Firefox and external endpoints do not receive these cues.
85
+
86
+ ## Dedicated profiles and idle retirement
87
+
88
+ `browserSignIn: false` remains the default: each launch uses a new temporary profile and an isolated browser context. With `browserSignIn: true`, the worker uses the dedicated profile's default context so cookies can persist, rather than an incognito context that would discard them. Kept storage is partitioned by browser and normalized executable path, so Edge, Chrome and Firefox installations do not share sign-ins. This never opens a daily profile. Normal graceful shutdown and idle retirement close the browser to flush cookie storage; abrupt termination cannot promise a final flush.
89
+
90
+ Storage defaults to `%LOCALAPPDATA%\pi-computer-use\browser`. `list_windows` reports the active automation profile's path and kept status. `PI_CU_BROWSER_PROFILE_ROOT` optionally chooses another storage root; the extension still creates its own kept/temporary subdirectories, never uses the root as a daily profile. To forget a dedicated sign-in, close its Pi workers and automation browser processes, then remove that reported kept directory and its adjacent `.lock` directory. Closing tabs or turning browserSignIn off does not erase stored sign-ins. Never remove a lock while an owner may still be live.
91
+
92
+ Each launch claims an atomic profile lease containing the worker PID, launch phase, browser PID and random ownership token. A second controller refuses a live owner. A dead worker with a live browser remains blocked. Recorded temporary profiles abandoned by confirmed dead workers/browsers are swept during the next profile acquisition. Kept-profile lock recovery requires both recorded processes to be dead; PID reuse conservatively blocks recovery. Missing, invalid or incomplete launch ownership is also blocked for manual inspection, since an unknown child must not be treated as dead. Cleanup never kills a PID obtained from an old lease. An unconfirmed launch/cleanup reports retained storage instead of deleting potentially live data.
93
+
94
+ Worker disposal now distinguishes acknowledged resource cleanup from mere process exit; see the [shutdown protocol](runtime.md#interfaces-for-implementation). A controlled short exit grace reproduced worker exit code 1 with retained temporary storage, while normal/ample controls exited 0 and removed storage. The cooperative protocol then allowed the same no-input lifecycle to finish and remove storage with a 20 ms exit grace, without padding ordinary calls. This establishes the premature-retirement mechanism, not the cause of every earlier residual profile. The [controlled live-browser recovery check](validation.md#controlled-dead-writerlive-browser-recovery), together with existing crash/flush evidence, covers the ownership/recovery criterion. The [manual Bitwarden check](validation.md#manual-bitwarden-sign-in-and-restart) now records interactive login, same-profile restart and removal; the later [GitHub check](validation.md#manual-github-remembered-sign-in) completes real-site remembered authentication and removal.
95
+
96
+ Browsers retire after ten minutes without a completed browser call. Active actions and waits protect the browser until their full promises settle; listing/health calls do not indefinitely keep it open. `PI_CU_BROWSER_IDLE_MS` can change the timeout from 1,000 to 86,400,000 milliseconds (default 600,000), useful for shorter-lived shared-desktop sessions or longer interactive workflows. Retirement withdraws old IDs; an explicit new-tab request creates a new identity and reloads a kept profile. External loopback endpoints get no owned profile: their existing/default context and discovered tabs remain externally owned; retirement/disposal disconnects and preserves the process and all existing/new tabs.
97
+
98
+ Run `npm run test:browser:profiles -- edge`, `-- chrome`, or `-- firefox` after building; append `--temporary-crash` to check cleanup of recorded dead temporary profiles; use an installed Firefox executable override where needed. The Windows-local fixture uses an HTTP-only persistent synthetic cookie, disposable settings and its own storage root. It distinguishes cookie persistence from real sign-in support, checks temporary isolation/removal, live contention, idle retirement and active-wait protection, and observes whether a deliberately crashed worker leaves a live browser or Windows closes its child process. Surviving live orphans must be refused; confirmed dead owners can recover.
99
+
100
+ The focused `npm run test:browser:profiles -- edge --reconnect` case closes only a freshly verified disposable kept-profile browser, then requests a new tab through the same live worker. Preview it with `--plan` without dependencies, backend setup or file writes. It avoids `list_windows` and native metadata binding: the fixture verifies its own profile lease plus exact OS process/creation/executable/profile association before one `Browser.close`. The existing full profile suites use native discovery and need an isolated Windows session under the [lifetime test boundaries](windows.md#window-lifetime-references).
101
+
102
+ One focused Edge run passed on the unchanged browser implementation: worker PID stayed the same, the old browser exited before a different browser PID/creation opened the same kept path, the synthetic cookie persisted and the old tab was explicitly stale. Both browser roots and the worker were independently absent after shutdown; the lease was removed, kept storage was present before fixture retirement, and the fixture then removed its own directory. Worker continuity uses repeated health PID on the retained host; OS worker creation time was not measured. This is reconnect coverage; controlled live-browser recovery is recorded separately below, and the separate manual Bitwarden check covers interactive login with expected site logout, rather than remembered authentication. See [scoped evidence](validation.md#same-worker-kept-profile-reconnect). The fixture retains independently observed process identities and refuses storage deletion if one remains live/unknown, even after a lease disappears; `--cleanup-control` exercises that refusal with a harmless subprocess and canary, without a browser.
103
+
104
+ The focused `npm run test:browser:profiles -- edge --live-orphan` uses a real production lease writer and a fixture-owned headless browser. After the writer exits, a normal contender must refuse the still-live browser without changing its lease. Only after the fixture closes its retained browser handle may a new worker recover the same kept path/cookie with a new ownership token. Preview with `--plan`. The [accepted component evidence](validation.md#controlled-dead-writerlive-browser-recovery) complements existing crash/flush and cleanup checks; it does not claim a naturally crashed `worker.js` left a surviving browser or establish interactive sign-in.
105
+
106
+ The focused `npm run test:browser:profiles -- firefox --dispose-only` reuses the existing local fixture and real WorkerClient for one headless open/read/dispose. Preview with `--plan` without backend setup or file writes; supply the supported installed-executable override where needed. It verifies the actual owned worker/browser creation identities, exact temporary-profile command argument, profile-process matcher positive control, retirement and profile/lock removal before fixture cleanup. Query, identity or cleanup uncertainty retains the owned pipes/storage instead of forcing retirement. It does not use global native discovery, inspect cookies or repeat kept-profile persistence tests.
107
+
108
+ Manual release check (completed for GitHub in the [recorded check](validation.md#manual-github-remembered-sign-in); reuse this evidence): on a dedicated automation profile with browserSignIn enabled, complete a real site's sign-in yourself, shut down Pi normally, restart, and verify the intended account remains signed in. Choose a site whose policy permits persistent sign-ins: [Bitwarden's web vault](https://bitwarden.com/help/vault-timeout/) deliberately logs out when its tab/browser closes, so that site cannot establish remembered authentication across restart. Repeat sign-out/removal verification as appropriate. Site authentication, MFA, browser security features and visible login UX cannot be established by the cookie fixture. Firefox automatic launches remain headless, so no visible Firefox sign-in workflow is claimed supported.
109
+
110
+
111
+ Profile evidence in this workspace: full kept/temporary/idle/contention/cookie fixtures passed against actual installed Edge and Chrome and official unmodified Firefox 144.0 from WSL2; the full Edge fixture also passed from a native Windows Pi host. Deliberately terminating the worker also closed its browser child on these hosts, so those runs verified dead-owner recovery rather than a surviving live orphan. Live-profile contention was refused; the surviving-orphan fixture branch asserts refusal when that process state is observed, but no surviving orphan was observed in those crash runs. The separate controlled dead-writer/live-browser case above now exercises the production refusal/recovery boundary. A focused Edge temporary-crash fixture verified safe cleanup of the recorded dead temporary profile on the next launch. The native Firefox endpoint fixture verified pre-existing tab discovery, cookie sharing, no owned profile creation and preservation of existing/new tabs and the external process. A user-reported Bitwarden interactive sign-in and expected post-restart logout are recorded separately above; the later [GitHub check](validation.md#manual-github-remembered-sign-in) completes the real-site remembered-authentication gate.
112
+
113
+ Browser key aliases are canonicalized before page dispatch, dialog handling, history navigation and the existing browser-window shortcut refusal set. `return`, `del`, `pgup`/`pgdn` and lowercase `arrow*` match their named keys. The local browser-actions fixture checks actual DOM key outcomes, prompt acceptance through `return`, and alias/case forms of existing refused shortcuts without changing foreground/tab state. This preserves the reference refusal policy; it does not newly forbid page key combinations or claim native browser-window shortcuts are supported.
@@ -0,0 +1,153 @@
1
+ # Pi computer-use implementation plan
2
+
3
+ Status: 0.1.0 is published with its recorded acceptance limits. Post-release delivery is owned by the [current queue](tickets.md#execution-queue), including the agreed input-timing change below. [ADR 0004](adr/0004-pi-first-acceptance.md) remains binding; DSH/manual parity runs are not prerequisites.
4
+
5
+ Requirements and behavioral contracts are authoritative in the [specification](spec.md). Use the [documentation index](README.md) to load only relevant context.
6
+
7
+ ## Input timing delivery
8
+
9
+ The [input timing/modifier-tap spec](spec.md#input-timing-and-modifier-taps) is the only requirement authority for this first post-release change. It extends existing tools. All three slices are implemented and accepted at the agreed seams, merged into main through PR #44 and prepared for 0.1.1. The [composed receipt](validation.md#input-timing-and-modifier-tap-acceptance) owns the source scope, final checks, live results and fixture-failure dispositions. Published npm 0.1.0 remains unchanged.
10
+
11
+ 1. Extend existing action schemas/normalizers and public tool/worker tests for standalone generic modifiers, timing values, route capability refusal and unchanged defaults.
12
+ 2. Keep per-call duration/deadline calculation in reusable request handling rather than duplicate it in host adapters. Thread effective policy through Pi/worker/native execution so omitted, explicit positive and disabled execution deadlines remain consistent. Reuse controlled request peers for long-duration/overflow/cancellation checks without real long sleeps; do not add a general scheduler or test-only production API.
13
+ 3. Extend native text/pointer execution with cancellable, representable pacing/hold waits and release acknowledgments using current input ownership. Keep each hold within one call and retain target/focus/protection checks. Reject unsupported explicit backend combinations before dispatch.
14
+ 4. Run affected configured regressions and package checks; extend the current owned text/pointer fixtures for one grouped live acceptance of actual down/up effects, exact text and newly affected cleanup. Exercise both host request paths only where changed forwarding requires it. Reuse accepted stop/display/profile evidence and retain any observed failures.
15
+ 5. Reconcile the existing topic docs/tool descriptions with the implemented fields and record validation. The second input-coverage change and any release/version bump need their own delivery work; do not expand this slice or mark it complete from portable mocks alone.
16
+
17
+ The delivery is complete for #41 keyboard, #42 text and #43 mouse. Reuse the linked acceptance; do not rerun historical release checks. Future broader input coverage and any merge/version/publication remain separate work.
18
+
19
+ Existing code locations for implementation navigation: `src/text.ts`, `src/pointer.ts` and `src/browser-actions.ts` own routed argument contracts; `src/extension.ts`, `src/client.ts`, `src/native.ts` and `src/worker.ts` own forwarding, deadlines and lifecycle; `assets/text.cs` and `assets/pointer.cs` own native effects/release. Prefer existing `tests/text.test.ts`, `tests/pointer.test.ts`, `tests/worker.test.ts`, input-preparation/extension tests and native text/pointer fixtures. These are delivery pointers, not a new shared-module hierarchy.
20
+
21
+ ## Structure
22
+
23
+ Keep flat, responsibility-named TypeScript modules in `src/`, tests in `tests/`, and packaged assets in `assets/`. Keep host-specific launcher and Windows interop logic separate from reusable orchestration through modules, without pre-creating nested directories. The [repository layout](README.md#repository-layout) is the navigation guide.
24
+
25
+ ## Design revision
26
+
27
+ The 2026-11-01 review covers the current runtime module structure, native-helper responsibilities, test runners and documentation ownership. It is a design review with a bounded first cleanup, not a claim that every line or Windows behavior has been revalidated. That cleanup preserved the then-current requirements; the later exclusive-control decision is scoped separately below.
28
+
29
+ | Area | Finding and design decision |
30
+ | --- | --- |
31
+ | Pi integration, launcher and worker | Preserve the host adapter and worker transport. Native Windows and WSL2 are real launch variations; callers should use the same tool interface. Keep native implementation details behind that interface. |
32
+ | Native execution, stopping and ownership | Preserve independent stopping and the ownership lease. Cleanup and uncertain-effect checks belong with the code that owns the processes or input; deleting them for brevity would weaken the contract. |
33
+ | Argument validation | Keep one existing schema/normalizer responsible for each routed action. Remove unreachable alternatives and repeated identical validation. Native and browser policies differ, so a universal validator is not justified by similar loops alone. |
34
+ | Browser implementation | Session/profile lifecycle, engine-specific observations and action dispatch are distinct responsibilities in the large browser module. Isolate a responsibility when a concrete change needs it; keep its small caller interface and existing external tests. Chromium/Firefox adapters are justified only for genuinely shared operations with different implementations. |
35
+ | Test host setup | Reuse the existing `computerUseHost` adapter rather than rebuilding mocks in individual tests. Retain registration history as well as the current tool map so duplicate registration remains observable. |
36
+ | Test orchestration and verification | Pure report verification runs in process. Process framing, cancellation, real Pi integration and Windows execution retain process tests. Keep one implementation of each guard; portable negative tests and live outcome checks have different jobs. The browser fixture still combines scope enforcement, task briefs and report verification; separate these responsibilities only when it reduces coupling without adding a framework. |
37
+ | Diagnostics | Each retained probe must answer a named unresolved question. Replace obsolete probes with behavior-level regression coverage before removing their command/documentation references. Do not keep adding ownership controllers to test ownership controllers. |
38
+ | Documentation | Requirements stay in the spec, design/delivery here, accepted evidence in validation, and next actions in the ticket queue. Replace repeated status prose rather than append another account of the same result. |
39
+
40
+ The first cleanup removes duplicate test setup, process wrappers around pure verification, unreachable browser validation and a redundant limit pass. It preserves the existing assertions and strengthens the existing public-worker argument test before deleting validation. It introduces no planner, controller, production dependency or tool interface. The only new file is a type declaration for the reused test host. See [cleanup dispositions](tickets.md#cleanup-dispositions) for the current decisions.
41
+
42
+ Continue with one responsibility at a time: identify the concrete duplication or coupling, show the smaller interface or deletion, and run the affected existing checks. A file split that merely relocates the same complexity is not a simplification. Full runtime restructuring and remaining live acceptance are not represented as complete by this cleanup.
43
+
44
+ ## Default-app and workspace delivery
45
+
46
+ #30 completed the [accepted policy](spec.md#default-apps-workspaces-and-foreground): Windows handler resolution/opening, existing native-app/ordinary-Chrome workspace preservation and foreground intent, independently reviewed [ambiguity/unsafe-workspace decisions](validation.md#workspace-decision-acceptance), and [popup/menu/drop/restoration acceptance](validation.md#scoped-pointer-and-restoration-acceptance). Reuse these scoped results. Dedicated protocol mode remains explicit; native UI operation does not broaden browser-protocol compatibility. Native creation/recovery (#35) and the reconciled display scope (#6) are complete; reuse their recorded receipts.
47
+
48
+ Acceptance uses owned fixtures and disposable browser profiles/windows as stand-ins for an existing user workspace: verify the resolved app identity, original tab/document contents unchanged, exactly one new task workspace, reuse on subsequent steps, explicit new-window request, no-existing-window launch, and unsupported/ambiguous outcomes. Independently observe foreground after opening and between related actions, explicit background preservation, result-visible completion and human handback without delayed focus restoration. Share editor checks with #26 and restoration checks with #30; reuse accepted stop/control evidence. Tests must not change the user's actual Windows default associations or operate personal documents. Current Chrome/Edge isolated-channel passes remain scoped evidence, not acceptance of this policy.
49
+
50
+ ## Feasibility gates
51
+
52
+ These original feasibility gates remain regression obligations. Reuse recorded evidence; repeat checks when changes or missing evidence warrant it:
53
+
54
+ 1. Prove a packaged Windows worker launches from native Pi and WSL2 Pi with clean stdio framing, spaces/Unicode paths, Windows-drive and WSL-filesystem checkouts, cancellation, parent exit and no orphan processes. Users must not install Windows Node separately.
55
+ 2. Retain the selected TypeScript-managed native helper for UIA, screenshots, OCR, input and stopping. Revisit interop only for a demonstrated Pi defect or concrete simplification; the existing choice does not need another comparison programme.
56
+ 3. Prove background Chrome/Edge actions, isolated/kept profiles and installed Firefox BiDi control. Evaluate libraries only where they preserve compatibility, targeting, cancellation and background behavior; do not replace installed Firefox support with managed Firefox without revisiting the decision.
57
+ 4. Prove ownership exclusion between a native Windows session and a WSL2 session, including crash recovery.
58
+
59
+ Record measurements and outcomes. If a gate fails, bring the concrete trade-off back to the user rather than quietly reducing supported Pi behavior or adding manual setup requirements.
60
+
61
+ ## Coverage matrix
62
+
63
+ For #6/#20, exercise the opt-in [window-only screenshot contract](spec.md#implementation-decisions) through the existing registered screenshot tool: covered painted pixels, blank-render WGC handoff, hidden/minimized refusal, invalid combinations and unchanged default behavior. Keep strict capture inside the capture module; bypass screen-copy entirely rather than detect unrelated pixels after acquisition. Then use this mode for owned display/presentation fixtures. Actual available DPI/layout checks and the local Snipping Tool result are accepted in the [composed #20 receipt](validation.md#snipping-tool-recording-acceptance); retain its limits.
64
+
65
+ The existing capture runner supplies backend-free plans and two bounded live modes: `--display` measures per-monitor PMV2 window DPI, independent DWM geometry and painted-point mapping at native/reduced PNG sizes; `--control-capture` samples owned edge pixels before/after strict capture and checks control identity, per-call suspension and settlement release. Run current settings first. In one grouped human session, change only the 1920×1080 monitor from its original 100% to 150%, then the highest available per-monitor preset (175% on this machine), keeping the 2560×1080 monitor at 100%; run both modes at each stage and restore the original settings. The user reports 200% requires custom scaling, sign-out and changing both monitors, so it is unavailable in this scoped session and remains untested; do not apply that configuration. The changed arrangement and local Snipping Tool recording are accepted; unexercised display/primary/recorder variants remain outside the claimed evidence. No command changes global display settings.
66
+
67
+ Every supported tool gets argument validation, success, failure, cancellation where applicable, and unsupported-target coverage. A matrix entry maps to named tests or an explicit manual procedure; mocked protocol success alone cannot establish real desktop support.
68
+
69
+ | Area | Required coverage |
70
+ | --- | --- |
71
+ | Host launch and packaging | Clean install; native Windows and WSL2; missing/corrupt assets; spaces/Unicode; both filesystem locations; startup failure; session reload; parent exit; child cleanup |
72
+ | Window discovery and management | List, target ambiguity, HWND/PID identity, stale/reused windows, launch, activate, move, close, unresponsive targets and inaccessible/elevated windows |
73
+ | UI Automation | Snapshot, tree, find, element info, focus, invoke, set value; depth/node/time budgets; stale elements; sparse/incomplete trees; native and Chromium fixtures |
74
+ | Capture and OCR | Screen/window capture paths and fallback metadata; occlusion/minimized windows; blank capture; image scaling; multi-monitor/DPI coordinates; literal OCR queries; inconclusive absence; [scoped availability/language/cancellation evidence](validation.md#pi-ocr-failure-acceptance), with clean-machine availability owned by #2 |
75
+ | Physical and background input | Click/double click/move/drag/scroll/type/key chords; background dispatch and escalation; covered points; focus changes; exclusive-control suppression and human handback; Unicode; clipboard preservation; key/button release |
76
+ | Browser control | Chrome/Edge and experimental installed Firefox; tab discovery, navigation, element IDs, reading, input, dialogs, new/closed tabs; navigation invalidation; disconnect/reconnect; cancellation; supported download/upload paths |
77
+ | Browser profiles | Temporary isolation, opt-in persistence, profile ownership, crash recovery and owned-resource cleanup for explicit dedicated mode; ordinary browser-window reuse under the [workspace policy](spec.md#default-apps-workspaces-and-foreground); interactive sign-in checked manually |
78
+ | Operation lifecycle | Multi-turn ownership; parallel calls; busy competitors across hosts; queued cancellation; emergency stop during input; blocked same-operation retries; new user operation; worker timeout and recovery |
79
+ | Settings and tool management | Defaults, persistence, invalid settings, full/lite/lazy/custom modes, execution denial, settings changes while active, stop shortcut and indicator configuration |
80
+ | Presentation | Pi tool rendering, image results, evidence/error distinctions; real indicators, stop shortcuts, remote/recording visibility and display changes checked manually |
81
+ | Performance | Pi startup, reads, input and representative-workflow measurements where needed to diagnose usability or regressions; no DSH target |
82
+
83
+ Use deterministic local pages, isolated browser profiles and disposable Windows targets. The user selected scoped tests in the current Windows session with standing idle-test authorization. Bound actions to owned disposable targets and preserve unrelated apps/documents; serialize desktop activity. Separate portable, live and physical-input suites. A fixture requiring stronger isolation stays blocked until its actual prerequisites are met.
84
+
85
+ Exercise the integrated extension through both native Windows Pi and WSL2 Pi; worker-only tests do not prove both launch paths. Pin/document tested Pi and dependency versions. Retain failure logs and timing artifacts without persisting sensitive desktop content unnecessarily. CI may run portable tests and suitable Windows fixtures; interactive-desktop, multi-monitor, emergency-stop and user acceptance runs must be recorded separately when CI cannot reproduce them.
86
+
87
+ ## Detailed milestones
88
+
89
+ The foundation, desktop and browser implementations exist. Complete the remaining Pi outcomes rather than restarting implementation or collecting another DSH baseline. Requirements stay in the [spec](spec.md); named cases/results stay in [validation](validation.md); the [queue](tickets.md#execution-queue) owns next actions.
90
+
91
+ | Phase | Work | Exit evidence |
92
+ | --- | --- | --- |
93
+ | 1. Reconcile scope and reuse | Remove DSH manual/parity gates, preserve Pi obligations, map existing accepted cases to their owners. | Current issues and docs have one Pi-first scope; historical comparisons are clearly historical. |
94
+ | 2. Close capability gaps | Finish the named missing Pi text/chord, clipboard, targeting, pointer/control, display and OCR cases. Diagnose only an observed Pi failure. | Independent outcome/cleanup evidence or explicit unavailable/accepted-limit disposition; affected regressions pass. |
95
+ | 3. Finish native agent outcomes | #35 creation/preservation, replacement/partial recovery and composed continuation are accepted. #36 retains its six accepted browser/mixed scenarios. | Named cases passed three fresh runs with effective model metadata and independent outcomes. Actual human/control proof is reused from #31; #20 presentation acceptance is also complete within its recorded scope. |
96
+ | 4. Release | Complete #21's remaining functional, clean-install, manual and packaging checks; document known limits and useful Pi responsiveness. | Every Pi release obligation has a result/disposition; no DSH setup, credentials, manual run or timing comparison is needed. |
97
+
98
+ The completed #35 continuation case [composes registered-tool cancellation and a labelled stand-in edit with real-Pi resumption](validation.md#native-cancellation-and-handback-continuation). Reuse #31's accepted actual-human/control proof; this creates no repeated physical-stop gate.
99
+
100
+ Execute per bounded case: establish the Pi result, diagnose an actual failure, make a targeted repair and rerun that case/affected regressions. A blocked case stops dependent activity; it does not reopen accepted cases or mandate a framework. No fixed production retry count or new agent policy is introduced.
101
+
102
+ Reconcile affected docs during each change: requirements here remain in the spec, delivery here, cases/results in validation, behavior in topic guides and rationale in ADRs. Replace stale instructions; preserve historical evidence without making it a current gate. #23/#37 are superseded umbrellas: capability owners retain targeted diagnosis, #35 retains native outcomes, and #21 retains aggregate release reconciliation. The [cleanup dispositions](tickets.md#cleanup-dispositions) still govern removal of demonstrated redundancy.
103
+
104
+ ## Exclusive control delivery
105
+
106
+ The [accepted control contract](spec.md#exclusive-control) changes shared-input behavior. The candidate uses keyboard/mouse hooks in the existing stop helper, an operation-scoped protection event and the existing worker preparation/settlement protocol. Portable refusal/release regressions and scoped no-input acquisition/handback checks exist; native Pi-host Esc has accepted evidence; known-held cancellation/error, WSL physical shortcut and human handback now have accepted evidence. #31 is accepted through the [composed interruption/cleanup evidence](validation.md#control-interruption-acceptance); presentation and the other capability owners retain their scoped work. Reuse the operation lifecycle, stop listener and existing flat native modules. Keep suppression, emergency-key interpretation and release in one responsible control boundary; tool-specific guards consume its state. Avoid a second ownership controller or public test-only API.
107
+
108
+ | Slice / owner | Work and exit evidence |
109
+ | --- | --- |
110
+ | 1. Mechanism feasibility — #31 | On owned fixtures, prove agent input works while ordinary physical input is suppressed, Esc/shortcut remain observable, Windows escape paths remain available, and protection releases after normal completion and failure. Test the actual native Windows and WSL2 launch topology. Compare hook and BlockInput constraints with the existing process/thread layout; select the smallest mechanism that meets the contract. Record unsupported cases. Enforce the [physical fixture prerequisites and cleanup gate](stopping.md#scripted-native-pi-host-fixture); never force-retire uncertain input owners. |
111
+ | 2. Integrate lifecycle and presentation — #31, #20 | Use existing operation start/settlement/cancellation paths for acquire, hold, handback and release. Verify the required border through reads/thinking, disabled presentation settings, capture, displays and idle periods. Handback pauses dispatch and explicit continuation revalidates/reacquires. A physical-key completion assertion includes stop observation, cleanup, release and retry protection; error classification is checked separately and remains a defect when wrong. |
112
+ | 3. Regress workflows and prune — #26, #27, #30, #31, #35; reconcile #21 | Extend existing pointer/text/clipboard and agent fixtures. Physical typing/movement/click attempts during control have no app effect; during handback they work and are preserved. Test full shortcut chords, agent Escape, real stop during busy input, owner/listener loss and safe interruption/cleanup on lock/session changes. Reuse accepted locked-input refusal and handback checks; target survival or seamless unlock recovery is not a release gate. Retain stale/occluded/foreground/clipboard guards. Remove only branches proven redundant under the new lifecycle, with affected regressions passing. |
113
+
114
+ The local nonelevated `BlockInput(true)` prerequisite returned Win32 access denied (5), without blocking or dispatching input. On 2026-11-01 the user accepted hooks with documented Windows limits for the first release, as recorded in [ADR 0003](adr/0003-exclusive-desktop-control.md). Hook suppression is not equivalent to OS input blocking: after silent hook removal, the observer cannot prove an event was suppressed or that Esc remains available during idle gaps. That platform limit no longer awaits a product decision. Actual-host integration, physical stop/release, lifecycle and failure acceptance remain required; bounded physical acceptance requires fresh same-launcher normal/host-exit proofs and per-run participant/input-cleanup receipts, without claiming held-input crash cleanup in advance.
115
+
116
+ Portable lifecycle tests precede integrated Windows checks. Group genuine physical-key/interference and border checks into one short human session rather than repeated prompts. Each result names scope (worker, actual Pi host, native Windows or WSL2); synthetic input alone does not prove physical suppression. Required real-agent scenarios retain their three-reset acceptance. Finish the feasibility slice before broader rewrites; a failed mechanism leads to a recorded limitation/design revision, not silent fallback to shared input.
117
+
118
+ ## Window lifetime design
119
+
120
+ The user approved stronger tracking under #28. [ADR 0002](adr/0002-window-lifetime-references.md) records the independently reviewed design; the native lifetime implementation has passed independent review with [scoped acceptance](validation.md#window-lifetime-reference-acceptance). Raw selectors remain explicitly weaker compatibility selection.
121
+
122
+ Use the existing native bridge as the sole reference authority. One optional `windowRef` identifies an observed lifetime; HWND/PID/title and raw native-window IDs remain compatibility selectors. A supplied reference cannot be discarded or restamped internally. Conflicting selectors fail explicitly. Native-child restart expires its references; fresh observation is recovery, never automatic mutation replay.
123
+
124
+ Bind cached UIA elements to the root reference and an immutable issued element ID, retaining RuntimeId as comparison metadata. Later observations must not overwrite an old reference with another root binding. Reuse the existing private WGC target handoff for stateless marker validation; OCR, snapshot images, waits, implicit targets and physical fallbacks must retain the originating reference.
125
+
126
+ If marker acquisition fails, preserve ordinary read metadata and an explicit reference-unavailable reason, but issue neither a strong window reference nor actionable native element IDs. Raw native-window selectors remain explicitly compatible; a failed strong reference is never silently converted into one. This avoids presenting an unbound element ID as safe to mutate.
127
+
128
+ Snapshot's existing default `captureWindow: false` produces a desktop image. Keep that image separately labeled as desktop evidence while the tree carries its window reference; do not forward a window reference into desktop capture or imply those pixels belong to the bound window. Window-image, OCR and hit-test composition retain the observed root reference.
129
+
130
+ Missing markers, expiry, unknown epochs and access denial are lost proof, not confirmed disappearance. Validate before dispatch and at existing multi-step checks; already-dispatched effects and concurrent destruction after the final check cannot be undone or atomically excluded. Normal expiry/disposal removes only owned markers. Residue after abrupt native-child exit remains explicit; no property sweeper, broker, privilege bypass or new event thread is planned.
131
+
132
+ The owned-window probe and interface were independently reviewed before implementation. Existing public tool/worker and owned fixtures now exercise the implemented contract, including a provider pause before final validation and a separate post-effect proof-loss case with independent counters. [Current scoped evidence](validation.md#window-lifetime-reference-acceptance) retains isolated-session gaps and distinguishes the separately recorded #6 automatic capture repair. Actual identical-HWND reuse and verified UIPI denial were unavailable in bounded owned probes; the user accepted them as [first-release test limitations](validation.md#pi-targeting-and-accessibility-reconciliation) on 2026-11-05. Different handles or synthetic denial cannot be mislabeled as passes, and identity/access safeguards remain binding. The optional owned-fixture real-time expiry case now validates the actual five-minute lifetime without refreshing or restarting its authority. Paired warm checks add no fixed sleeps or extra model rounds.
133
+
134
+ Live binding tests stay on disposable owned windows. Global discovery and internal launch-enumeration paths must be reviewed for metadata writes; tests that would bind unrelated windows require a suitably isolated session rather than bypassing the user's scoped-test choice.
135
+
136
+ ## Completion criteria
137
+
138
+ - All 25 supported Pi capabilities have implementation/test mappings; experimental and unsupported cases are labeled honestly.
139
+ - Required portable and live integration suites pass on the agreed hosts, with manual indicator/stop/display checks and user acceptance recorded.
140
+ - Clean installation works with default Pi, with runtime/helper dependencies supplied or managed by the package and no extra Pi extension or DSH installation.
141
+ - Observed Pi responsiveness issues and regressions are measured and addressed or explicitly accepted without removing safeguards. DSH performance is not a gate.
142
+ - README covers installation, prerequisites, development, build/package, tests, troubleshooting, settings/profile locations and known limitations.
143
+ - The consolidated scope has been confirmed by the user; implement against the specification and report feasibility failures before changing its contracts.
144
+
145
+ ## Technology choices to resolve through feasibility work
146
+
147
+ - Packaged worker feasibility and the minimum necessary native interop.
148
+ - Library choices that reduce custom browser and Windows interop logic without sacrificing agreed behavior.
149
+ - Exact ownership primitive, worker framing, package data paths and bounded shutdown deadlines, supported by the feasibility results.
150
+
151
+ ## Evidence
152
+
153
+ See [Pi extension research](pi-extension-research.md). The inspected reference is version 0.5.3 at commit `6fb642129595a6357588c70812974951db304662`.
@@ -0,0 +1,53 @@
1
+ # Control indicators
2
+
3
+ The candidate ties the required border to [exclusive-control acquisition and release](spec.md#exclusive-control). Appearance, lifecycle, human/control and display acceptance are complete within the recorded #20/#31 scope; release reconciliation remains #21.
4
+
5
+ The independent Windows stop helper also draws Pi-branded screen edges, a status bar and a pointer tag. The screen border uses a per-pixel translucent blue falloff with a bright core, following the reference glow at 1.5 times its width: a 3-pixel core plus 27-pixel falloff at reference width scale 1, scaled by monitor width (clamped from 1 to 2). The separate Pi status bar and pointer tag retain their existing appearance. If frame preparation or presentation fails, that frame stays hidden and its error is reported until configuration or display layout causes a rebuild; the other indicators continue. It uses non-activating, disabled, layered windows; the pointer guard additionally skips the verified helper process if a Windows point-query API still returns an overlay. The tag follows the pointer, accounts for the current cursor bitmap size and stays inside its monitor's working area.
6
+
7
+ ## Current operation lifetime
8
+
9
+ Arming alone does not acquire physical control or show the border. Before a physical action, the worker acquires suppression and verifies the border; quiet-only actions do neither. The border remains through subsequent reads, tool gaps and model thinking, without an idle expiry. Stop or confirmed operation settlement releases control and hides it. Failed border creation refuses acquisition; loss of the interactive desktop or required border signals stop. Optional screen/pointer preferences cannot disable the local control border; `indicatorIdleMs` remains a saved compatibility preference and does not delay release or expire exclusive control.
10
+
11
+ Settings come from the existing personal settings manager. Screen and pointer presentation are independent; disabling either or both never disables global stopping. Configured changes are applied to a running helper, including when a UIA request is blocked. Esc remains reserved and an extra Ctrl/Alt/Shift/Win shortcut can be configured. Injected and lower-integrity-injected keyboard events are ignored by the stop hook so an agent's Escape action can dismiss UI without stopping its own operation.
12
+
13
+ Own native screenshots/OCR suspend overlay windows and restore presentation in `finally`, retaining the armed stop event throughout. Remote/recording visibility selects Windows display affinity; helper status reports whether that call actually succeeded. [Microsoft documents this as applying to supported capture APIs](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity); it does not establish universal recorder behavior. Actual recorder results need a named application/method and human observation.
14
+
15
+ ## Preview and diagnostics
16
+
17
+ - `/computer-settings preview check` draws an offscreen sample and reports monitor layout/settings without displaying overlays or arming physical stopping.
18
+ - `/computer-settings preview` displays a two-second preview without input and without arming Esc. It does not modify saved preferences.
19
+ - `health` reports control-helper status only if already started; quiet health does not start overlays or a global listener.
20
+ - `npm run test:indicators` uses disposable Windows settings and exercises configuration, offscreen rendering, shortcut parsing, operation lifetime, capture suspension and disabled-indicator stopping without physical input.
21
+ - `npm run test:indicators -- --recording-preview --plan` describes a backend-free, unarmed preview. The live `--recording-preview` mode displays recording-visible and recording-hidden settings for two approximately eight-second steady phases, with a brief expiry gap between them. Both phases keep the local border visible; it sends no input, acquires no exclusive control and captures no pixels. Use the shared live-test lock. The user operates their recorder and inspects the result locally; the runner's successful helper metadata is not a recorder verdict.
22
+ - Set `PI_CU_ENABLE_INDICATOR_PREVIEW=1` for the same runner's brief visible show/hide, native hit-query and capture-suspension checks. Run live checks under `flock -w 120 /tmp/pi-cu-live-tests.lock` from WSL to avoid overlap with other desktop fixtures.
23
+
24
+ The updated glow border is source-reviewed. Portable checks, package validation and compilation of the Windows stop/indicator helper passed; the helper was not launched for these checks. A later opt-in Windows run passed visible edge/bar/pointer count, frame-error, screenshot-suspension, bar hit-query and hide checks on two monitors with owned process cleanup. This does not establish human visual appearance, edge click-through, alpha fidelity or third-party capture behavior. An earlier transient screenshot invalid-handle failure did not recur and its cause remains unknown. Automated protocol state and native window-query checks establish behavior, not human visual acceptance. Physical keys and mouse input are not emitted by this runner.
25
+
26
+ A later approximately ten-second preview used temporary settings and showed the expected frame and bar windows on two monitors across six status observations, with no frame errors; the disposable worker and settings were cleaned up and no input or screenshots were sent. The user confirmed that the blue border's appearance and thickness looked right. This does not establish edge click-through, alpha fidelity, third-party capture or real-control lifecycle. A separate read-only Windows hit-test placed a disposable topmost window at each monitor's top-left corner. At one point inside each visible edge, `WindowFromPoint` returned the disposable window both before and during the preview on both monitors; both frames and bars were visible with no frame errors. This samples two edge locations only; actual mouse delivery, remaining edge regions and pointer-tag pass-through remain unverified.
27
+
28
+ ## Acceptance checklist
29
+
30
+ Current #20 scope reuses accepted control results; an unchecked item here is not a request to repeat a completed physical-stop test.
31
+
32
+ - [x] Human appearance/thickness acceptance and the two sampled native edge hit-tests above.
33
+ - [x] Required border retention across reads, a gap longer than the configured idle value, optional-visual/remote setting changes and a refused settings update; immediate hide after settlement/stop and show for an explicit new operation. See [automated lifecycle evidence](validation.md#automated-border-lifecycle).
34
+ - [x] Actual human editing/explicit continuation and physical stopping from [#31's accepted receipts](validation.md#control-interruption-acceptance). The new lifecycle check supplies presentation state across release/reacquisition; no new physical-key test is queued. Injected-key filtering retains its existing source/protocol scope, not a new dialog-keyboard workflow claim.
35
+ - [x] Owned strict PrintWindow pixel exclusion, capture restoration and retained control at 100%, 150%/100%, 175%/100% and the changed negative-origin arrangement. [Pixel and layout receipts](validation.md#changed-monitor-arrangement) compose with accepted protocol suspension. This does not claim pixel coverage of every default screen-copy, WGC or OCR path.
36
+ - [x] Actual available 100%/150%/175% scales, mixed-DPI window transitions and a changed display arrangement passed. Both scales are restored; the secondary returned to the right with a recorded vertical alignment difference. See [measured stages](validation.md#mixed-dpi-stages). #6's practical first-release scope is accepted; unavailable 200% and other unexercised display variants stay unverified; optional bars/tag are not universally certified by frame-only pixel samples.
37
+ - [x] Human-confirmed Windows Snipping Tool region recording: local border visible in both steady phases, included in the recording when enabled and omitted when disabled. [Receipt and composed scope](validation.md#snipping-tool-recording-acceptance). Other recorder/remote methods and unexercised per-route overlay pixels are not certified by this result.
38
+
39
+ The current desktop has two 100%-scale monitors. Unmeasured display/capture configurations, remaining hit-test regions and third-party recording must not be inferred from the accepted strict-path pixels or preview/status assertions. The [execution queue](tickets.md#execution-queue) records completed #6/#20 and the remaining release work.
40
+
41
+ ## Browser cues
42
+
43
+ Owned Chromium pages use a Pi favicon while active and a small page-coordinate click marker in an isolated, closed shadow root. The host is `aria-hidden` and every marker layer uses `pointer-events: none`. Native browser actions remain quiet: these page cues do not arm desktop stopping or show desktop overlays. Page screenshots hide the marker and restore it in `finally`; results report whether hiding was confirmed.
44
+
45
+ Visible owned Chromium processes receive a Pi-blue Windows 11 DWM **border** tint, without installing a browser extension or changing the user's daily browser theme. Only windows belonging to the retained Puppeteer child process are considered. Tab cues and owned-frame tint are removed/restored at operation settlement or stop. Cosmetic failures remain visible as presentation warnings rather than being misreported as successful decoration or replaying an action. Experimental Firefox has no browser presentation cues. External Chromium endpoints may receive page cues, but their browser frames are never tinted.
46
+
47
+ Headless browser fixtures verify badge/marker state, accessibility hiding, closed-shadow isolation, pointer pass-through, screenshot suspension and operation cleanup. Actual browser-border appearance and page-marker visibility still belong to manual visual acceptance.
48
+
49
+ ![Offscreen native statusbar fixture](../assets/indicator-preview.png)
50
+
51
+ Offscreen native statusbar fixture; not full desktop acceptance.
52
+
53
+ A visible Edge fixture confirmed the owned DWM call accepted the border tint and the page/screenshot cue assertions passed. Its broader foreground-preservation assertion changed HWND across browser launch and is unresolved; this is not evidence of completed visual/focus acceptance. Headless action and cue lifecycle fixtures passed.
@@ -0,0 +1,17 @@
1
+ # Native interop comparison — research notes
2
+
3
+ Scope: Windows-side worker launched by native Pi or WSL2; the choice should not require users to install another extension. This is a source/documentation comparison, not a prototype, benchmark, exhaustive package survey, or proof of the globally smallest implementation.
4
+
5
+ ## Established facts
6
+
7
+ - Current source uses a persistent out-of-process C# bridge, built lazily using the Windows .NET Framework64 compiler, with TypeScript managing lifecycle; UIA runs on an MTA thread. The project's `docs/windows.md` records a Windows 11 x64 WSL run and notes missing compiler failures and untested configurations. `docs/runtime.md` records separate Windows Node provisioning and its tested bounds. This is an actual selected design and scoped feasibility evidence, not evidence it outperforms every alternative.
8
+ - Node-API is a stable ABI for native addons, which are native code modules loaded by Node ([Node-API](https://nodejs.org/api/n-api.html), [Node addons](https://nodejs.org/api/addons.html)). Calling the C++ Windows interfaces through an addon can present a TypeScript interface, but requires implementing or adopting and distributing native code. Node advises moving long-running addon work off the event loop. A worker thread avoids blocking the event loop but does not itself isolate native process crashes; a separate process can provide that boundary (engineering inference).
9
+ - Koffi is a concrete Node FFI option: its maintainers document C calls, structs, pointers and callbacks, Node 16+, and packaged prebuilt Windows x64/arm64 binaries ([overview](https://koffi.dev/)). Callback declarations, registered lifetime, threading and calling conventions need care ([callbacks](https://koffi.dev/callbacks)). This adds a native runtime dependency despite TypeScript-facing declarations. Those capabilities do not demonstrate complete UIA/WinRT bindings or behavioral parity for this application.
10
+ - The current pinned Windows runtime is Node 22.23.4 (`docs/runtime.md`). Node's built-in `node:ffi` was added in 26.1 and is experimental, unsafe, and restricted by callback thread and lifetime rules; it is not an option in the pinned runtime without changing that runtime ([Node FFI](https://nodejs.org/api/ffi.html)). Neither a Node version upgrade nor experimental FFI is established as necessary here.
11
+ - UIA is COM-based; Microsoft recommends a separate MTA thread for a client interacting with all desktop elements and notes that COM element lifetimes can depend on their originating apartment ([UIA threading](https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-threading)). An FFI/addon implementation must solve the same COM object, apartment and cleanup problems; generic C-call support alone does not establish that those problems are solved.
12
+ - Capture in this project includes HWND interop and a free-threaded Direct3D frame pool, not only a raw desktop bitmap ([CreateForWindow](https://learn.microsoft.com/en-us/windows/win32/api/windows.graphics.capture.interop/nf-windows-graphics-capture-interop-igraphicscaptureiteminterop-createforwindow), [CreateFreeThreaded](https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture.direct3d11captureframepool.createfreethreaded)). OCR uses the Windows Runtime OCR API's asynchronous operation and bitmap ([RecognizeAsync](https://learn.microsoft.com/en-us/uwp/api/windows.media.ocr.ocrengine.recognizeasync?view=winrt-20348)). Microsoft’s [OCR namespace documentation](https://learn.microsoft.com/en-us/uwp/api/windows.media.ocr?view=winrt-26100) states desktop support requires package identity; the project's observed fixture success on its tested host does not resolve that documented support limitation for unpackaged installations. Do not claim broad supported clean-install OCR from that success.
13
+ - Physical input uses Windows `SendInput`, which accepts native structures and is subject to UIPI ([SendInput](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)). A low-level keyboard hook requires a message loop on its installing thread and must return promptly; Windows may silently remove a timed-out hook ([LowLevelKeyboardProc](https://learn.microsoft.com/en-us/windows/win32/winmsg/lowlevelkeyboardproc)). These Windows restrictions do not disappear through FFI or an addon.
14
+
15
+ ## Engineering judgment and unresolved tradeoffs
16
+
17
+ Retain the current helper provisionally: it already supplies the required apartment, OS bindings, and process boundary without another Pi extension or additional Node FFI dependency, with recorded scoped validation. This supports a practical maintainability decision relative to the two identified approaches; it does not prove minimum size or that Koffi/Node-API could not improve selected functions. The helper still depends on the Windows Framework compiler, WinRT availability and platform-specific code, incurs compilation/process costs, and needs explicit handling of blocked or failed providers. A native addon/FFI approach could simplify isolated Win32 calls or packaging in some scenarios, but would require new bindings, architecture/distribution evaluation and the same semantics, isolation, stop and cleanup evidence; no fair implementation comparison or performance measurements have been made. Revisit when a concrete improvement demonstrably preserves those behaviors on native Windows and WSL2, including clean installation, supported architectures and failure paths. No current evidence warrants a rewrite.
@@ -0,0 +1,25 @@
1
+ # Desktop ownership
2
+
3
+ Desktop ownership excludes competing agents. The candidate adds [exclusive control](spec.md#exclusive-control) for physical user input; input cleanup and confirmed control release precede ownership handoff.
4
+
5
+ The Windows worker acquires an OS named mutex on the first desktop request in a computer-use operation. A separate small helper holds the mutex, so UI Automation work cannot block ownership control. The mutex is scoped to the Windows interactive session; both native Windows and WSL2 hosts use Windows workers and therefore contend for the same mutex.
6
+
7
+ Competing sessions receive `COMPUTER_USE_BUSY` immediately instead of queueing stale actions. The owning client's requests are serialized before execution deadlines start; queued cancellation does not send the cancelled request. A Windows mutex has no heartbeat expiry: a slow live owner is not displaced, and Windows releases the mutex when its owning thread/process exits. No filesystem lease is stolen based on elapsed time.
8
+
9
+ ## Pi lifecycle
10
+
11
+ The extension assigns an operation identifier when a user run starts, retains it through model turns, low-level `agent_end` events and custom continuations, and sends the internal `operation_end` request at `agent_settled`. A new user run ends prior owned activity before starting another operation. Session shutdown disposes the worker and its helpers. Health does not acquire desktop ownership.
12
+
13
+ Human handback uses the same final-settlement path: the agent explains the human step and finishes its run before the user acts. The next user message starts a fresh operation, with fresh observation before further input. There is no separate pause controller or model-facing handback tool.
14
+
15
+ The internal worker envelope accepts an optional operation identifier; direct worker consumers without one use the worker's process-scoped operation. They must end the operation or dispose the worker to release ownership. This control is not an additional model-facing tool.
16
+
17
+ ## Validation
18
+
19
+ - `npm test`: public worker-interface regressions prove serialized execution budgets and queued cancellation; a Pi-host protocol peer proves operation identity and settlement behavior. These portable tests do not establish real Windows mutex behavior.
20
+ - `npm run build` followed by `npm run test:ownership` from WSL2: real native Windows and WSL2 extension factories invoke their registered window-listing tools against distinct Windows workers. The test verifies busy rejection, retention across `agent_end`, settlement handoff, non-stealing of a live owner and recovery after killing the owning worker. It sends no physical input and operates on no application contents.
21
+ - Full Pi CLI acceptance remains a release check. Live testing requires WSL interop/Windows process access and the packaged private runtime.
22
+
23
+ ## Physical-input acceptance
24
+
25
+ The [stopping contract](stopping.md) supplies independent listener/event control and bounded retirement for cancelled read-only bridges. Four known-held cancellation/target-error cases, actual WSL Pi physical shortcut and ordinary human handback/resume now have [scoped passing evidence](validation.md#known-held-input-release). The [composed interruption/cleanup evidence](validation.md#control-interruption-acceptance) completes #31; target survival or seamless unlock recovery is not required; caller cancellation alone is not input-cleanup evidence. Current passive results and the difference from DSH host activity tracking are recorded in the [passive operation baseline](validation.md#passive-operation-evidence).
@@ -0,0 +1,57 @@
1
+ # Pi extension integration research
2
+
3
+ Checked 2026-10-29. This document records verified interfaces separately from proposed design choices. It does not claim that desktop behavior has been tested.
4
+
5
+ ## Version and identity
6
+
7
+ The local installed package is `@earendil-works/pi-coding-agent` **1.4.0**, under `/home/ycheung5/.nvm/versions/node/v22.20.0/lib/node_modules/`. Its installed declaration files were checked against the official source. Current package metadata uses the `@earendil-works` namespace and identifies `earendil-works/pi` as the repository; older `badlogic/pi-mono` links still expose the project. Implementation should target the installed version rather than assume older tutorials match. [Official package metadata](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/package.json)
8
+
9
+ ## Verified extension interfaces
10
+
11
+ - An extension exports a default factory receiving `ExtensionAPI`. TypeScript loads without a separate compilation step. Register long-lived resource cleanup with `session_shutdown`; start processes lazily or during `session_start`, rather than inside the factory. Legacy `@mariozechner/*` imports remain mapped, but new code can use current names. [Extension documentation](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md)
12
+ - `pi.registerTool()` accepts a name, label, description, TypeBox parameter schema, and `execute(toolCallId, params, signal, onUpdate, ctx)`. The optional `executionMode: "sequential"` requests serialization with other calls. `ctx.abort()` aborts the current operation. Tool exposure and activation are separate concepts; `setActiveTools()` changes declarations and does not make deferred tools uncallable. [Extension types](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/extensions/types.ts)
13
+ - Pi also exposes `registerMcpServer()`, `unregisterMcpServer()`, and `getMcpServers()`. An extension can therefore register the reference's standalone stdio server as an incremental migration path. [MCP registration interface](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/extensions/types.ts)
14
+ - A tool result has model-facing `content` and rendering/state `details`. It may additionally carry `structuredContent`, `isError`, and `terminate`. Error text alone does not signal failure: throw or return `isError: true`. Partial updates use the same result shape. `terminate` is a batch-level hint, effective only when every finalized result in that batch terminates. [Agent result contract](https://github.com/earendil-works/pi/blob/main/packages/agent/src/types.ts)
15
+ - `content` supports images with `{ type: "image", data, mimeType }`; `data` is base64. Thus screenshots can accompany textual window/coordinate metadata without inserting base64 into text. Actual visual reasoning still depends on the selected model. [Image content definition](https://github.com/earendil-works/pi/blob/main/packages/ai/src/types.ts)
16
+
17
+ ## Verified lifecycle and packaging
18
+
19
+ `agent_end` is not the final idle notification: retries or queued work can start another run. `agent_settled` represents final settlement. `turn_end` is one assistant response plus its tools, not an entire user task. Session replacement and reload invoke shutdown, followed by startup for the replacement runtime. Tool execution events include terminal outcomes even when execution was blocked or aborted. The documentation explicitly demonstrates checking the tool's abort signal and passing it to subprocess work. [Lifecycle and tool documentation](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md)
20
+
21
+ A package can declare `"pi": { "extensions": ["./src/extension.ts"] }`. Test a local entry with `pi -e`; install a directory with `pi install ./local-package`. Published runtime dependencies belong in `dependencies`; host-provided Pi packages and TypeBox belong in peer dependencies, not bundled runtime copies. Project-local package loading follows project trust. [Package documentation](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/packages.md)
22
+
23
+ The official tool-selector example uses `getAllTools()`, `getActiveTools()`, and `setActiveTools()`. It persists selection with `appendEntry()` and restores state from the current branch on `session_start` and `session_tree`. [Tool-selector example](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/tools.ts)
24
+
25
+ ## Recommendations for this project
26
+
27
+ These are design proposals, not guarantees supplied by Pi:
28
+
29
+ 1. Use native Pi tools backed by a host-independent Windows engine. MCP is optional transport, rather than a required intermediary for every desktop call.
30
+ 2. Keep an engine queue despite Pi's sequential tool setting: commands, native stop events, and separate Pi processes are outside that tool batch. Add desktop ownership across processes.
31
+ 3. Connect each tool's abort signal to queued-request cancellation and native cooperative stopping. Keep the independent Windows stop helper; terminal shortcuts are insufficient when another app has focus.
32
+ 4. Maintain an explicit operation identifier spanning model turns. End indicators on settlement and shutdown; block further actions after emergency stop until a new user operation. Do not reset stop state merely at `turn_end`.
33
+ 5. Return compact model-readable evidence and typed metadata; use images only for screenshot payloads. Preserve `sent` versus `observed` verification.
34
+ 6. Treat settings-based tool denial as engine policy. Merely hiding a declaration is not an authorization mechanism.
35
+
36
+ ## Proposed implementation shape and milestones
37
+
38
+ ```text
39
+ Pi extension (tools, commands, status, lifecycle)
40
+ -> DesktopRuntime (execute(request, signal), dispose())
41
+ -> Windows worker transport -> UIA/native automation
42
+ -> independent emergency-stop helper
43
+ -> browser adapter (later milestone)
44
+ ```
45
+
46
+ `DesktopRuntime` should hide queueing, ownership, worker recovery, target validation, and evidence normalization. The Pi adapter should translate host events and results without implementing Windows behavior. Registering the existing MCP server is a useful baseline for comparison, but does not carry over DSH's adapter-level conversation ownership or operation tracking.
47
+
48
+ Suggested milestones:
49
+
50
+ 1. Establish the TypeScript package, worker protocol, ownership rules, and deterministic lifecycle/cancellation tests.
51
+ 2. Deliver Windows observation and guarded actions: windows, snapshots, screenshots/OCR, element actions, typing, and verification. Carry over applicable MIT notices when reusing code.
52
+ 3. Exercise real Windows fixtures, emergency stop, queue cancellation, worker timeouts, clipboard restoration, and multi-process ownership.
53
+ 4. Add dedicated browser automation and optional MCP transport after the desktop contract is stable.
54
+
55
+ ## Remaining validation
56
+
57
+ Recommend native Windows Pi as the first supported configuration. This workspace runs under WSL2 (verified from `/proc/sys/kernel/osrelease`) and exposes Windows `powershell.exe` through `/mnt/c/`; path conversion, process lifecycle, and desktop access remain unverified. Before implementation, define engine ownership and shutdown deadlines, and confirm the chosen image model's screenshot behavior. Test cancellation while queued, during real input, on reload, and after worker failure. No Pi settings or installed extensions were changed during this research.