You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Always send OBS exports to /observabilityService, including batch and per-request exports. Retain useS2SEndpoint for compatibility but ignore it, even when false; never fall back to /observability.
Add an app-only resolver overload for AgenticTokenCache.RefreshObservabilityToken. The legacy user-authorization overload now fails explicitly instead of acquiring a delegated OBS token.
Surface token acquisition failures, clear stale expiry metadata when refreshing opaque tokens, and document the migration. Workload MCP/Graph/OBO authentication is unchanged.
Compatibility
This changes OBS routing and the hosting cache's authentication contract. Callers must supply an app-only OBS token for the exporting agent and tenant; a delegated scp token cannot authenticate the S2S route. Endpoint selection does not mint or convert tokens.
Validation
93 targeted tests passed across the exporter, builder/configuration, hosting cache, and output middleware.
Runtime, observability, and hosting packages built in CJS and ESM; changed TypeScript passed ESLint.
Covered omitted/false/true legacy flags, domain overrides, per-request routing, no OBO fallback on 401/403/404, app-only cache callbacks, error propagation, and expiry.
Authenticated live AI Teammate/OBO ingestion remains unverified: a working provisioned agent identity and authentic OBO test assertion are still required. No live ingestion success is claimed.
Always route OBS to observabilityService, retain the legacy endpoint option as ignored compatibility state, and replace delegated hosting-cache exchange with an explicit app-only resolver.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
There are a couple of actionable review findings (type-only imports to avoid runtime dependencies and a brittle/slow cache-capacity test) that should be addressed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the observability (OBS) export pathing and hosting token cache contract to enforce S2S-only export routing and require app-only token acquisition, aligning exporter behavior, tests, and documentation with the new authentication model.
Changes:
Route all OBS exports (batch + per-request) to /observabilityService and deprecate/ignore useS2SEndpoint (even when false).
Change hosting AgenticTokenCache.RefreshObservabilityToken to require an app-only resolver; legacy TurnContext/Authorization overload now throws and token acquisition failures propagate.
Update docs/changelog and adjust tests to cover the new endpoint and token acquisition/expiry behaviors.
The reason will be displayed to describe this comment to others. Learn more.
Full first-pass panel review
Verdict: Needs work (high risk). The S2S-only route is a security-sensitive public authentication-contract change. Batch export and the hosting cache now have an app-only resolver path, but per-request export still ignores that resolver and forwards the existing OTel-context token to the new S2S endpoint. That leaves a supported mode unable to satisfy the contract and can drop all per-request telemetry with 401/403 responses.
Blocking
packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts:192 - Per-request export is routed to /observabilityService, but the token branch at lines 210-212 still always uses getExportToken(). ObservabilityBuilder.createPerRequestProcessor() also does not propagate a configured tokenResolver, and the changed test explicitly proves an arbitrary context token is forwarded. Existing AI Teammate/OBO callers therefore keep sending delegated tokens that the PR says S2S rejects. Wire an app-only resolver into per-request export (or introduce a distinctly contracted app-only context), update the public token-context migration guidance, and add a regression that distinguishes delegated from app-only acquisition.
Existing unresolved review items (not duplicated)
Copilot: use type-only imports in AgenticTokenCache.ts.
Copilot: avoid hard-coding and iterating through 10,001 cache entries in the eviction test.
Trade-off
Removing the delegated/OBO fallback is the correct security direction; the fix should preserve that invariant rather than restore the old route. The missing piece is an app-only token source for every export mode.
Persona roll-up
Security: Blocking token-source/trust-boundary mismatch in per-request mode; no secret exposure or authorization fallback added elsewhere.
Privacy: No new collection, retention, or tenant-mixing path; the loss-of-user-attribution caveat is documented.
Performance: Retries and cache lifetime remain bounded; the expensive eviction test is already covered by an existing thread.
Customer service: The changelog describes the break, but per-request consumers lack a working migration path.
Business / COGS: No material storage, egress, cardinality, or provisioning increase.
Senior engineer: The changed test verifies routing but not the new authentication invariant, allowing a production telemetry outage to pass.
Architect: Batch/cache and per-request modes now implement different credential contracts behind one public exporter API.
Feedback ledger
No repository-specific ledger exists yet. The two existing Copilot findings were suppressed from new inline comments to avoid duplication.
Approval gate
Not approved: one blocking finding remains, two prior review threads are unresolved, and the branch is behind main. All exact-head CI checks currently pass, but green CI does not exercise the delegated-token counterfactual described above.
Use the configured OBS resolver for batch and per-request export without ambient-token or OBO-route fallback. Fail missing-token exports explicitly, add delegated-context counterfactuals and builder integration coverage, declare the tooling axios dependency, and update migration guidance and cache tests.
Review response amendments:
- Startup error now names the fix ("Per-request export now requires withTokenResolver(...)") and points at AgenticTokenCache for caching.
- README and CHANGELOG document that resolvers must cache; the exporter invokes the resolver on every batch and per identity group.
- Defensive resolver guard in exportGroup now comments that it only catches post-construction mutation; the constructor is the primary check.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5cbf5f6b-cc40-4b7e-a591-65848db73a12
Bring in main's dependency security overrides. Align the new tooling axios dependency with main's axios override (^1.16.0, resolved 1.20.0) and record it with the override specifier in pnpm-lock.yaml, matching how main records hono.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5cbf5f6b-cc40-4b7e-a591-65848db73a12
Per-request export now uses the configured app-only tokenResolver, the same as batch export. Agent365Exporter no longer reads getExportToken(), so a token in runWithExportToken is never sent to OBS.
An enabled exporter without a resolver fails at configuration, and the error names the fix (withTokenResolver(...)). Empty tokens or failed acquisition fail the export without sending a request. There is no OBO fallback.
AgenticTokenCache uses type-only imports; the eviction test derives the cache capacity.
@microsoft/agents-a365-tooling now declares axios, which it imports directly. After merging main, the catalog range matches main's axios security override (^1.16.0).
The README, CHANGELOG and hosting design doc describe the per-request migration and note that resolvers should cache tokens, because the exporter calls them for each export batch.
Breaking change: per-request users who relied on runWithExportToken must configure withTokenResolver(...). See the README section Migrating per-request authentication.
Validation
Merged tree: all packages build (CJS and ESM), lint is clean, and the full unit suite passes (65 suites, 1,314 tests).
New regressions: a delegated JWT in the request context is never exported, while the resolver's roleless app token is. They run through the real builder, per-request processor and exporter, and also cover concurrent identities, resolver precedence, and missing or failed acquisition.
Live: an earlier head of this PR exported real agent telemetry through the S2S OTLP route using a roleless app-only token (HTTP 200, downstream processing confirmed). The per-request changes in this update are covered by offline tests only.
The reason will be displayed to describe this comment to others. Learn more.
Delta re-review (93f1934 -> 65ffbcc)
Verdict: Needs work (high-risk authentication contract). The original blocking per-request credential defect is fixed at the new head, and I found no additional issues beyond the two existing Copilot threads about the public exporterOptions path.
Author-claimed fixes
Accepted - per-request app-only resolver:ObservabilityBuilder.createPerRequestProcessor() now uses the same createExporterOptions() path as batch export, and Agent365Exporter.exportGroup() obtains credentials only from options.tokenResolver. The new real builder/processor/exporter tests distinguish a delegated context token from the resolver token and cover concurrent identities and resolver precedence.
Accepted - fail closed without a resolver: the exporter constructor rejects a missing resolver; empty results and acquisition failures fail before fetch; 401/403/404 remain on /observabilityService without an OBO fallback. Exact-head tests cover each branch.
Accepted - hosting cache fixes:AgenticTokenCache now uses type-only hosting imports, clears stale expiry/acquisition metadata after failures or opaque-token refreshes, and the eviction test derives _maxCacheSize while reusing one resolver.
Accepted - tooling dependency:@microsoft/agents-a365-tooling now declares its direct axios dependency through the workspace catalog and has a manifest regression test.
Partially accepted - migration documentation: the README, changelog, and hosting design now explain the breaking per-request migration and resolver caching. However, the README also presents exporterOptions.tokenResolver as equivalent without qualifying that it only works through ObservabilityBuilder.withExporterOptions(...). ObservabilityManager.start(options) accepts BuilderOptions but does not forward options.exporterOptions; the exact-head regression tests exercise the builder directly, not this public convenience API.
Accepted - validation evidence: all exact-head GitHub checks are complete and green, including Node.js 18/20, CodeQL, and JavaScript/TypeScript analysis. The reported live S2S success was on an earlier head; the PR correctly limits the new per-request claim to offline coverage.
Existing approval blockers
The source-valid ObservabilityManager.start(options) / exporterOptions.tokenResolver gap remains represented by the README thread and the design-doc thread. These are one underlying public API/migration issue and are not duplicated here. Either forward exporterOptions in ObservabilityManager.start with a regression test, or scope the documentation to the working withExporterOptions({ tokenResolver }) builder path.
The prior panel blocker was source-verified as fixed in 78deb5d and its thread has been resolved. The type-import and cache-capacity findings are also source-verified and resolved. No new inline findings were added.
Persona roll-up
Security: app-only credential selection is now consistent across batch and per-request modes; no delegated fallback or new secret exposure found.
Privacy: no new data collection, retention, residency, or tenant-mixing issue found.
Performance / COGS: resolver calls are explicitly documented as cache-required; no new unbounded runtime work or material cost issue found.
Customer service: the remaining public-options ambiguity can send a documented migration path to a startup exception.
Senior engineer / Architect: implementation and focused regressions close the original mode-skew defect; the public convenience API is still inconsistent with its accepted options type.
Approval gate: not approved because two source-valid review threads remain unresolved. All substantive CI checks are green; merge remains subject to branch protection.
ObservabilityManager.start(options) accepted BuilderOptions.exporterOptions
but never passed it to the builder, so the documented
`exporterOptions.tokenResolver` migration path failed at startup with the
now-required app-only resolver. Forward it through withExporterOptions; a
top-level tokenResolver still takes precedence.
Regression tests exercise the public start() path through the real
processor and exporter and fail without the fix.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5cbf5f6b-cc40-4b7e-a591-65848db73a12
Regenerate lockfile to preserve axios catalog mapping
pnpm-lock.yaml:500
The package manifest uses axios: catalog:, but this importer records the dependency as the literal ^1.16.0, and the lockfile has no corresponding axios entry under catalogs.default. Regenerate pnpm-lock.yaml with pnpm so the catalog mapping and importer stay consistent; otherwise frozen installs can report the lockfile as out of date or resolve this dependency differently.
State in the README, design guide, and changelog that
ObservabilityManager.start(options) forwards exporterOptions (including
exporterOptions.tokenResolver), and that tokenResolver/withTokenResolver
takes precedence.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5cbf5f6b-cc40-4b7e-a591-65848db73a12
6464933: ObservabilityManager.start(options) now forwards options.exporterOptions, so start({ exporterOptions: { tokenResolver } }) works. A top-level tokenResolver still takes precedence. The regression tests go through the public start() path with the real processor and exporter, and fail without the fix.
dd4a8be (docs only): the README, design guide, and CHANGELOG now say that start(options) forwards exporterOptions.
Validation on 6464933: pnpm build, pnpm lint, and all 1316 unit tests pass locally.
Jason-R-Lien, both threads are resolved; ready for another look.
The reason will be displayed to describe this comment to others. Learn more.
Delta re-review (65ffbcc -> dd4a8be)
Verdict: re-review complete with no new source-valid findings. The requested ObservabilityManager.start(options) fix is present and covered, but I am leaving a COMMENT rather than approving because two later Copilot threads remain unresolved. Both late findings are source-rebutted below; thread resolution is the only remaining approval-gate condition.
Author-claimed fixes
Accepted - exporterOptions forwarding:ObservabilityManager.start() now passes options.exporterOptions to withExporterOptions(). ObservabilityBuilder.createExporterOptions() merges those values and then applies the top-level tokenResolver, preserving the documented precedence.
Accepted - public-path regression coverage:observabilityManager-exporter-options.test.ts exercises the real public start() path through the processor/exporter, proves exporterOptions.tokenResolver exports successfully, and proves a top-level resolver wins.
Accepted - documentation: README, design guide, and CHANGELOG now accurately state that start(options) forwards exporterOptions.
Accepted - validation: every exact-head check is complete and green, including Node.js 18/20, CodeQL, and JavaScript/TypeScript analysis.
Late review reconciliation
Rejected - lockfile is unsynchronized:pnpm-lock.yaml records the workspace catalog's Axios range as ^1.16.0 because the same package is pinned by the root override; the existing overridden hono catalog entry has the same resolved-specifier shape. Exact-head CI's pnpm i succeeds on both Node versions before build/test/pack.
Rejected - Jest 30 typing does not compile: this test uses the repository's global jest type from @types/jest, and the exact file compiles and passes under the configured ts-jest diagnostics on both Node 18 and 20. The one-generic @jest/globals signature cited by the thread is not the binding used here.
Approval gate
No blocking or should-fix issue remains in the code, docs, tests, security/privacy boundary, performance path, or package contract. Approval is withheld solely because the lockfile thread and the Jest typing thread are still unresolved; the gate requires every live review thread to be resolved even when its technical claim is rebutted. Merge remains subject to branch protection.
Jason-R-Lien, thanks for the re-review. I replied to the two late Copilot threads with the same evidence (root pnpm.overrides pins axios, like hono; the file uses the @types/jest two-generic jest.fn) and resolved them. No live threads remain on dd4a8be.
The reason will be displayed to describe this comment to others. Learn more.
Same-head re-review after author response
Approved. The author resolved the two remaining threads, and the technical rebuttals are accepted from exact-head source and CI evidence.
Author-claimed fixes and rebuttals
Accepted - per-request app-only authentication:ObservabilityBuilder.createPerRequestProcessor() shares createExporterOptions() with batch mode, and Agent365Exporter uses only the configured resolver. Regressions distinguish delegated context credentials from the app-only resolver token and cover resolver precedence.
Accepted - fail-closed behavior and hosting cache corrections: missing/empty/failed resolver paths stop before export; no OBO fallback was restored; hosting imports are type-only and cache expiry/eviction behavior is covered.
Accepted - public start(options) path:ObservabilityManager.start() forwards exporterOptions; public-path tests prove exporterOptions.tokenResolver works and a top-level resolver wins. README, design, and changelog match that behavior.
Accepted - Axios lockfile rebuttal: the root override pins Axios to ^1.16.0, so the importer records the effective override range; overridden hono entries use the same shape. Exact-head Node 18/20 jobs install successfully.
Accepted - Jest typing rebuttal: this test uses the global jest declaration supplied by @types/jest, whose two-generic fn<Return, Args>() form is valid here. The configured non-isolated ts-jest compilation and test suite pass on Node 18 and 20.
All seven review threads are resolved. Every exact-head substantive check is complete and successful, including Node.js 18/20, CodeQL, and JavaScript/TypeScript analysis. No blocking or should-fix finding remains across security, privacy, correctness, performance, customer impact, cost, or architecture. Merge remains subject to branch protection.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/observabilityService, including batch and per-request exports. RetainuseS2SEndpointfor compatibility but ignore it, even when false; never fall back to/observability.AgenticTokenCache.RefreshObservabilityToken. The legacy user-authorization overload now fails explicitly instead of acquiring a delegated OBS token.Compatibility
This changes OBS routing and the hosting cache's authentication contract. Callers must supply an app-only OBS token for the exporting agent and tenant; a delegated
scptoken cannot authenticate the S2S route. Endpoint selection does not mint or convert tokens.Validation