Skip to content

[WIP] feat: add agent inspector into azd cli#8125

Draft
XiaofuHuang wants to merge 7 commits intoAzure:mainfrom
XiaofuHuang:xiaofua/azd-inspector-pr-review
Draft

[WIP] feat: add agent inspector into azd cli#8125
XiaofuHuang wants to merge 7 commits intoAzure:mainfrom
XiaofuHuang:xiaofua/azd-inspector-pr-review

Conversation

@XiaofuHuang
Copy link
Copy Markdown

Spec: https://github.com/coreai-microsoft/foundrysdk_specs/pull/169

This pull request introduces the Agent Inspector UI to the Azure AI Agents extension, allowing users to launch a browser-based inspector for local agents without any Python or Node.js runtime dependencies. The main changes add a new --inspector mode to the azd ai agent invoke command, which serves a standalone Single Page Application (SPA) built from the VS Code extension and hosts it with an embedded Go HTTP/WebSocket server. This enables local agent inspection and debugging in a user-friendly way.

The most important changes are:

Agent Inspector UI integration:

  • Adds a new --inspector flag (with --inspector-port) to the azd ai agent invoke command. When enabled (and combined with --local), this launches a browser-based inspector UI connected to the local agent, instead of streaming responses to the terminal. Input validation ensures this flag is only used in supported scenarios. (invoke.go, invoke_inspector.go) [1] [2] [3] [4] [5] [6]
  • Implements the inspector backend in Go, embedding the SPA assets and serving them via an in-process HTTP server. The backend reimplements the Python rpc_handler.py logic, supporting the required WebSocket JSON-RPC protocol for the UI. (internal/inspector/assets.go, internal/inspector/proxy_fetch.go) [1] [2]
  • Includes the frontend SPA assets (HTML, JS, CSS, highlight.js themes) in the repository and sets up a PowerShell script to build and stage these assets for embedding. (internal/inspector/assets/assets/*, internal/inspector/assets/index.html, build-frontend.ps1) [1] [2] [3] [4]

Dependency and build system updates:

  • Updates go.mod to add direct dependencies on github.com/cli/browser (for browser launching) and github.com/gorilla/websocket (for WebSocket support). (go.mod) [1] [2]
  • Documents the build and embedding process for the inspector SPA, clarifying how to integrate the UI into the extension binary. (build-frontend.ps1, comments in code) [1] [2]

Testing and validation:

  • Adds unit tests for localhost assertion logic, ensuring the inspector only proxies requests to local endpoints for security. (internal/inspector/localhost_test.go)

Overall, these changes provide a seamless, cross-platform way to inspect and debug local agents via a modern browser UI, improving developer experience and removing the need for extra runtime dependencies.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the customer-reported identify a customer issue label May 11, 2026
@microsoft-github-policy-service
Copy link
Copy Markdown
Contributor

Thank you for your contribution @XiaofuHuang! We will review the pull request and get back to you soon.

Adds a `--inspector` flag to `azd ai agent invoke` that launches the
Agent Inspector SPA in the browser and proxies localhost HTTP/SSE/RPC
to the agent running via `azd ai agent run --local`. The SPA is the
same one shipped by the VS Code extension built in `--mode azd`; its
assets are checked in under `internal/inspector/assets/` and embedded
via `go:embed`.

Includes table-driven tests for `assertLocalhost` and a server smoke
test that binds an ephemeral port and verifies SPA fallback.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
@XiaofuHuang XiaofuHuang force-pushed the xiaofua/azd-inspector-pr-review branch from 22ae049 to e95eb9d Compare May 11, 2026 07:17
XiaofuHuang and others added 6 commits May 11, 2026 15:38
- Drop assertLocalhost and its test; remove dead compile-time check.
- Extract validateInspectorFlags; add DefaultInspectorPort constant.
- Server.Start signals readiness so browser launches after bind.
- sendError uses err.Error() and JSON-RPC -32603; log unhandled methods.
- SSE loop uses bufio.Reader.ReadString to avoid per-iteration alloc.
- Tests use strconv.Itoa and the ready channel.
- Simplify comments throughout.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
- rpc.go: handle the new "inspector/fixRequested" SPA notification by
  logging "there is an error and user want AI to fix (source=...): ..."
  to the inspector logger. The standalone host has no Copilot UI, so
  this turns the SPA's Fix buttons into a usable signal during preview.
- Re-embed the rebuilt SPA bundle (azd flavor) carrying the matching
  frontend changes: Fix buttons now route through this notification,
  and Events/Tools/Output empty states say "Run the agent" instead of
  "Run the workflow" under VITE_HOST=azd.
handleFixRequested now writes the "user wants AI to fix" line directly
to os.Stderr in addition to the regular logger. The root command
redirects log.Default() to io.Discard (no --debug) or a daily file
(with --debug), so the existing s.logger.Printf was invisible at the
terminal. Other inspector logs stay on the existing path — only this
specific user-facing signal is teed to stderr.
- rpc.go handleFixRequested: prefix output with "[inspector]
  [fix-with-ai] ", drop the "(source=...)" tag, and indent
  continuation lines so multi-line summaries align under the prefix.
  Empty summaries fall back to "(no error details provided)".
- Re-embed the rebuilt SPA bundle (azd flavor) carrying the matching
  generateFixPrompt change: the "Fix this failed workflow step:
  <executorId>." prefix line is gone, leaving only the fields that
  actually carry data (Failed step / Error Code / Error Message /
  Traceback).
…Default)

Drop the URL-based "/responses" gate in proxyInvoke and the hardcoded
logRaw=false in proxyFetchSSE. Both now log unconditionally through
s.logger; --debug controls visibility through the existing
log.Default() routing (file with --debug, io.Discard without).

- proxyInvoke: always log POST body and response (full body for
  /responses, byte count for others to keep diagnostics readable).
- proxyInvoke streaming: pass logRaw=true so SSE chunks are logged for
  every streaming response, not just /responses.
- proxyFetchSSE: pass logRaw=true so SPA-driven SSE channels (e.g.
  /agentdev/v1/diagnostics that powers the Events tab) are also
  captured. Previously these were silently dropped even with --debug.
proxyInvoke now logs the full buffered response body for every URL,
not just /responses. Symmetry with the streaming SSE path which
already logs every chunk regardless of endpoint. --debug still
controls visibility through log.Default().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

customer-reported identify a customer issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant