Commit e8d8907
Replat azure-ai-agentserver-githubcopilot onto agentserver-core 2.0 + responses 1.0 (#46101)
* feat: replat adapter onto agentserver-core 2.0 + responses 1.0
Major architecture change: FoundryCBAgent (inheritance) → AgentHost +
ResponseHandler (composition). Hypercorn replaces uvicorn.
Fixes:
- SSE streaming truncation (correct RAPI event ordering works natively)
- Duplicate text in streaming (only emit ASSISTANT_MESSAGE_DELTA)
- Eliminates heartbeat hack (built-in SSE keepalive)
Deleted:
- _copilot_response_converter.py (355 lines) — replaced by ResponseEventStream builders
- _copilot_request_converter.py (313 lines) — replaced by get_input_text()
- Unit tests for deleted converter
Preserved:
- Public API: GitHubCopilotAdapter.from_project(".").run()
- All env vars unchanged
- BYOK auth, model discovery, Tool ACL, skill/tool discovery
- Conversation history bootstrap (now uses own AsyncOpenAI client)
Dependencies:
- azure-ai-agentserver-core>=2.0.0a1 (was >=1.0.0b14,<1.0.0b18)
- azure-ai-agentserver-responses>=1.0.0a1 (new)
- Removed opentelemetry-exporter-otlp-proto-http (tracing via core[tracing])
Validated on ADC as trove-replat-v4 with correct streaming.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: make handler an async generator (yield from _handle_create)
The ResponseHandler expects the create_handler to be an async generator
(with __anext__), not a coroutine. Fixed by using async for delegation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use --extra-index-url + --no-input instead of bundled wheels
The Azure DevOps feed is public — pip just needs --no-input to suppress
the interactive auth prompt in non-interactive builds (like ACR Tasks).
Removes wheel download/copy from deploy script and Dockerfile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: separate pip calls for dev feed vs PyPI packages
The Azure DevOps feed proxies github-copilot-sdk but requires auth for
upstream packages it hasn't cached. Fix: install base packages from
dev feed in a separate pip call, then install our package + deps from
PyPI only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR #46101 review comments
- Fix duplicate kwargs bug: remove explicit skill_directories/tools from
create_session() calls — already present in sdk_config via _session_config
- Bump version to 1.0.0b2 to match CHANGELOG
- Remove .foundry-agent.json (contained real session/conversation IDs)
- Close AsyncDefaultCredential in _load_conversation_history to prevent
async transport/socket leak
- Restore attachment handling: _extract_input_with_attachments() extracts
input_file and input_image items from RAPI requests and appends to prompt
- Observe cancellation_signal in event loop to stop early on client disconnect
- Drop [tracing] extra from test Dockerfile to match pyproject.toml deps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: fall back to session_id for multi-turn when conversation_id is missing
The ResponseHandler context only populates conversation_id when the
request includes an explicit "conversation" field. Most callers (invoke
scripts, Playground) only send session_id. Without this fallback,
conversation_id is always None and the adapter creates a fresh Copilot
SDK session on every request, breaking multi-turn.
Also fixes Windows --no-logs in integration test deploy script.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: multi-turn session reuse via raw_body session_id fallback
conversation_id is only set when the request includes an explicit
"conversation" field. Most callers (invoke scripts, Playground) only
send session_id. The adapter now falls back to context.raw_body
["session_id"] and sets it on context.conversation_id so session
reuse and history bootstrap work transparently.
Also adds build tag canary for deployment verification.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: extract conversation_id from raw_body conversation.id for Playground
The Playground sends conversation identity via raw_body['conversation']['id']
(from Chat Completions API translation), not session_id. The fallback now
checks both session_id and conversation.id in the raw body.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add 27 unit tests for replat features
Covers input extraction with attachments, conversation_id fallback
(session_id and conversation.id from raw_body), session config
building, BYOK URL derivation, project endpoint resolution, and
skill directory discovery.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove name from Dockerfile comment to fix cspell
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update import
* fix: update imports for core 2.0 + responses 1.0 renames
AgentHost -> AgentServerHost (in core)
ResponseHandler -> ResponsesAgentServerHost (in responses.hosting)
ResponsesAgentServerHost is now the combined server+handler (no separate AgentHost needed)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: import Tool from copilot.tools in _tool_discovery.py
copilot-sdk 0.2.x moved Tool to copilot.tools module. The try/except
fallback set Tool=None causing TypeError when constructing tools.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Support responses API and always-on model discovery (#46251)
- Always run model discovery even when model is configured via env var
- Validate configured model against discovered deployments
- Accept models with responses=true capability (not just chatCompletion)
- Set wire_api dynamically (responses or completions) based on model capabilities
- Add capabilities dict and wire_api property to FoundryDeployment
- Remove hardcoded gpt-4.1 default from config builder to allow discovery to run
Co-authored-by: Valerie Pham <valeriepham@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: add azure-ai-agentserver-responses to dev_requirements.txt
The sphinx doc build fails because the responses package is a dependency
but was missing from dev_requirements.txt, so CI couldn't resolve imports.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: export get_input_text as public API from responses package
Rename _get_input_text → get_input_text and add it to __init__.py
and __all__. The githubcopilot adapter imports this function, and it's
a natural public API alongside get_input_expanded and get_conversation_id.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: add fallback import for get_input_text compatibility
The sphinx CI venv installs azure-ai-agentserver-responses from the
dev feed (where get_input_text is still private as _get_input_text)
rather than from the locally-built sibling wheel. Add a try/except
fallback so the adapter works with both the old and new export names.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: default wire_api to 'responses' for BYOK mode
Models like gpt-5.3-codex only support the Responses API, not
Chat Completions. When model discovery cannot run (e.g. API key auth
without ARM credentials), wire_api was stuck on 'completions' causing
HTTP 400 'unsupported operation' errors.
The Responses wire API is a superset of Completions, so this default
is backward-compatible with models that support both.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: add MCP toolbox discovery and auto-auth for Foundry toolboxes
Adds automatic discovery of MCP server configs from mcp.json and
environment variables (FOUNDRY_AGENT_TOOLBOX_ENDPOINT, TOOLBOX_MCP_ENDPOINT).
MCP servers without explicit Authorization headers are marked for auto-auth.
The adapter refreshes Foundry-scoped tokens before each session creation,
injecting them into MCP server headers alongside the existing BYOK token
refresh.
Also ensures DefaultAzureCredential is created when MCP auto-auth is
needed, even when using API key BYOK mode (which previously skipped
credential creation).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: add reasoning support and fix public b1 API compatibility
- Add ASSISTANT_REASONING_DELTA / ASSISTANT_REASONING event handling to
_handle_create, emitting reasoning output items before message content
- Fix create_handler -> response_handler rename (with fallback compat)
- Fix emit_done(text) -> emit_text_done(text) + emit_done() for
TextContentBuilder state machine in public b1 packages
- Remove emit_content_done() call (removed from MessageBuilder)
- Lazy msg/text_builder creation to support reasoning-first streaming
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: update BYOK tests to expect wire_api='responses'
Tests were out of sync with fcf8833 which changed BYOK wire_api
default from 'completions' to 'responses'.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: add azure-ai-agentserver-responses to shared_requirements.txt
Fixes dependency analysis failure: the package was missing from
shared_requirements.txt, causing sdk_analyze_deps to report it
as an unknown dependency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR #46101 review comments: use ResponseContext APIs and MCR registry
- Dockerfile: use mcr.microsoft.com/mirror/docker/library/python base image
- _copilot_adapter: use context.get_input_text() and context.get_input_items()
instead of standalone get_input_text(request) and manual input parsing
- _copilot_adapter: use get_conversation_id(context.request) helper instead
of raw_body session_id/conversation.id fallback
- Refactor _extract_input_with_attachments to be async and accept
ResponseContext, following sample_14_file_inputs.py pattern
- Update unit tests to match new async/ResponseContext-based APIs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Extract toolbox MCP logic into _toolbox.py, remove env var reads
- New _toolbox.py: discover_mcp_servers(), refresh_mcp_auth(), make_sdk_safe_mcp_config()
- Remove FOUNDRY_AGENT_TOOLBOX_ENDPOINT/TOOLBOX_MCP_ENDPOINT env var reads
- Accept explicit toolbox_endpoint parameter on GitHubCopilotAdapter
- Auto-detect toolbox URLs (/toolboxes/) and inject Foundry-Features header
- Fix config mutation bug: deep-copy MCP servers before stripping _auto_auth
- Fix missing _auto_auth stripping in GitHubCopilotAdapter._get_or_create_session
- Add SESSION_MCP_SERVERS_LOADED and SESSION_TOOLS_UPDATED event logging
- Add 19 unit tests for toolbox discovery, auth refresh, and SDK-safe config
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix McpBridge: per-request HTTP clients, dataclass ToolInvocation access
- Fix 'Event loop is closed' by creating httpx.AsyncClient per-request
instead of reusing a shared client across different async contexts
- Fix ToolInvocation access: use getattr() instead of dict .get()
(ToolInvocation is a dataclass in SDK 0.2.2, not a TypedDict)
- Remove unused close() logic (no persistent client to close)
- Tool calls now work end-to-end: agent_get successfully lists agents
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add debug logging for empty tools/list response
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Upgrade empty tools/list log to warning level for container visibility
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add httpx to dependencies (fixes sphinx import failure)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: inject AGENTS.md as system_message for agent persona
Load AGENTS.md from the project root in GitHubCopilotAdapter and inject
its content as a system_message with mode 'append'. This ensures deployed
agents embody the persona defined in their AGENTS.md rather than defaulting
to the generic Copilot CLI identity.
The content is only injected when no system_message is already configured,
preserving explicit overrides.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix AttributeError when mcp.json has non-dict values
The discover_mcp_servers function crashes with 'str object has no
attribute setdefault' when mcp.json contains flat key-value entries
(e.g. toolset_id, mcp strings). Skip non-dict values in the header
injection loop.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switch AGENTS.md injection from append to replace mode
Append mode preserves the Copilot CLI identity, causing agents to
introduce themselves as 'GitHub Copilot CLI' instead of their actual
persona. Replace mode makes the AGENTS.md the full system message so
the agent IS the persona defined in it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Guard all mcp_servers iterations against non-dict values
The mcp.json written by foundry-agent can contain flat string entries
(toolset_id, mcp_endpoint, tools, notes). Both discover_mcp_servers,
refresh_toolbox_tokens, and connect_toolboxes now skip non-dict values
to prevent AttributeError crashes at startup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* revert to private
* toolbox debugging
* Use shared HTTP client for MCP init+list_tools to fix connection affinity
The MCP server behind Envoy load balancer returns 0 tools when
initialize and tools/list hit different backend instances. Using a
single httpx.AsyncClient ensures connection reuse across the full
MCP handshake (initialize -> notifications/initialized -> tools/list).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Log JWT identity claims on MCP initialize for auth debugging
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use persistent httpx.AsyncClient in McpBridge (matches working sample)
The working sample uses a single httpx.AsyncClient stored in __init__
and reused across initialize/list_tools/call_tool. Our code created a
new AsyncClient per method call, which breaks connection affinity
through Envoy load balancers — each call could hit a different backend
that never saw the initialize handshake, returning 0 tools.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix CSpell error: rename idtyp to identity_type in log output
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove _describe_token diagnostic helper (CSpell fix)
Remove the JWT token decoding helper that was added for debugging.
The idtyp claim name was flagged by CSpell and the diagnostic has
served its purpose — root cause identified as auth architecture issue.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: root <root@CPC-cearl-W9ZSG.localdomain>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: vmpham1012 <54032794+vmpham1012@users.noreply.github.com>
Co-authored-by: Valerie Pham <valeriepham@microsoft.com>
Co-authored-by: Christopher T Earley <jrekct@gmail.com>1 parent 961082f commit e8d8907
20 files changed
Lines changed: 2236 additions & 1600 deletions
File tree
- sdk/agentserver
- azure-ai-agentserver-githubcopilot
- azure/ai/agentserver/githubcopilot
- tests
- integration
- test_agent
- unit_tests
- azure-ai-agentserver-responses/azure/ai/agentserver/responses
- models
Lines changed: 25 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
4 | 28 | | |
5 | 29 | | |
6 | 30 | | |
| |||
10 | 34 | | |
11 | 35 | | |
12 | 36 | | |
13 | | - | |
| |||
Lines changed: 3 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
11 | 14 | | |
12 | 15 | | |
13 | 16 | | |
| |||
0 commit comments