6ad935d093
Review on #8052 found three problems in the write/delete fallback seam. A symlink guard that only `lstat`'d the final component let the same escape through a symlinked ancestor: `ws/link/file` under a `ws/link -> /outside` link reached a handler as a lexically innocent path, so a helper's prefix allowlist passed while the bytes landed outside. Refusing every symlinked component is not available, since `/var` and `/tmp` are links on macOS and every path under `os.tmpdir()` traverses one. So `req.dst` is now the path the failed syscall itself acted on, via `resolveSyscallTarget` beside `confineToWorkspace`: fully resolved for a write, resolved up to the last component for a delete, because `unlink` removes a link rather than following it. Resolving also closes the TOCTOU window a refusal left open. A path that cannot be canonicalized — a dangling final link, or an ancestor whose own resolution is denied — is not brokered at all. Per-handler throw isolation lived outside the per-extension trampoline, so a throw from one extension's first handler advanced the registry to the next extension and skipped every later handler that one had registered. Each handler call is now wrapped individually. The registry stays process-wide. A subagent spawned with restricted tools gets `preloadedExtensionPaths: []` and loads no extensions of its own, so scoping resolution to the originating session would turn its brokered writes into hard failures, and a host that registers once in its top-level session expects its subagents covered. The request names its origin instead: `req.sessionId` against the handler's own `ctx.sessionManager.getSessionId()`, entered by `ExtensionToolWrapper`, which `sdk.ts` already puts around the whole tool registry whenever a runner exists. Both registries are also walked over a snapshot, so a concurrent session shutdown cannot make another session's walk skip whichever handler shifted into the hole. Unit tests go 30 -> 35 and integration 6 -> 7, covering the resolved target, a symlinked ancestor on both seams, a dangling link, a target whose own metadata is behind the boundary, same-extension handler ordering after a throw, and `req.sessionId` matching the handler's own session end to end.