Skip to content

fix(desktop): snapshot window lookup survives endpoint security agents - #21749

Closed
ekrako wants to merge 3 commits into
pingdotgg:mainfrom
ekrako:fix/mac-window-lookup-stdin
Closed

ekrako wants to merge 3 commits into
pingdotgg:mainfrom
ekrako:fix/mac-window-lookup-stdin

Conversation

@ekrako

@ekrako ekrako commented Oct 19, 2026 •

Copy link
Copy Markdown

Problem

On a Mac running an endpoint security agent (SentinelOne and Kandji ESF here), SnapShot setup fails at Test capture and continue with Could not capture a test snapshot. Finding the front window failed. while Screen Recording and Accessibility both read Allowed. The window lookup runs osascript -l JavaScript -e <script>, and the agent SIGKILLs osascript when that ObjC-bridge script arrives inline. Node reports code: null, signal: "SIGKILL".

Fixes https://git.cafe/pingdotgg/t3code/issues/21747. Possibly also the real cause behind https://git.cafe/pingdotgg/t3code/issues/18595, whose report has no exit status to confirm it.

Change

runMacLookup() passes -l JavaScript - and writes the same script to osascript's stdin. Timeout, buffer limit and error mapping are unchanged. A stdin write error is ignored on purpose: it only happens when osascript already exited, and the execFile callback reports that exit.

The modifier-pair poller in MacModifierPairShortcutProcess.ts also uses -e, but the agent does not kill it in any mode, so it is left alone.

Scope and approval

One small fix for an obvious bug: the capture is killed before it runs, and the change only moves the script from argv to stdin. Bug report with the measurements: https://git.cafe/pingdotgg/t3code/issues/21747.

Verification

Same script, run through T3 Code's own Electron binary (ELECTRON_RUN_AS_NODE=1) on the affected Mac:

How the script is passed Result
osascript -l JavaScript -e <script> {"signal":"SIGKILL","ms":14}
osascript -l JavaScript -, script on stdin frontmost window JSON
osascript -l JavaScript <file> frontmost window JSON

ActiveWindow.test.ts extends main's existing tests:

  • "feeds the macOS lookup script to osascript over stdin and parses the frontmost window" checks the args are exactly ["-l", "JavaScript", "-"] and stdin.end is called once with the lookup script.
  • "reports the process failure when osascript exits before reading stdin" (added by maria-rcks) checks that a broken stdin pipe does not replace the real exit error.
$ vp test run src/snapShot/ActiveWindow.test.ts   (apps/desktop)
 Test Files  1 passed (1)
      Tests  17 passed (17)

vp run check:changed passes: format, lint, knip, typecheck, tests.

effect-tsgo tsrs --noEmit in apps/desktop (tsrs 0.21.0) exits 0. It prints only schemaClassMissingBrand suggestions, and none are in the changed files.

End-to-end on the affected Mac (comment): the shipped Nightly built from main fails Test capture. A desktop build of this branch passes it, then captures and runs a real provider turn.

Implemented with Claude Opus 5.5 (Claude Code harness) in T3 Code.

Fixes #21747

@github-actions github-actions Bot added vouch:unvouched Author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 19, 2026
@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch from 79ea2ce to 554e854 Compare October 19, 2026 14:46
@ekrako
ekrako marked this pull request as ready for review October 19, 2026 14:46
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • Resume reviews
  • Trigger review

No actionable comments were generated in the recent review.

Walkthrough

The macOS window lookup now sends its JavaScript source to osascript through stdin instead of an inline -e argument. Tests verify script delivery and process failure handling.

Changes: macOS Snapshot lookup

Layer / File(s) Summary
Run and test the lookup through stdin
apps/desktop/src/snapShot/ActiveWindow.ts, apps/desktop/src/snapShot/ActiveWindow.test.ts
runMacLookup passes the script through stdin and ignores stdin write errors. Tests verify the command arguments, script delivery, stdin termination, and rejection when osascript exits after a stdin error.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related PRs

  • pingdotgg/t3code#22624: Both PRs change the macOS ActiveWindow lookup. This PR changes script transport; PR #22624 changes JXA results and window selection.
  • pingdotgg/t3code#24572: Both PRs change the same lookup implementation. This PR changes script transport; PR #24572 changes window-owner matching for invalid PIDs.

Suggested reviewers: t3dotgg

Merge Risk: Minimal · previous review b1098

The lookup preserves process-error propagation, timeout, and buffer handling; native macOS validation remains advisable but is not an established merge blocker.

Security architecture risk: Low · up to 554e8

The lookup still executes the same fixed script with no added privileges or caller-supplied code. The change is narrow, but its interaction with managed-device security policies has not been independently verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed execution path remains bounded to desktop macOS window metadata lookup. Successful execution can expose the same window title, bounds, application identity, process identifier, and application path as before; the inspected change adds no new data destination.

Trust Boundaries and Controls

  • observed — The child executable remains the absolute path /usr/bin/osascript, invoked through execFile without a shell. Script text is a fixed constant rather than caller input; window metadata is collected as data and JSON-encoded, not interpolated into executable source.

Resilience and Maintainability Implications

  • inferred — Each lookup owns its child process and stdin, with no shared mutable or persistent state introduced. Repetition and concurrency do not introduce shared-state ordering requirements in this helper; process failure remains bounded by the existing callback and timeout rather than requiring rollback.
Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: making desktop snapshot window lookup survive endpoint security agents. It follows the repository's conventional commit style.
Linked Issues check Passed Issue [#21747] requires the macOS window lookup to avoid the inline -e script that endpoint security agents kill. Whole-PR diff (merge base to head) for apps/desktop/src/snapShot/ActiveWindow.ts s…
Out of Scope Changes check Passed The whole-PR diff for the inspected paths touches only apps/desktop/src/snapShot/ActiveWindow.ts and apps/desktop/src/snapShot/ActiveWindow.test.ts. The ActiveWindow.ts change is the stdin trans…
Description check Passed The description includes all required sections. It explains the problem and expected behavior, describes the change and scope, and gives focused test and manual verification results.
Full details: Linked Issues check

Explanation

Issue [#21747] requires the macOS window lookup to avoid the inline -e script that endpoint security agents kill. Whole-PR diff (merge base to head) for apps/desktop/src/snapShot/ActiveWindow.ts shows runMacLookup() now calls /usr/bin/osascript with ["-l", "JavaScript", "-"] and writes MAC_LOOKUP_SCRIPT through child.stdin?.end(...). The -e argument is removed from this path. Timeout (MAC_LOOKUP_TIMEOUT_MS), maxBuffer (1 MiB) and the error and resolve callback are unchanged. Since the previous review, the head adds child.stdin?.on("error", () =&gt; undefined), so a broken-pipe write does not raise an unhandled error. The execFile callback still reports the process exit. ActiveWindow.test.ts asserts the exact arguments ["-l", "JavaScript", "-"], asserts one stdin.end call containing CGWindowListCopyWindowInfo, and adds a case where stdin emits EPIPE and the process failure is rejected. These tests cover the stdin requirement. The modifier-pair poller is not part of this requirement and is unchanged. Limit: the native behavior (SIGKILL avoidance on a managed Mac in a packaged build) is not verifiable from repository tests. The reported measurements in the PR description support it but are not reproduced here. Coding requirement is met at code and unit-test level.

Full details: Out of Scope Changes check

Explanation

The whole-PR diff for the inspected paths touches only apps/desktop/src/snapShot/ActiveWindow.ts and apps/desktop/src/snapShot/ActiveWindow.test.ts. The ActiveWindow.ts change is the stdin transport for the macOS lookup, the stdin error handler, and a code comment. Each directly implements [#21747]. The ActiveWindow.test.ts changes are supporting test changes: the stdin argument and write assertions, the stdin fake in stubMacLookup, the EPIPE failure-path test, and a vi.mock of ../electron/WindowsForeground.ts. The WindowsForeground mock is not tied to the lookup change. Its purpose is unclear from the diff alone. It is test setup only and does not change runtime behavior, so it is not a demonstrated unrelated change. The raw summary lists no other changed files. The file list was truncated, so the full set of changed paths was not confirmed.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between c4f2ec3 and b1098a9.

Files selected for processing: 2

  • apps/desktop/src/snapShot/ActiveWindow.test.ts
  • apps/desktop/src/snapShot/ActiveWindow.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch 3 times, most recently from de9fa72 to c4f2ec3 Compare October 25, 2026 10:41

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

kept @ekrako's production fix unchanged and preserved the contributor commit. merged current main without rebasing. added one test in the existing file proving that a broken stdin pipe does not replace the actual process failure. the final diff still touches only ActiveWindow.ts and ActiveWindow.test.ts.

verification at 03451954f90a7285928e00012cf81118293de64e:

  • the original regression test fails against main's inline invocation.
  • blacksmith vp run check:changed passes: format, lint, knip, affected typechecks, and both lookup tests.
  • 86 nearby tests pass in DesktopSnapShot.test.ts and snapShot.test.ts.
  • two independent gpt-6.1-sol reviews found no code issues on this head.

unverified: packaged macos "test capture and continue" and endpoint-security behavior. this linux vm cannot run the native path, so no native capture evidence is claimed. @ekrako, a packaged capture on the affected managed mac is the remaining verification. coderabbit is rate limited on this push; its green status is not a fresh review.

ci is green on this head, including build/typecheck, lint, tests, all eight server shards, and the aggregate check. fork workflow approval is complete. no unresolved review findings. handoff is waiting only for native packaged capture verification on an affected mac before maria's review.

@juliusmarminge juliusmarminge added bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed labels Oct 27, 2026

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

no changed behavior could be verified on this linux vm. this is an unverifiable result, not a successful runtime verification.

read the description, discussion, full diff and commit authors. the pr is open, with no commits by juliusmarminge or t3dotgg. installed github/main (570d4b03ee) in a separate /home/user/main worktree with vp i, then checked out exact head 03451954f90a7285928e00012cf81118293de64e and ran vp i successfully.

confirmed the host reports platform=linux; a real attempt to execute /usr/bin/osascript returns ENOENT. the user path is snapshot setup's "Test capture and continue" -> desktop bridge setupSnapShot("test-mac-capture") -> desktop setup -> macos active-window lookup. desktop setup explicitly rejects this action outside macos. the pr changes only that macos lookup and its tests.

command transcript and verification limits

Not verified

original endpoint-security failure on main; successful capture on the pr; packaged macos desktop integration; sentinelone/kandji behavior; screen recording/accessibility permissions; shortcut capture, cancellation and lookup-error handling; captured-image delivery to a real codex gpt-6.1-sol turn. windows and native mobile paths were not exercised.

a linux dev web server and provider turn cannot execute this macos electron path, so neither was started. no screenshots or recording claim native success. no mocks or platform overrides were used. ci reports 28 successful checks, but that does not establish this fix works.

remaining verification requires the desktop app on an affected managed mac, comparing main and this exact head through "Test capture and continue", then normal shortcut capture and a real provider turn. no code or pr metadata was changed.

@ekrako

ekrako commented Nov 1, 2026

Copy link
Copy Markdown
Author

main in the shipped app

Installed T3 Code Nightly 0.1.7-nightly.20261101.4016 on the affected Mac. Checked its app.asar: it calls execFile("/usr/bin/osascript", ["-l", "JavaScript", "-e", MAC_LOOKUP_SCRIPT]), matching main's code.

In Settings > Snapshots > Set up snapshots, Screen Recording and Accessibility both show Allowed. System Settings > Screen & System Audio Recording also has T3 Code (Nightly) enabled. Still, "Test capture and continue" fails with:

DesktopSnapShotSetupError: Could not capture a test snapshot. Finding the front window failed.

PR head in a desktop build

On the same Mac, ran vp run dev:desktop from exact PR head b1098a9176. Checked that the built dist-electron/main.cjs contains child.stdin?.end(MAC_LOOKUP_SCRIPT). Granted Screen Recording and Accessibility to T3 Code (Dev).

Results from the dev app's desktop.trace.ndjson and state DB:

  • Setup: "Test capture and continue" succeeded both times: two successful desktop.snapShot.setup spans, 705 ms and 649 ms. No "Finding the front window failed." error.
  • Shortcut capture: all 8 desktop.snapShot.capture spans and all 5 desktop.snapShot.persistCapture spans reported Success.
  • Provider turn: attached window-2026-11-01T07-59-59.916Z.png to a new thread with no text and sent it to Codex (gpt-6-astra). The turn completed in about 5 s. The reply correctly described the captured window as T3 Code's DevTools on the Elements tab.

Function-level comparison

Ran the lookup comparison on the same Mac at 2026-11-01 07:23 UTC: macOS 27.0.1, arm64, with SentinelOne (sentineld) running.

Used the real apps/desktop/src/snapShot/ActiveWindow.ts and apps/desktop/src/electron/WindowsForeground.ts, unmodified via git show, from main ce9e016406 and exact PR head b1098a9176, with the repo's pinned [email protected]. Diff checked: the only difference between the two ActiveWindow.ts files is this PR's runMacLookup change. Called the exported activeWindow("darwin") from each.

Runtime main PR head
T3 Code Nightly's own Electron 44.10.0 / Node 24.22.1 (ELECTRON_RUN_AS_NODE=1) failed 3/3 succeeded 3/3
Plain Node 26.12.1 failed 3/3 succeeded 3/3

That exact lookup step failed on main 6/6 after 3–13 ms with code: null, signal: "SIGKILL" and Command failed: /usr/bin/osascript -l JavaScript -e .... PR head succeeded 6/6 in 103–393 ms, returning the frontmost Arc window's owner, id and title. Titles are redacted below.

electron main: {"ok":false,"ms":13,"code":null,"signal":"SIGKILL"}
electron pr:   {"ok":true,"ms":301,"owner":"Arc","id":18836,"title":"<redacted>"}
node     main: {"ok":false,"ms":3,"code":null,"signal":"SIGKILL"}
node     pr:   {"ok":true,"ms":103,"owner":"Arc","id":18836,"title":"<redacted>"}

Each case was run 3x with the same outcome; the transcript shows one run per case.

Not verified: Kandji isolated from SentinelOne (both installed, as in #21747), or a packaged/signed build of the PR head (dev build only). Windows and mobile are not affected by this change.

@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch 3 times, most recently from 27b421f to 8e0fbaa Compare November 9, 2026 07:33
@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch from 8e0fbaa to c3409f5 Compare November 9, 2026 09:23
@ekrako

ekrako commented Nov 9, 2026

Copy link
Copy Markdown
Author

Scope

Verified on the same affected Mac, whose endpoint security agent SIGKILLs osascript scripts passed inline with -e.

Tested 8e0fbaaa33. After the re-push, git range-diff confirms both PR commits are patch-identical to current head c3409f5e12; no diff under apps/desktop.

Dev build

Ran vp run dev:desktop. Built dist-electron/main.cjs contains child.stdin?.end(MAC_LOOKUP_SCRIPT).

  • "Test capture and continue": desktop.snapShot.setup succeeded (~1.0 s).
  • Both-Shift: 5 captures; all shortcutActivated, prepareCapture, persistCapture, and capture spans succeeded.
  • Discard: removed from composer, acknowledged and dismissed; never sent.
  • Codex gpt-6.1-sol: "Describe this screenshot in one sentence." with the captured window image attached (495,574 bytes). Completed in ~3 s; correctly described the iPhone Mirroring home screen.

Packaged build

Built unsigned with scripts/build-desktop-artifact.ts --platform mac --target dir --arch arm64. The dmg target was skipped after GitHub returned 504 for the dmgbuild download.

app.asar calls execFile("/usr/bin/osascript", ["-l", "JavaScript", "-"]) and writes the script to stdin. Ran an ad-hoc-signed copy with its own bundle ID and isolated home to preserve installed Nightly permissions; app.asar was byte-identical to build output.

  • Setup succeeded (612 ms), including the permissions request.
  • 2 shortcut captures; all spans succeeded.
  • Discard acknowledged; excluded from sent message.
  • Codex gpt-6.1-sol, same prompt, 490,538-byte image: accurate reply in ~4 s.

Results and limits

Both runs: no failed snapshot spans or osascript, SIGKILL, or "Finding the front window failed" errors in desktop.trace.ndjson.

Unverified: signed/notarized build, dmg, and cancel path. macOS captures directly; region selection and Escape-to-cancel exist only for Linux Wayland portal capture.

@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch from c3409f5 to 643314c Compare November 9, 2026 09:38
@juliusmarminge

Copy link
Copy Markdown
Member

The automated pipeline stopped on this pull request and will not merge it:

the proof has no capture anyone can open:

  • Permanent environment blocker: the affected managed macOS environment is unavailable, and this task prohibits accessing another machine.
  • Base failure was not observed; failsOnBase=false means unverified, not that the base passed.
  • Head success was not observed; passesOnHead=false means unverified, not that the fix failed.
  • Screen Recording and Accessibility grants, test-capture setup continuation, and dev and packaged desktop captures remain unverified.
  • No captures or driver scripts were produced, so ghdrop upload was not attempted and no public evidence URLs are available.

It stays open for a person to finish or close.

@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch from 2c3d833 to bbba443 Compare November 11, 2026 07:38
@ekrako

ekrako commented Nov 11, 2026 •

Copy link
Copy Markdown
Author

Verified base failure and head success on a managed Mac whose endpoint security agent kills osascript -e; the fix passes the JXA lookup script on stdin with osascript -l JavaScript -.

Base (fails)

T3 Code (Nightly) 0.1.9-nightly.20261110.4465, built from main before the fix. In Settings > Snapshots > Set up, Screen Recording and Accessibility both show Allowed. "Test capture and continue" fails:

DesktopSnapShotSetupError: Could not capture a test snapshot. Finding the front window failed.

1-base-nightly-fails

Head, dev build (passes)

bbba443, vp run dev:desktop, empty database. Both grants show Allowed.

2-head-dev-grants

"Test capture and continue" succeeds and advances to step 2, Shortcut ("Shortcut saved"). Trace span desktop.snapShot.setup exits Success in 629 ms.

3-head-dev-continued

Head, packaged build (passes)

bbba443, built with node scripts/build-desktop-artifact.ts --platform mac --target dir --arch arm64, ad-hoc signed under a separate bundle ID with isolated HOME. Both grants show Allowed.

4-head-packaged-grants

The same continuation succeeds; desktop.snapShot.setup exits Success in 938 ms. The packaged app.asar contains the stdin change.

5-head-packaged-continued

Dev and packaged screenshots come from the app's renderer; base is a window crop. My earlier PR comment covers a full both-Shift capture sent to an agent on both builds.

@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch 4 times, most recently from e81b0aa to e5d3b63 Compare November 12, 2026 07:27
ekrako and others added 2 commits November 12, 2026 10:42
On Macs running endpoint security agents (e.g. SentinelOne, Kandji ESF),
snapshot "Test capture" failed with "Finding the front window failed" even
with Screen Recording and Accessibility granted: the agent SIGKILLs
osascript when the ObjC-bridge JXA lookup script is passed inline via `-e`.

Run osascript with `-l JavaScript -` and write the script to its stdin,
which the same agents allow. The modifier-pair poller is unaffected and
keeps using `-e`.
…p test

AI-assisted; verified with the tests and tools listed above.
@ekrako
ekrako force-pushed the fix/mac-window-lookup-stdin branch from e5d3b63 to 4f6b291 Compare November 12, 2026 08:42
@t3dotgg

t3dotgg commented Nov 12, 2026

Copy link
Copy Markdown
Member

Note

🤖 Claude Opus 5.5 responding on behalf of Theo

Closing. This PR has been open for more than 3 weeks, and main has moved a long way since then. If the change still matters, open a fresh PR from current main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed size:XS 0-9 changed lines (additions + deletions). vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: macOS SnapShot test capture fails when endpoint security kills inline osascript

4 participants