* Extensions: first-class advertised-extensions map (Phase 1 of #1633)
Collapse the two ad-hoc `capabilities.extensions` spreads in the
InspectorClient constructor (EMA, gated on enterpriseManaged; the modern
Tasks extension, always-on) into a single builder that is the source of
truth for what the client advertises.
- core/mcp/extensions.ts: `ADVERTISABLE_EXTENSIONS` registry (id + label +
default-on flag), the `EMA_EXTENSION_KEY` constant, and
`buildClientExtensions()`. The registry is the shared list the Server
Settings toggle UI (Phase 2, #1739) will drive; EMA stays a conditional
built-in (auth-mode driven, not a free toggle).
- InspectorClientOptions.advertisedExtensions: per-extension overrides
(present key wins over the registry default) so a future UI can toggle
advertised extensions as a debugging knob.
- Behavior preserved: tasks defaults to advertised, so the map is non-empty
and rides every modern request envelope; EMA advertised iff enterpriseManaged.
The raw modern-task envelope re-stamp is left untouched (it fires only on
the raw tasks channel, which requires the extension anyway).
Tests: extensions.test.ts covers the registry and every builder branch
(defaults, EMA on/off, override enable/disable, unknown key) at 100%.
The tasks-era and EMA integration suites still assert the same wire
advertisement. `advertisedExtensions` is plumbed but not yet wired to the
form (that lands in Phase 2).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
* Extensions: address Phase 1 review (EMA-override test + envelope comment)
- extensions.test.ts: add an EMA-specific assertion that an override for
EMA_EXTENSION_KEY cannot advertise it (auth-mode only) — documents intent
and guards against EMA being added to ADVERTISABLE_EXTENSIONS by mistake.
- inspectorClient.ts: rewrite the withModernTaskEnvelope tasks-stamp comment
so it stays accurate once Phase 2 lets a user disable general tasks
advertisement — the force-stamp is load-bearing (the raw tasks/* channel
requires the extension), not a redundant re-add.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
* TUI: de-flake step-up OAuth frame assertions (raise poll ceiling)
The step-up authorize tests intermittently timed out on the final
"Step-up authorization succeeded" frame. Root cause is a test-infra
timing budget, not product code: waitForFrame/waitUntil polled only 25×
(25ms tick ≈ 625ms), and the step-up flow adds an extra async OAuth-runner
hop plus React commits that can exceed that under CI load with v8 coverage.
Hoist the poll count into a named POLL_TRIES = 100 (~2.5s ceiling). A poll
exits the instant its predicate is true, so a higher ceiling never slows a
passing assertion — it only widens the margin for the slow path. Verified
5× green (3 plain + 2 under --coverage), including the previously-flaky
step-up cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
* TUI: prettier-format the wrapped waitForFrame signature
The POLL_TRIES change pushed waitForFrame's signature past the print width;
Prettier wraps it. (Root has no aggregate format script — format is
per-client, so this was missed locally.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>