Replaces the bespoke batch-scoped process.exit interceptor with the
withExitGuard convention main established for extension/hook/plugin
loaders (500c39aa2): guard the module import and factory invocation so
a synchronous process.exit()/process.reallyExit() from a custom tool
becomes an ExtensionExitError handled as a recoverable load error,
while host exit paths stay untouched outside the guarded windows.
Tests cover the import-time exit (issue #1704 repro) and factory-time
exit; both would kill the test process without the guard.
Replaced the per-load process.exit replacement with a permanent interceptor that stays active for the entire loadCustomTools batch. The previous version restored process.exit as soon as a tool's import or factory promise resolved, so a tool that scheduled deferred async work during import (e.g. void main().catch(() => process.exit(1))) still killed the host when the deferred callback fired. The batch-scoped guard intercepts those microtask follow-ups too because the microtask drain at every await point happens before the surrounding batch promise resolves.
Added a regression test for the deferred-exit pattern alongside the existing synchronous-exit test.
Fixes#1704
Converted process.exit calls during custom tool module import or factory execution into recoverable load errors so a bad custom tool cannot terminate session startup.
Added regression coverage for a CLI-style custom tool that calls main() at import time and exits on failure.
Fixes#1704