Migrate to Copilot SDK for azd agent implementation#6883
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds the GitHub Copilot SDK (v0.1.25) as a foundational dependency and creates new agent infrastructure that will eventually replace the existing langchaingo-based implementation. This is Phase 1 of a 3-phase migration, where all new code coexists alongside existing code without any deletions or modifications to current functionality.
Changes:
- Adds GitHub Copilot SDK dependency and creates wrapper types (
CopilotClientManager,SessionConfigBuilder) that bridge azd config to SDK types - Implements
CopilotAgentandCopilotAgentFactorythat satisfy the existingAgentinterface for seamless future switchover - Creates event handling infrastructure (
SessionEventLogger,SessionFileLogger) that maps SDK events to azd's existing UX patterns - Adds 8 new config options (
ai.agent.*) for customizing agent behavior (model, tools, MCP servers, system message)
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
go.mod / go.sum |
Adds copilot-sdk/go v0.1.25 and transitive dependency jsonschema-go v0.4.2 (both marked indirect) |
resources/config_options.yaml |
Adds 8 new config keys for Copilot SDK agent customization |
pkg/llm/copilot_client.go |
Manager wrapping SDK client lifecycle with azd-specific error messages |
pkg/llm/copilot_client_test.go |
Tests for client manager instantiation (2 cases) |
pkg/llm/session_config.go |
Bridge converting azd config → SDK SessionConfig with MCP server merging |
pkg/llm/session_config_test.go |
Tests for config reading, tool control, MCP merging (8 cases) |
internal/agent/copilot_agent.go |
Agent implementation using SDK Session.SendAndWait, reuses existing UX patterns |
internal/agent/copilot_agent_factory.go |
Factory creating agents with SDK client, session, hooks, event handlers |
internal/agent/logging/session_event_handler.go |
Event handlers mapping SDK events to thought channel + file logging |
internal/agent/logging/session_event_handler_test.go |
Tests for event handling, tool input extraction, composite handler (10 cases) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
d606a7b to
b3ff2be
Compare
0c5b9fd to
f4f6bd0
Compare
spboyer
left a comment
There was a problem hiding this comment.
Combined Code Review — GPT-5.4 + Claude Opus 4.6
Reviewed independently by two models, findings deduplicated and combined below.
Summary
| Severity | Count | Details |
|---|---|---|
| 🔴 Critical/Bug | 7 | 3x nil deref in error middleware, 1x error swallowed, 2x security (fail-open consent, blanket permissions), 1x WaitForIdle hang |
| 🟡 Warning | 5 | Always-on content logging, swallowed errors, cleanup ordering, singleton lifecycle, config fallback |
| 🔵 Suggestion | 1 | Ticker/canvas race condition |
Cross-Model Consensus (flagged by BOTH models independently)
These are the highest-confidence findings:
- Nil deref on
agentResult.Contentin error middleware (3 locations) - Original error silently swallowed when user declines fix
- Blanket SDK permission approval bypasses safety boundary
- Consent check fails open on error
Dead Config Keys (flagged by GPT-5.4)
ai.agent.mode in config_options.yaml is never read (factory hard-codes "interactive"), and ai.agent.copilot.logLevel is never read (container constructs NewCopilotClientManager(nil) which defaults to "debug"). Users can set these keys, but they have no runtime effect. Either wire them into construction or remove from config_options.yaml until they work.
c961cd9 to
5e41265
Compare
Heads up: orphaned
|
vhvb1989
left a comment
There was a problem hiding this comment.
No blockers from my side. The migration to the Copilot SDK looks solid overall. Left a couple of comments for consideration:
- Silent plugin install —
ensurePlugins()installs the Azure plugin system-wide without user awareness. Worth surfacing a message or prompting. - Old config migration — Users with
ai.agent.model.typeset to"github-copilot"will get a hard failure. A small auto-migration or friendly error would help early adopters. - Orphaned
ghCopilotbuild tag —github_copilot.go,ci-build.ps1, and CI YAML params still reference the old build tag. Dead code that can be cleaned up in a follow-up.
None of these are blocking — just flagging for author consideration.
Bug fixes: - Fix nil pointer dereferences in error.go when agentResult is nil - Fix session.error not unblocking WaitForIdle (signal idleCh on error) - Fix consent check fails-open: deny on error instead of allow Security: - PreToolUse consent check now denies on error with logged reason - OnPermissionRequest remains approve-all (CLI-level coarse permissions, fine-grained control via PreToolUse hooks) Code quality: - Deterministic cleanup order: changed from map to ordered slice with reverse teardown (session events -> file logger -> client) - Log warning on config load failure - Simplified MCP server unmarshaling Config: - Added skills.directories and skills.disabled to config_options.yaml - Fixed tools.available/excluded type from object to array Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AgentMode: - New AgentMode type with constants: AgentModeInteractive, AgentModeAutopilot, AgentModePlan. WithMode() now takes AgentMode instead of string. Removed logging (redundant with Copilot CLI logs at ~/.copilot/logs/): - thought_logger.go — old langchaingo callback handler - file_logger.go — old langchaingo callback handler - chained_handler.go — old langchaingo callback handler - session_event_handler.go — SessionEventLogger, SessionFileLogger, CompositeEventHandler all unused after AgentDisplay consolidation - session_event_handler_test.go Kept: logging/util.go with TruncateString (used by display.go) Net: 902 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the user declines the agent fix, the code returned 'err' which was nil (consent check succeeded), silently swallowing the original command failure. Now returns originalError to preserve the error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deleted (all replaced by Copilot CLI built-in tools): - tools/dev/ — command executor (shell tool) - tools/io/ — 12 file/directory tools + tests - tools/common/ — AnnotatedTool interface, ToLangChainTools, ToolLoader - tools/loader.go — composite tool loader - tools/mcp/tool_adapter.go — MCP-to-langchaingo adapter - tools/mcp/sampling_handler.go — MCP sampling handler - tools/mcp/elicitation_handler.go — MCP elicitation handler - tools/mcp/loader.go — MCP tool loader - consent/consent_wrapper_tool.go — langchaingo tool wrapper Cleaned: - Removed WrapTool/WrapTools from ConsentManager interface and impl - Removed common package import from consent - Kept tools/mcp/embed.go with McpJson embed (still used by factory) Net: 7,025 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updated test expectations after MCP tool migration to Copilot SDK skills. Tests now verify error_troubleshooting, provision_common_error, and validate_azure_yaml (the 3 remaining MCP tools). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- intPtr() -> new() for pointer creation - strings.Split -> strings.SplitSeq for range iteration - strings.HasPrefix+TrimPrefix -> strings.CutPrefix - floatPtr/strPtr helpers replaced with new() in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…error middleware - Migrate all config keys from ai.agent.* to copilot.* namespace - Move copilot_client.go and session_config.go to internal/agent/copilot/ - Delete entire pkg/llm/ package (azure_openai, ollama, github_copilot, model_factory, manager) - Move consent commands from azd mcp consent to azd copilot consent - Streamline error middleware: single consent prompt + agent-driven troubleshooting - Troubleshooting prompts in embedded Go text templates - AgentDisplay: render AssistantMessage in real-time, red x for failed tools - Remove Content from AgentResult, delete dead feedback package - Adopt SDK bundler for CLI binary embedding, remove npm path scanning - Clean up CI pipelines: remove ghCopilot build tag and ldflags - Add WithSystemMessage AgentOption - Add composable config key constants with ConfigRoot prefix - Remove langchaingo dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add CopilotCLI managed tool (internal/agent/copilot/cli.go) following Bicep pattern
- Download platform-specific CLI from npm registry on first use
- Cache at ~/.azd/bin/copilot-cli-{version}, override via AZD_COPILOT_CLI_PATH
- Implement tools.ExternalTool interface (Name, InstallUrl, CheckInstalled)
- Integrate with CopilotClientManager (resolves CLI at Start time)
- Remove SDK bundler (zcopilot_* files, go tool bundler CI step, tool dep)
- Binary size reduced ~106MB (no longer embedded)
- Fix cspell: add agentcopilot to word list, reword comment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix WaitForIdle hang when SessionIdle fires before AssistantMessage - Fix ticker vs Pause() TOCTOU race causing spinner to render over consent prompts - Fix consent grant errors silently denying tool execution - Add error logging for consent rule load/save failures - Improve tool completion display with contextual verbs and diff stats - Add tree-style sub-detail for shell commands and MCP tool args - Add colored diff stats (green +N / red -N) for edit/create tools - Show plugin version in skill invocation display - Normalize whitespace spacing via printSeparated for all section transitions - Show error messages on tool call failures - Add comprehensive unit tests for display helpers and consent persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4e90ac2 to
e500827
Compare
Includes copilot CLI plugin management (ListPlugins, InstallPlugin), session time formatting, consent command migration, and azdcontext updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On WSL with Windows filesystem mounts (/mnt/c/...), fsnotify's filepath.Walk + inotify watch setup can hang or be extremely slow. Moving NewWatcher() to a background goroutine prevents it from blocking SendMessage. The watcher results are still collected at cleanup via mutex-protected access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…E test - Check GitHub Copilot auth status before session creation - Prompt to sign in via copilot login (OAuth device flow) if not authenticated - Add CopilotCLI.Login() wrapper for interactive copilot login command - Fix spinner showing 'Running Ran tool' — use tool name for spinner, verb for completion - Fix E2E test: add OnPermissionRequest: ApproveAll to session config - Add ErrToolExecutionSkipped to excluded errors list in error mapping test - Add unit tests for Login success and error cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
weikanglim
left a comment
There was a problem hiding this comment.
Overall this looks good! I'm really happy that we're removing the langchain dependencies in favor of copilot-sdk.
I left a comment about namespacing session state just to make it easier for us to work with longer term.
* Add Copilot SDK foundation alongside existing langchaingo agent Add the GitHub Copilot SDK (github.com/github/copilot-sdk/go) as a new dependency and create the foundational types for a Copilot SDK-based agent implementation. All new code coexists alongside the existing langchaingo agent — no existing code is modified or deleted. New files: - pkg/llm/copilot_client.go: CopilotClientManager wrapping copilot.Client lifecycle (Start, Stop, GetAuthStatus, ListModels) - pkg/llm/session_config.go: SessionConfigBuilder that reads ai.agent.* config keys and produces copilot.SessionConfig, including MCP server merging (built-in + user-configured) and tool control - internal/agent/copilot_agent.go: CopilotAgent implementing the Agent interface backed by copilot.Session with SendAndWait - internal/agent/copilot_agent_factory.go: CopilotAgentFactory that creates CopilotAgent instances with SDK client, session, permission hooks, MCP servers, and event handlers - internal/agent/logging/session_event_handler.go: SessionEventLogger, SessionFileLogger, and CompositeEventHandler for SDK SessionEvent streaming to UX thought channel and daily log files Config additions (resources/config_options.yaml): - ai.agent.model: Default model for Copilot SDK sessions - ai.agent.mode: Agent mode (autopilot/interactive/plan) - ai.agent.mcp.servers: Additional MCP servers - ai.agent.tools.available/excluded: Tool allow/deny lists - ai.agent.systemMessage: Custom system prompt append - ai.agent.copilot.logLevel: SDK log level Resolves Azure#6871, Azure#6872, Azure#6873, Azure#6874, Azure#6875 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add 'copilot' as default agent provider type Register CopilotProvider as a named 'copilot' model provider in the IoC container. This makes 'copilot' the default agent type when ai.agent.model.type is not explicitly configured. Changes: - Add LlmTypeCopilot constant and CopilotProvider (copilot_provider.go) - Default GetDefaultModel() to 'copilot' when no model type is set - Register 'copilot' provider in container.go - Update init.go to set 'copilot' instead of 'github-copilot' - Update error message to list copilot as supported type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic logging to Copilot SDK agent pipeline Add [copilot] and [copilot-event] prefixed log statements throughout the Copilot SDK agent pipeline for troubleshooting: - CopilotClientManager: Start/stop state transitions - CopilotAgentFactory: MCP server count, session config details, PreToolUse/PostToolUse/ErrorOccurred hook invocations - CopilotAgent.SendMessage: prompt size, response size, errors - SessionEventLogger: every event type received, plus detail for assistant.message, tool.execution_start, and assistant.reasoning Run with AZD_DEBUG=true to see log output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Wire CopilotAgentFactory into AgentFactory for automatic delegation AgentFactory.Create() now checks the configured model type. When it's 'copilot' (the new default), it delegates to CopilotAgentFactory which creates a CopilotAgent backed by the Copilot SDK session. No call site changes needed — existing code calling AgentFactory.Create() gets the Copilot SDK agent automatically. Changes: - AgentFactory now takes CopilotAgentFactory as a dependency - Create() checks model type and delegates to CopilotAgentFactory - Register CopilotAgentFactory, CopilotClientManager, and SessionConfigBuilder in IoC container (cmd/container.go) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable SDK debug logging to diagnose CLI process startup failure Set SDK LogLevel to 'debug' by default to surface the command and args the SDK uses when spawning the copilot CLI process. This will help diagnose the 'exit status 1' error during client.Start(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.26-preview.0 CLI v0.0.419-0 now supports --headless and --stdio flags required by the SDK. Updated Go SDK from v0.1.25 to v0.1.26-preview.0 for latest compatibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Auto-discover native Copilot CLI binary from @github/copilot-sdk npm package The copilot CLI shim in PATH (from @github/copilot npm package) doesn't support --headless --stdio flags required by the Go SDK. However, the @github/copilot-sdk npm package bundles a newer native binary at node_modules/@github/copilot-{platform}/copilot[.exe] that does. CopilotClientManager now auto-discovers this binary with resolution order: 1. COPILOT_CLI_PATH environment variable 2. Native binary from @github/copilot-sdk npm package (platform-specific) 3. Falls back to 'copilot' in PATH (SDK default) Also adds a passing e2e test (TestCopilotSDK_E2E) that validates the full SDK lifecycle: client start → auth check → list models → create session → send prompt → receive response → cleanup. Pure native binary, no Node.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: explicitly allow tools in PreToolUse hook The empty PreToolUseHookOutput{} was interpreted as deny by the SDK, blocking all tool calls. Set PermissionDecision to 'allow' explicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: add OnPermissionRequest handler to approve tool permissions The SDK has two separate permission mechanisms: 1. PreToolUse hooks (lifecycle interception) - already set to 'allow' 2. OnPermissionRequest handler (CLI permission prompts) - was NOT set Without OnPermissionRequest, the CLI's permission requests go unanswered and default to deny, blocking all tool calls. Use the SDK's built-in PermissionHandler.ApproveAll to approve all requests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: increase SendAndWait timeout to 10 minutes The SDK defaults to 60s timeout when the context has no deadline, which is too short for agent init tasks (discovery, IaC generation, Dockerfile creation, etc.). Set a 10-minute timeout per message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use Send + idle event instead of SendAndWait to avoid timeout Replace SendAndWait (which imposes a 60s default timeout) with Send (non-blocking) + explicit wait for session.idle event. The agent task runs until the SDK signals completion or the parent context is cancelled. No artificial timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Integrate azd consent system and required plugin auto-install Two major changes to CopilotAgentFactory: 1. Required plugin auto-install: - Runs 'copilot plugin install microsoft/GitHub-Copilot-for-Azure:plugin' before starting each session (idempotent, non-interactive) - Uses the resolved CLI binary path from CopilotClientManager - Logs warnings on install failure but doesn't block session creation 2. Wire azd consent system into SDK permission handlers: - OnPermissionRequest: delegates to ConsentManager.CheckConsent() for CLI-level permission requests. If consent requires prompting, uses ConsentChecker to show azd's interactive consent UX with scoped persistence (session/project/global). - OnPreToolUse: checks ConsentManager before each tool execution. If no rule exists, prompts via ConsentChecker.PromptAndGrantConsent() which stores the user's choice at their selected scope. - Replaces the previous PermissionHandler.ApproveAll with proper consent-gated approval flow. Also: - CopilotAgentFactory now takes ConsentManager as a dependency - CopilotClientManager exposes CLIPath() for plugin install commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mage namespace discovery for dev:install target The Dev struct needs to be declared as 'type Dev mg.Namespace' for mage to discover it as a namespace. Also adds github.com/magefile/mage to go.mod as required by the magefile's mg import. This fixes 'mage dev:install' and 'mage dev:uninstall' targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel schema version conflict causing panic on startup The copilot-sdk pulled in otel core v1.42.0 (schema 1.39.0) but otel/sdk remained at v1.38.0 (schema 1.37.0). This mismatch caused a panic in resource.New() at startup. Upgraded all OTel SDK packages to v1.42.0 to match the core version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.32 Updated from v0.1.26-preview.0 to v0.1.32. Adapted to API change where PermissionRequest.Kind is now a typed PermissionRequestKind instead of plain string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel semconv schema mismatch: update to v1.40.0 OTel SDK v1.42.0 uses semconv/v1.40.0 internally for resource.Default(), but our resource.go imported semconv/v1.39.0. The schema URL mismatch (1.40.0 vs 1.39.0) caused a panic on resource.Merge(). Updated import to match the SDK's semconv version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: approve CLI permission requests, use consent only in PreToolUse The OnPermissionRequest and OnPreToolUse handlers were conflicting: OnPermissionRequest was calling CheckToolConsent (which only checks rules, doesn't prompt) and falling through to 'denied' when no rules existed. Meanwhile OnPreToolUse correctly called PromptAndGrantConsent. Fix: OnPermissionRequest now approves all CLI-level permission requests (file access, shell, URLs). Fine-grained per-tool consent with user prompting is handled exclusively by the OnPreToolUse hook. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentDisplay for consolidated Copilot SDK UX rendering Replace the thought-channel indirection with AgentDisplay — a direct event-driven UX renderer that subscribes to session.On() and handles all SDK event types using existing azd UX components. New: internal/agent/display.go - AgentDisplay manages Canvas + Spinner + VisualElement per SendMessage - Handles 15+ event types: turn lifecycle, tool execution with elapsed timer, reasoning/thinking display, streaming message deltas, errors, warnings, skill invocations, subagent delegation - WaitForIdle() blocks until session.idle with final content capture - Thread-safe state management for concurrent event handling Changes: - CopilotAgent.SendMessage() creates AgentDisplay per turn, subscribes it to session events, and uses WaitForIdle() for completion - CopilotAgentFactory no longer creates thought channel or SessionEventLogger — file logger remains for audit trail - Export TruncateString from logging package for shared use Removes: - Thought channel and WithCopilotThoughtChannel option - renderThoughts() goroutine (replaced by AgentDisplay) - SessionEventLogger dependency in factory (UX moved to display) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix reasoning display and show relative paths in tool summaries Two fixes to AgentDisplay: 1. Reasoning: accumulate reasoning_delta chunks into a rolling buffer instead of replacing with each delta. Also handle streaming_delta events with phase='thinking' for models that emit reasoning that way. Canvas.Update() called after each reasoning update for immediate display. 2. Paths: extractToolInputSummary now converts absolute paths to relative (via filepath.Rel to cwd) for cleaner display. Paths that escape cwd are shown absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show full reasoning in scrolling window with flush on transitions Reasoning display now works as a scrolling window: - VisualElement shows last ~5 lines of reasoning below the spinner, updating live as delta chunks stream in - Full reasoning accumulates in a buffer (no truncation) - On tool start or turn end, the complete reasoning is printed as a persistent dimmed block above the canvas, then the buffer resets Removed latestThought field — reasoning state is now entirely managed via reasoningBuf. Removed AssistantMessageDelta handler (message deltas don't need UX rendering — final content comes via AssistantMessage). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: add blank line before spinner and change text to 'Working...' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Check if plugin is installed before install/update ensurePlugins now lists installed plugins first. If a required plugin is already installed, it runs 'copilot plugin update' instead of a full install. New plugins get 'copilot plugin install'. Also restructured requiredPlugins as pluginSpec with Source (install path) and Name (installed name for update command). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify init to single prompt + wire OnUserInputRequest handler Two changes: 1. Wire OnUserInputRequest in CopilotAgentFactory: Enables the agent's built-in ask_user tool. When the agent asks a question, the handler renders it using azd's UX components: - Choices → ux.NewSelect() with azd styling - Freeform → ux.NewPrompt() for text input This lets the agent ask clarifying questions during execution (architecture choices, service selection, config options). 2. Simplify initAppWithAgent() from 6-step loop to single prompt: Replaces 6 hardcoded steps + inter-step feedback loops + post- completion summary aggregation with a single prompt that delegates to azure-prepare and azure-validate skills from the Azure plugin. The skills handle all orchestration internally and can ask the user questions via ask_user when needed. Removed: initStep struct, step definitions, collectAndApplyFeedback(), postCompletionSummary(), feedback import (~140 lines). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: use intent as spinner text, move reasoning above spinner, set interactive mode Three UX improvements: 1. Spinner text: uses assistant.intent events (short task descriptions like 'Analyzing project structure') instead of static 'Working...' Truncated to 80 chars to stay concise. 2. Reasoning display: moved above the spinner instead of below. Layout is now: blank line → reasoning (last 5 lines, gray) → blank line → spinner. More natural reading order. 3. Mode: SendMessage now explicitly sets Mode to 'interactive' so the agent asks for approval before executing tools. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Strip markdown from ask_user prompts, render reasoning with markdown, fix skill triggers Three changes: 1. ask_user: Strip markdown formatting (bold, italic, backticks, headings) from question text and choice labels before rendering in azd UX prompts. Choices still return the original value to the agent. 2. flushReasoning: Render accumulated reasoning with output.WithMarkdown() instead of raw gray text, giving proper formatting for code blocks, lists, and other markdown content. 3. init prompt: Use natural trigger phrases ('prepare this application for deployment to Azure', 'validate that everything is ready') that match the azure-prepare and azure-validate skill description triggers, instead of referencing skill names directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Support AllowFreeform in ask_user with 'Other' choice option When the agent sends choices with AllowFreeform=true, append an 'Other (type your own answer)' option to the Select list. If selected, follow up with a freeform Prompt and return WasFreeform: true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Explicitly invoke @azure-prepare and @azure-validate skills in init prompt Use @skill-name syntax and numbered steps to ensure both skills are invoked in order. Previous natural language triggers may not have reliably activated the skills. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Specify 'azd' recipe for azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Store Copilot session files in .azure/copilot relative to cwd Sets SessionConfig.ConfigDir to .azure/copilot in the current working directory so session state is project-local instead of global ~/.copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix skill discovery: revert ConfigDir, use qualified skill names ConfigDir override to .azure/copilot broke plugin discovery — the CLI loads plugins from ConfigDir/installed-plugins/ which was empty. Reverted to default ~/.copilot so installed plugins (and their skills) are found. Set WorkingDirectory instead for tool operations. Updated init prompt to use fully qualified azure:azure-prepare and azure:azure-validate skill names (plugin:skill-name format). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pass installed plugins via --plugin-dir for headless mode discovery The CLI in --headless --stdio mode (used by the SDK) doesn't auto-discover installed plugins from ~/.copilot/installed-plugins/. This meant skills from the Azure plugin were never loaded. Fix: discoverInstalledPluginDirs() scans ~/.copilot/installed-plugins/ for plugin directories (verified by presence of skills/ or .claude-plugin/) and passes each via --plugin-dir CLIArgs to the SDK client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Scope plugin discovery to Azure plugin only Only load the microsoft/GitHub-Copilot-for-Azure plugin via --plugin-dir. Checks both _direct and marketplace install paths. Other plugins will be handled separately later. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use SkillDirectories instead of --plugin-dir for skill loading The --plugin-dir CLI flag isn't supported by all copilot binary builds. Instead, pass the Azure plugin's skills directory via SessionConfig. SkillDirectories, which is sent via JSON-RPC createSession and works reliably in headless mode. discoverAzurePluginSkillDirs() finds the skills/ directory from the installed Azure plugin and adds it to SkillDirectories alongside any user-configured skill directories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Persist intent text in spinner instead of resetting to 'Thinking...' Track lastIntent and reuse it when the spinner resets after turn start or tool completion. Only falls back to 'Thinking...' if no intent has been received yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank line after spinner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show relative paths for skill directories in tool summaries toRelativePath now tries both cwd and ~/.copilot/installed-plugins/ as base directories. Paths under the plugins root (skill files) are shown relative instead of absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Capture intent from report_intent tool calls for spinner text The agent uses a report_intent tool (not assistant.intent events) to signal what it's working on. Extract the intent text from the tool's arguments and use it as the spinner text. The report_intent tool is suppressed from UX display (no 'Ran report_intent' completion line). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show nested subagent tool calls with rich display Subagent events now show richer UX: - started: '◆ {DisplayName} — {Description}' - completed: '✔ {DisplayName} completed' with summary - failed: '✖ {DisplayName} failed: {error}' - deselected: resets subagent state Tool calls inside a subagent are indented with 2 spaces to show nesting visually: ◆ Azure Prepare — Prepare apps for deployment ✔ Ran read_file with path: main.go ✔ Ran write_file with path: azure.yaml ✔ Azure Prepare completed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX polish: blank lines before skill/subagent, suppress internal tools - Add blank line before 'Using skill:' and 'Delegating to:' lines - Suppress tool call display for report_intent, ask_user, task, and skill: prefixed tools — these are internal/UX tools that shouldn't show as 'Ran X' completion lines Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Prompt for reasoning effort and model on first agent run First run (no ai.agent.reasoningEffort config): - Prompt for reasoning effort (low/medium/high) with cost guidance - Prompt for model selection (default or specific model) - Save both to azd config Subsequent runs: - Show info note with current model and reasoning level - Point to 'azd config set' for changes Also: - Wire ai.agent.reasoningEffort to SessionConfig.ReasoningEffort - Add reasoningEffort to config_options.yaml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: only signal idle when final content exists Multiple session.idle events can arrive during a session (after permission prompts, between tool calls). The first idle was consumed by WaitForIdle before the assistant message arrived, causing the display to clear with no summary shown. Fix: only signal idleCh when finalContent has been set by an assistant.message event. Early idle events are ignored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Load Azure plugin MCP servers into session config Read .mcp.json from the installed Azure plugin and merge its MCP servers (azure, foundry-mcp, context7) into SessionConfig.MCPServers alongside built-in and user-configured servers. Merge order: built-in → Azure plugin → user config (last wins). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Suppress 'skill' tool from display, add blank line after Using skill Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix agent exit: reset finalContent per turn, add completion logging Root cause: assistant.message from an earlier turn set finalContent, then a session.idle between tool calls found hasContent=true and signaled WaitForIdle prematurely — before the actual final message. Fixes: - Reset finalContent on every assistant.turn_start so only the last turn's message is considered - Handle session.task_complete and session.shutdown as additional completion signals - Add debug logging to assistant.message, session.idle, and WaitForIdle for future troubleshooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add scoped system message and empty directory handling System message (append mode): - Scopes the agent to Azure application development only - Rejects unrelated requests with a focused explanation - User-configured ai.agent.systemMessage appended after default Init prompt: - Agent now checks if cwd is empty/has no code first - If empty, asks user what type of Azure app to build - Then proceeds with azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consistent whitespace and improved config display Whitespace rules: - Skills, subagents, reasoning, errors, warnings: blank line before and after (via printSeparated, no double blanks) - Tool completions: stack without blank lines (via printLine) - lastPrintedBlank flag prevents duplicate blank lines Config display: - Split into multiple lines for readability - Show model and reasoning on separate bullet points - azd commands shown in highlight format Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank lines before/after user prompts, fix extra blank in config display - User prompts (ask_user): blank line before and after Select/Prompt - Config display: remove leading blank line (was doubling up with prior output), remove trailing newline from alpha warning title Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Log MCP server details and skill dirs for debugging Before session creation, log each configured MCP server (name, type, command/url) and skill directory. Also capture session.info events with AllowedTools list and skill.invoked events in the file logger. Visible with AZD_DEBUG=true or in ~/.azd/logs/azd-agent-*.log. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print MCP servers and skill dirs to console output for debugging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print available tools to console from session events Captures AllowedTools from the first session event that carries them and prints the full list to console output for debugging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP server config: use type 'local' and add tools wildcard The SDK expects type='local' (not 'stdio') for local MCP servers, and requires a 'tools' field listing which tools to expose. Without tools=['*'], no MCP tools were made available to the agent. Fixes: - convertServerConfig: use type='local', add tools=['*'] - loadAzurePluginMCPServers: normalize plugin configs — set type to 'local' for command-based servers, add tools=['*'] if missing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic prompt to list all tools including MCP server tools Temporary: agent prints all available tools grouped by built-in, MCP server tools, and skills before proceeding with init. Will remove once MCP server integration is verified working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dump first 30 session events with details for MCP debugging Temporary: prints event type + key fields (allowedTools, tools, message, infoType, name) for the first 30 events to diagnose MCP server loading. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove diagnostic prompt, rely on event dump for tool debugging The system message was causing the model to ignore the diagnostic 'list tools' request. Event dump from the factory provides better programmatic visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Debugging: disable system message, add diagnostic tools list prompt, remove event dump Temporary changes to diagnose MCP server tool availability: - Comment out system message (was blocking diagnostic requests) - Add explicit 'list all tools' prompt before init task - Remove event dump from factory (wasn't showing useful data) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add post-init Q&A loop for follow-up questions After the init summary, prompt the user 'Any questions?' in a loop. User can ask follow-up questions (sent to the same agent session with full context). Press Enter with no input to finish and exit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add session resume support for cancelled/crashed agent sessions On azd init with agent mode, checks for previous sessions in the current directory via client.ListSessions(). If found, prompts user to resume a previous session or start fresh. Resume uses client.ResumeSession() which restores full conversation history with the same MCP servers, skills, permissions, and hooks. Changes: - CopilotAgentFactory: add ListSessions() and Resume() methods - init.go: add session picker before agent creation, add CopilotAgentFactory to initAction struct Spec at docs/specs/copilot-agent-ux/session-resume.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Polish session resume UX: numbers, local time, truncated labels - Show numbered choices in session picker (DisplayNumbers: true) - Convert timestamps to local time (Today 3:04 PM, Yesterday, Jan 2) - Truncate labels to ~120 chars total - Shorter prompt: 'Previous sessions found:' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix session labels: collapse newlines, enforce 120 char max Summaries from sessions can contain newlines and markdown. Use strings.Fields() to collapse all whitespace into single spaces, then truncate to fit within 120 chars total. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable line numbers and filtering on all Select prompts Added DisplayNumbers and EnableFiltering to reasoning effort, model selection, and session picker prompts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dynamic model list from SDK with billing and reasoning metadata Fetch models via ListModels() instead of hardcoding. Each option shows: 'Claude Sonnet 4.5 (high) (1x)' — name, default reasoning effort, billing multiplier Also: - Remove trailing ':' from prompt messages (UX components add them) - Add ListModels() to CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show session usage metrics at end of init Accumulate usage from assistant.usage and session.usage_info events: input/output tokens, cost multiplier, premium requests, API duration, and model used. Display at session end: Session usage: • Model: claude-sonnet-4.5 • Input tokens: 45.2K • Output tokens: 12.8K • Total tokens: 58.0K • Cost: 1.0x premium • Premium requests: 15 • API duration: 2m 34s Token counts formatted as K/M for readability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove post-init Q&A loop Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consolidate agent into self-contained CopilotAgent with factory Major refactor: CopilotAgent is now a self-contained agent that encapsulates initialization, session management, display, and usage. CopilotAgentFactory creates agents with dependencies wired via IoC. New API: agent, _ := factory.Create(ctx, agent.WithMode('interactive')) initResult, _ := agent.Initialize(ctx) selected, _ := agent.SelectSession(ctx) result, _ := agent.SendMessage(ctx, prompt, agent.WithSessionID(...)) // result.Content, result.SessionID, result.Usage agent.Stop() New types (types.go): AgentResult{Content, SessionID, Usage} InitResult{Model, ReasoningEffort, IsFirstRun} UsageMetrics with Format() method AgentOption: WithModel, WithReasoningEffort, WithMode, WithDebug SendOption: WithSessionID InitOption: WithForcePrompt Agent methods: Initialize() — config prompts (first run), plugin install, client start SelectSession() — UX picker for session resume ListSessions() — raw session listing SendMessage() / SendMessageWithRetry() — returns AgentResult Stop() — cleanup Deleted (old langchaingo agent): agent.go, agent_factory.go, conversational_agent.go, prompts/ Simplified: init.go — ~40 lines instead of ~150 container.go — removed old AgentFactory, ModelFactory registrations error.go — updated to use CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix spacing: add blank line after each prompt, remove leading blanks Each prompt component (Select, Prompt) now adds a blank line after its Ask() call. Callers manage spacing before prompts. Removed the leading blank line from SelectSession (was doubling up with the trailing blank from the previous prompt). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add unit tests for types.go and display.go pure functions New test files: - types_test.go: UsageMetrics.Format(), TotalTokens(), formatTokenCount, stripMarkdown, formatSessionTime (40 test cases) - display_test.go: extractToolInputSummary, extractIntentFromArgs, toRelativePath, GetUsageMetrics accumulation All pure functions tested without SDK mocking. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix billing display and CI checks - Rename Cost to BillingRate (per-request multiplier, not cumulative) - Show premium requests from SDK (omit if not reported) - Handle session.shutdown for TotalPremiumRequests - Remove manual API call counter - Fix all lint issues (errorlint, gosec, staticcheck, unused) - Fix formatting, remove unused code (github_copilot_registration files, discoverInstalledPluginDirs) - All CI checks pass: gofmt, golangci-lint, tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Fix config_options.yaml: tools.available/excluded type 'object' -> 'array' - Add missing config docs: ai.agent.skills.directories, ai.agent.skills.disabled - Log warning on userConfigManager.Load() failure instead of silently swallowing - Simplify redundant MCP server unmarshaling (removed type probe, single path) Other review items (r3, r6, r8, r9) referenced old code that was deleted in the agent consolidation commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address second round PR review feedback Bug fixes: - Fix nil pointer dereferences in error.go when agentResult is nil - Fix session.error not unblocking WaitForIdle (signal idleCh on error) - Fix consent check fails-open: deny on error instead of allow Security: - PreToolUse consent check now denies on error with logged reason - OnPermissionRequest remains approve-all (CLI-level coarse permissions, fine-grained control via PreToolUse hooks) Code quality: - Deterministic cleanup order: changed from map to ordered slice with reverse teardown (session events -> file logger -> client) - Log warning on config load failure - Simplified MCP server unmarshaling Config: - Added skills.directories and skills.disabled to config_options.yaml - Fixed tools.available/excluded type from object to array Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentMode enum, remove redundant logging infrastructure AgentMode: - New AgentMode type with constants: AgentModeInteractive, AgentModeAutopilot, AgentModePlan. WithMode() now takes AgentMode instead of string. Removed logging (redundant with Copilot CLI logs at ~/.copilot/logs/): - thought_logger.go — old langchaingo callback handler - file_logger.go — old langchaingo callback handler - chained_handler.go — old langchaingo callback handler - session_event_handler.go — SessionEventLogger, SessionFileLogger, CompositeEventHandler all unused after AgentDisplay consolidation - session_event_handler_test.go Kept: logging/util.go with TruncateString (used by display.go) Net: 902 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: return originalError when user declines fix in error middleware When the user declines the agent fix, the code returned 'err' which was nil (consent check succeeded), silently swallowing the original command failure. Now returns originalError to preserve the error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document permission handler intent and relationship to PreToolUse Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove docs/specs/copilot-agent-ux (deleted by reviewer) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Delete dead agent tools and unused consent wrapper Deleted (all replaced by Copilot CLI built-in tools): - tools/dev/ — command executor (shell tool) - tools/io/ — 12 file/directory tools + tests - tools/common/ — AnnotatedTool interface, ToLangChainTools, ToolLoader - tools/loader.go — composite tool loader - tools/mcp/tool_adapter.go — MCP-to-langchaingo adapter - tools/mcp/sampling_handler.go — MCP sampling handler - tools/mcp/elicitation_handler.go — MCP elicitation handler - tools/mcp/loader.go — MCP tool loader - consent/consent_wrapper_tool.go — langchaingo tool wrapper Cleaned: - Removed WrapTool/WrapTools from ConsentManager interface and impl - Removed common package import from consent - Kept tools/mcp/embed.go with McpJson embed (still used by factory) Net: 7,025 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused ExecutingTool and related global state from consent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP functional tests for remaining tools Updated test expectations after MCP tool migration to Copilot SDK skills. Tests now verify error_troubleshooting, provision_common_error, and validate_azure_yaml (the 3 remaining MCP tools). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix init.go: remove stray backtick, highlight azd up command Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply go fix for Go 1.26 compatibility - intPtr() -> new() for pointer creation - strings.Split -> strings.SplitSeq for range iteration - strings.HasPrefix+TrimPrefix -> strings.CutPrefix - floatPtr/strPtr helpers replaced with new() in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused intPtr, floatPtr, strPtr helpers (inlined by go fix) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: migrate to copilot.* namespace, delete pkg/llm, streamline error middleware - Migrate all config keys from ai.agent.* to copilot.* namespace - Move copilot_client.go and session_config.go to internal/agent/copilot/ - Delete entire pkg/llm/ package (azure_openai, ollama, github_copilot, model_factory, manager) - Move consent commands from azd mcp consent to azd copilot consent - Streamline error middleware: single consent prompt + agent-driven troubleshooting - Troubleshooting prompts in embedded Go text templates - AgentDisplay: render AssistantMessage in real-time, red x for failed tools - Remove Content from AgentResult, delete dead feedback package - Adopt SDK bundler for CLI binary embedding, remove npm path scanning - Clean up CI pipelines: remove ghCopilot build tag and ldflags - Add WithSystemMessage AgentOption - Add composable config key constants with ConfigRoot prefix - Remove langchaingo dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: on-demand Copilot CLI download, replace SDK bundler - Add CopilotCLI managed tool (internal/agent/copilot/cli.go) following Bicep pattern - Download platform-specific CLI from npm registry on first use - Cache at ~/.azd/bin/copilot-cli-{version}, override via AZD_COPILOT_CLI_PATH - Implement tools.ExternalTool interface (Name, InstallUrl, CheckInstalled) - Integrate with CopilotClientManager (resolves CLI at Start time) - Remove SDK bundler (zcopilot_* files, go tool bundler CI step, tool dep) - Binary size reduced ~106MB (no longer embedded) - Fix cspell: add agentcopilot to word list, reword comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: copilot display deadlocks, consent persistence, and UX improvements - Fix WaitForIdle hang when SessionIdle fires before AssistantMessage - Fix ticker vs Pause() TOCTOU race causing spinner to render over consent prompts - Fix consent grant errors silently denying tool execution - Add error logging for consent rule load/save failures - Improve tool completion display with contextual verbs and diff stats - Add tree-style sub-detail for shell commands and MCP tool args - Add colored diff stats (green +N / red -N) for edit/create tools - Show plugin version in skill invocation display - Normalize whitespace spacing via printSeparated for all section transitions - Show error messages on tool call failures - Add comprehensive unit tests for display helpers and consent persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: commit pending branch changes for CI Includes copilot CLI plugin management (ListPlugins, InstallPlugin), session time formatting, consent command migration, and azdcontext updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: move file watcher to background goroutine to prevent WSL hang On WSL with Windows filesystem mounts (/mnt/c/...), fsnotify's filepath.Walk + inotify watch setup can hang or be extremely slow. Moving NewWatcher() to a background goroutine prevents it from blocking SendMessage. The watcher results are still collected at cleanup via mutex-protected access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add auth check with interactive login, fix spinner verb, fix E2E test - Check GitHub Copilot auth status before session creation - Prompt to sign in via copilot login (OAuth device flow) if not authenticated - Add CopilotCLI.Login() wrapper for interactive copilot login command - Fix spinner showing 'Running Ran tool' — use tool name for spinner, verb for completion - Fix E2E test: add OnPermissionRequest: ApproveAll to session config - Add ErrToolExecutionSkipped to excluded errors list in error mapping test - Add unit tests for Login success and error cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Copilot SDK foundation alongside existing langchaingo agent Add the GitHub Copilot SDK (github.com/github/copilot-sdk/go) as a new dependency and create the foundational types for a Copilot SDK-based agent implementation. All new code coexists alongside the existing langchaingo agent — no existing code is modified or deleted. New files: - pkg/llm/copilot_client.go: CopilotClientManager wrapping copilot.Client lifecycle (Start, Stop, GetAuthStatus, ListModels) - pkg/llm/session_config.go: SessionConfigBuilder that reads ai.agent.* config keys and produces copilot.SessionConfig, including MCP server merging (built-in + user-configured) and tool control - internal/agent/copilot_agent.go: CopilotAgent implementing the Agent interface backed by copilot.Session with SendAndWait - internal/agent/copilot_agent_factory.go: CopilotAgentFactory that creates CopilotAgent instances with SDK client, session, permission hooks, MCP servers, and event handlers - internal/agent/logging/session_event_handler.go: SessionEventLogger, SessionFileLogger, and CompositeEventHandler for SDK SessionEvent streaming to UX thought channel and daily log files Config additions (resources/config_options.yaml): - ai.agent.model: Default model for Copilot SDK sessions - ai.agent.mode: Agent mode (autopilot/interactive/plan) - ai.agent.mcp.servers: Additional MCP servers - ai.agent.tools.available/excluded: Tool allow/deny lists - ai.agent.systemMessage: Custom system prompt append - ai.agent.copilot.logLevel: SDK log level Resolves Azure#6871, Azure#6872, Azure#6873, Azure#6874, Azure#6875 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add 'copilot' as default agent provider type Register CopilotProvider as a named 'copilot' model provider in the IoC container. This makes 'copilot' the default agent type when ai.agent.model.type is not explicitly configured. Changes: - Add LlmTypeCopilot constant and CopilotProvider (copilot_provider.go) - Default GetDefaultModel() to 'copilot' when no model type is set - Register 'copilot' provider in container.go - Update init.go to set 'copilot' instead of 'github-copilot' - Update error message to list copilot as supported type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic logging to Copilot SDK agent pipeline Add [copilot] and [copilot-event] prefixed log statements throughout the Copilot SDK agent pipeline for troubleshooting: - CopilotClientManager: Start/stop state transitions - CopilotAgentFactory: MCP server count, session config details, PreToolUse/PostToolUse/ErrorOccurred hook invocations - CopilotAgent.SendMessage: prompt size, response size, errors - SessionEventLogger: every event type received, plus detail for assistant.message, tool.execution_start, and assistant.reasoning Run with AZD_DEBUG=true to see log output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Wire CopilotAgentFactory into AgentFactory for automatic delegation AgentFactory.Create() now checks the configured model type. When it's 'copilot' (the new default), it delegates to CopilotAgentFactory which creates a CopilotAgent backed by the Copilot SDK session. No call site changes needed — existing code calling AgentFactory.Create() gets the Copilot SDK agent automatically. Changes: - AgentFactory now takes CopilotAgentFactory as a dependency - Create() checks model type and delegates to CopilotAgentFactory - Register CopilotAgentFactory, CopilotClientManager, and SessionConfigBuilder in IoC container (cmd/container.go) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable SDK debug logging to diagnose CLI process startup failure Set SDK LogLevel to 'debug' by default to surface the command and args the SDK uses when spawning the copilot CLI process. This will help diagnose the 'exit status 1' error during client.Start(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.26-preview.0 CLI v0.0.419-0 now supports --headless and --stdio flags required by the SDK. Updated Go SDK from v0.1.25 to v0.1.26-preview.0 for latest compatibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Auto-discover native Copilot CLI binary from @github/copilot-sdk npm package The copilot CLI shim in PATH (from @github/copilot npm package) doesn't support --headless --stdio flags required by the Go SDK. However, the @github/copilot-sdk npm package bundles a newer native binary at node_modules/@github/copilot-{platform}/copilot[.exe] that does. CopilotClientManager now auto-discovers this binary with resolution order: 1. COPILOT_CLI_PATH environment variable 2. Native binary from @github/copilot-sdk npm package (platform-specific) 3. Falls back to 'copilot' in PATH (SDK default) Also adds a passing e2e test (TestCopilotSDK_E2E) that validates the full SDK lifecycle: client start → auth check → list models → create session → send prompt → receive response → cleanup. Pure native binary, no Node.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: explicitly allow tools in PreToolUse hook The empty PreToolUseHookOutput{} was interpreted as deny by the SDK, blocking all tool calls. Set PermissionDecision to 'allow' explicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: add OnPermissionRequest handler to approve tool permissions The SDK has two separate permission mechanisms: 1. PreToolUse hooks (lifecycle interception) - already set to 'allow' 2. OnPermissionRequest handler (CLI permission prompts) - was NOT set Without OnPermissionRequest, the CLI's permission requests go unanswered and default to deny, blocking all tool calls. Use the SDK's built-in PermissionHandler.ApproveAll to approve all requests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: increase SendAndWait timeout to 10 minutes The SDK defaults to 60s timeout when the context has no deadline, which is too short for agent init tasks (discovery, IaC generation, Dockerfile creation, etc.). Set a 10-minute timeout per message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use Send + idle event instead of SendAndWait to avoid timeout Replace SendAndWait (which imposes a 60s default timeout) with Send (non-blocking) + explicit wait for session.idle event. The agent task runs until the SDK signals completion or the parent context is cancelled. No artificial timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Integrate azd consent system and required plugin auto-install Two major changes to CopilotAgentFactory: 1. Required plugin auto-install: - Runs 'copilot plugin install microsoft/GitHub-Copilot-for-Azure:plugin' before starting each session (idempotent, non-interactive) - Uses the resolved CLI binary path from CopilotClientManager - Logs warnings on install failure but doesn't block session creation 2. Wire azd consent system into SDK permission handlers: - OnPermissionRequest: delegates to ConsentManager.CheckConsent() for CLI-level permission requests. If consent requires prompting, uses ConsentChecker to show azd's interactive consent UX with scoped persistence (session/project/global). - OnPreToolUse: checks ConsentManager before each tool execution. If no rule exists, prompts via ConsentChecker.PromptAndGrantConsent() which stores the user's choice at their selected scope. - Replaces the previous PermissionHandler.ApproveAll with proper consent-gated approval flow. Also: - CopilotAgentFactory now takes ConsentManager as a dependency - CopilotClientManager exposes CLIPath() for plugin install commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mage namespace discovery for dev:install target The Dev struct needs to be declared as 'type Dev mg.Namespace' for mage to discover it as a namespace. Also adds github.com/magefile/mage to go.mod as required by the magefile's mg import. This fixes 'mage dev:install' and 'mage dev:uninstall' targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel schema version conflict causing panic on startup The copilot-sdk pulled in otel core v1.42.0 (schema 1.39.0) but otel/sdk remained at v1.38.0 (schema 1.37.0). This mismatch caused a panic in resource.New() at startup. Upgraded all OTel SDK packages to v1.42.0 to match the core version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.32 Updated from v0.1.26-preview.0 to v0.1.32. Adapted to API change where PermissionRequest.Kind is now a typed PermissionRequestKind instead of plain string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel semconv schema mismatch: update to v1.40.0 OTel SDK v1.42.0 uses semconv/v1.40.0 internally for resource.Default(), but our resource.go imported semconv/v1.39.0. The schema URL mismatch (1.40.0 vs 1.39.0) caused a panic on resource.Merge(). Updated import to match the SDK's semconv version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: approve CLI permission requests, use consent only in PreToolUse The OnPermissionRequest and OnPreToolUse handlers were conflicting: OnPermissionRequest was calling CheckToolConsent (which only checks rules, doesn't prompt) and falling through to 'denied' when no rules existed. Meanwhile OnPreToolUse correctly called PromptAndGrantConsent. Fix: OnPermissionRequest now approves all CLI-level permission requests (file access, shell, URLs). Fine-grained per-tool consent with user prompting is handled exclusively by the OnPreToolUse hook. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentDisplay for consolidated Copilot SDK UX rendering Replace the thought-channel indirection with AgentDisplay — a direct event-driven UX renderer that subscribes to session.On() and handles all SDK event types using existing azd UX components. New: internal/agent/display.go - AgentDisplay manages Canvas + Spinner + VisualElement per SendMessage - Handles 15+ event types: turn lifecycle, tool execution with elapsed timer, reasoning/thinking display, streaming message deltas, errors, warnings, skill invocations, subagent delegation - WaitForIdle() blocks until session.idle with final content capture - Thread-safe state management for concurrent event handling Changes: - CopilotAgent.SendMessage() creates AgentDisplay per turn, subscribes it to session events, and uses WaitForIdle() for completion - CopilotAgentFactory no longer creates thought channel or SessionEventLogger — file logger remains for audit trail - Export TruncateString from logging package for shared use Removes: - Thought channel and WithCopilotThoughtChannel option - renderThoughts() goroutine (replaced by AgentDisplay) - SessionEventLogger dependency in factory (UX moved to display) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix reasoning display and show relative paths in tool summaries Two fixes to AgentDisplay: 1. Reasoning: accumulate reasoning_delta chunks into a rolling buffer instead of replacing with each delta. Also handle streaming_delta events with phase='thinking' for models that emit reasoning that way. Canvas.Update() called after each reasoning update for immediate display. 2. Paths: extractToolInputSummary now converts absolute paths to relative (via filepath.Rel to cwd) for cleaner display. Paths that escape cwd are shown absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show full reasoning in scrolling window with flush on transitions Reasoning display now works as a scrolling window: - VisualElement shows last ~5 lines of reasoning below the spinner, updating live as delta chunks stream in - Full reasoning accumulates in a buffer (no truncation) - On tool start or turn end, the complete reasoning is printed as a persistent dimmed block above the canvas, then the buffer resets Removed latestThought field — reasoning state is now entirely managed via reasoningBuf. Removed AssistantMessageDelta handler (message deltas don't need UX rendering — final content comes via AssistantMessage). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: add blank line before spinner and change text to 'Working...' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Check if plugin is installed before install/update ensurePlugins now lists installed plugins first. If a required plugin is already installed, it runs 'copilot plugin update' instead of a full install. New plugins get 'copilot plugin install'. Also restructured requiredPlugins as pluginSpec with Source (install path) and Name (installed name for update command). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify init to single prompt + wire OnUserInputRequest handler Two changes: 1. Wire OnUserInputRequest in CopilotAgentFactory: Enables the agent's built-in ask_user tool. When the agent asks a question, the handler renders it using azd's UX components: - Choices → ux.NewSelect() with azd styling - Freeform → ux.NewPrompt() for text input This lets the agent ask clarifying questions during execution (architecture choices, service selection, config options). 2. Simplify initAppWithAgent() from 6-step loop to single prompt: Replaces 6 hardcoded steps + inter-step feedback loops + post- completion summary aggregation with a single prompt that delegates to azure-prepare and azure-validate skills from the Azure plugin. The skills handle all orchestration internally and can ask the user questions via ask_user when needed. Removed: initStep struct, step definitions, collectAndApplyFeedback(), postCompletionSummary(), feedback import (~140 lines). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: use intent as spinner text, move reasoning above spinner, set interactive mode Three UX improvements: 1. Spinner text: uses assistant.intent events (short task descriptions like 'Analyzing project structure') instead of static 'Working...' Truncated to 80 chars to stay concise. 2. Reasoning display: moved above the spinner instead of below. Layout is now: blank line → reasoning (last 5 lines, gray) → blank line → spinner. More natural reading order. 3. Mode: SendMessage now explicitly sets Mode to 'interactive' so the agent asks for approval before executing tools. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Strip markdown from ask_user prompts, render reasoning with markdown, fix skill triggers Three changes: 1. ask_user: Strip markdown formatting (bold, italic, backticks, headings) from question text and choice labels before rendering in azd UX prompts. Choices still return the original value to the agent. 2. flushReasoning: Render accumulated reasoning with output.WithMarkdown() instead of raw gray text, giving proper formatting for code blocks, lists, and other markdown content. 3. init prompt: Use natural trigger phrases ('prepare this application for deployment to Azure', 'validate that everything is ready') that match the azure-prepare and azure-validate skill description triggers, instead of referencing skill names directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Support AllowFreeform in ask_user with 'Other' choice option When the agent sends choices with AllowFreeform=true, append an 'Other (type your own answer)' option to the Select list. If selected, follow up with a freeform Prompt and return WasFreeform: true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Explicitly invoke @azure-prepare and @azure-validate skills in init prompt Use @skill-name syntax and numbered steps to ensure both skills are invoked in order. Previous natural language triggers may not have reliably activated the skills. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Specify 'azd' recipe for azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Store Copilot session files in .azure/copilot relative to cwd Sets SessionConfig.ConfigDir to .azure/copilot in the current working directory so session state is project-local instead of global ~/.copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix skill discovery: revert ConfigDir, use qualified skill names ConfigDir override to .azure/copilot broke plugin discovery — the CLI loads plugins from ConfigDir/installed-plugins/ which was empty. Reverted to default ~/.copilot so installed plugins (and their skills) are found. Set WorkingDirectory instead for tool operations. Updated init prompt to use fully qualified azure:azure-prepare and azure:azure-validate skill names (plugin:skill-name format). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pass installed plugins via --plugin-dir for headless mode discovery The CLI in --headless --stdio mode (used by the SDK) doesn't auto-discover installed plugins from ~/.copilot/installed-plugins/. This meant skills from the Azure plugin were never loaded. Fix: discoverInstalledPluginDirs() scans ~/.copilot/installed-plugins/ for plugin directories (verified by presence of skills/ or .claude-plugin/) and passes each via --plugin-dir CLIArgs to the SDK client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Scope plugin discovery to Azure plugin only Only load the microsoft/GitHub-Copilot-for-Azure plugin via --plugin-dir. Checks both _direct and marketplace install paths. Other plugins will be handled separately later. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use SkillDirectories instead of --plugin-dir for skill loading The --plugin-dir CLI flag isn't supported by all copilot binary builds. Instead, pass the Azure plugin's skills directory via SessionConfig. SkillDirectories, which is sent via JSON-RPC createSession and works reliably in headless mode. discoverAzurePluginSkillDirs() finds the skills/ directory from the installed Azure plugin and adds it to SkillDirectories alongside any user-configured skill directories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Persist intent text in spinner instead of resetting to 'Thinking...' Track lastIntent and reuse it when the spinner resets after turn start or tool completion. Only falls back to 'Thinking...' if no intent has been received yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank line after spinner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show relative paths for skill directories in tool summaries toRelativePath now tries both cwd and ~/.copilot/installed-plugins/ as base directories. Paths under the plugins root (skill files) are shown relative instead of absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Capture intent from report_intent tool calls for spinner text The agent uses a report_intent tool (not assistant.intent events) to signal what it's working on. Extract the intent text from the tool's arguments and use it as the spinner text. The report_intent tool is suppressed from UX display (no 'Ran report_intent' completion line). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show nested subagent tool calls with rich display Subagent events now show richer UX: - started: '◆ {DisplayName} — {Description}' - completed: '✔ {DisplayName} completed' with summary - failed: '✖ {DisplayName} failed: {error}' - deselected: resets subagent state Tool calls inside a subagent are indented with 2 spaces to show nesting visually: ◆ Azure Prepare — Prepare apps for deployment ✔ Ran read_file with path: main.go ✔ Ran write_file with path: azure.yaml ✔ Azure Prepare completed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX polish: blank lines before skill/subagent, suppress internal tools - Add blank line before 'Using skill:' and 'Delegating to:' lines - Suppress tool call display for report_intent, ask_user, task, and skill: prefixed tools — these are internal/UX tools that shouldn't show as 'Ran X' completion lines Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Prompt for reasoning effort and model on first agent run First run (no ai.agent.reasoningEffort config): - Prompt for reasoning effort (low/medium/high) with cost guidance - Prompt for model selection (default or specific model) - Save both to azd config Subsequent runs: - Show info note with current model and reasoning level - Point to 'azd config set' for changes Also: - Wire ai.agent.reasoningEffort to SessionConfig.ReasoningEffort - Add reasoningEffort to config_options.yaml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: only signal idle when final content exists Multiple session.idle events can arrive during a session (after permission prompts, between tool calls). The first idle was consumed by WaitForIdle before the assistant message arrived, causing the display to clear with no summary shown. Fix: only signal idleCh when finalContent has been set by an assistant.message event. Early idle events are ignored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Load Azure plugin MCP servers into session config Read .mcp.json from the installed Azure plugin and merge its MCP servers (azure, foundry-mcp, context7) into SessionConfig.MCPServers alongside built-in and user-configured servers. Merge order: built-in → Azure plugin → user config (last wins). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Suppress 'skill' tool from display, add blank line after Using skill Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix agent exit: reset finalContent per turn, add completion logging Root cause: assistant.message from an earlier turn set finalContent, then a session.idle between tool calls found hasContent=true and signaled WaitForIdle prematurely — before the actual final message. Fixes: - Reset finalContent on every assistant.turn_start so only the last turn's message is considered - Handle session.task_complete and session.shutdown as additional completion signals - Add debug logging to assistant.message, session.idle, and WaitForIdle for future troubleshooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add scoped system message and empty directory handling System message (append mode): - Scopes the agent to Azure application development only - Rejects unrelated requests with a focused explanation - User-configured ai.agent.systemMessage appended after default Init prompt: - Agent now checks if cwd is empty/has no code first - If empty, asks user what type of Azure app to build - Then proceeds with azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consistent whitespace and improved config display Whitespace rules: - Skills, subagents, reasoning, errors, warnings: blank line before and after (via printSeparated, no double blanks) - Tool completions: stack without blank lines (via printLine) - lastPrintedBlank flag prevents duplicate blank lines Config display: - Split into multiple lines for readability - Show model and reasoning on separate bullet points - azd commands shown in highlight format Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank lines before/after user prompts, fix extra blank in config display - User prompts (ask_user): blank line before and after Select/Prompt - Config display: remove leading blank line (was doubling up with prior output), remove trailing newline from alpha warning title Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Log MCP server details and skill dirs for debugging Before session creation, log each configured MCP server (name, type, command/url) and skill directory. Also capture session.info events with AllowedTools list and skill.invoked events in the file logger. Visible with AZD_DEBUG=true or in ~/.azd/logs/azd-agent-*.log. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print MCP servers and skill dirs to console output for debugging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print available tools to console from session events Captures AllowedTools from the first session event that carries them and prints the full list to console output for debugging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP server config: use type 'local' and add tools wildcard The SDK expects type='local' (not 'stdio') for local MCP servers, and requires a 'tools' field listing which tools to expose. Without tools=['*'], no MCP tools were made available to the agent. Fixes: - convertServerConfig: use type='local', add tools=['*'] - loadAzurePluginMCPServers: normalize plugin configs — set type to 'local' for command-based servers, add tools=['*'] if missing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic prompt to list all tools including MCP server tools Temporary: agent prints all available tools grouped by built-in, MCP server tools, and skills before proceeding with init. Will remove once MCP server integration is verified working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dump first 30 session events with details for MCP debugging Temporary: prints event type + key fields (allowedTools, tools, message, infoType, name) for the first 30 events to diagnose MCP server loading. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove diagnostic prompt, rely on event dump for tool debugging The system message was causing the model to ignore the diagnostic 'list tools' request. Event dump from the factory provides better programmatic visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Debugging: disable system message, add diagnostic tools list prompt, remove event dump Temporary changes to diagnose MCP server tool availability: - Comment out system message (was blocking diagnostic requests) - Add explicit 'list all tools' prompt before init task - Remove event dump from factory (wasn't showing useful data) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add post-init Q&A loop for follow-up questions After the init summary, prompt the user 'Any questions?' in a loop. User can ask follow-up questions (sent to the same agent session with full context). Press Enter with no input to finish and exit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add session resume support for cancelled/crashed agent sessions On azd init with agent mode, checks for previous sessions in the current directory via client.ListSessions(). If found, prompts user to resume a previous session or start fresh. Resume uses client.ResumeSession() which restores full conversation history with the same MCP servers, skills, permissions, and hooks. Changes: - CopilotAgentFactory: add ListSessions() and Resume() methods - init.go: add session picker before agent creation, add CopilotAgentFactory to initAction struct Spec at docs/specs/copilot-agent-ux/session-resume.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Polish session resume UX: numbers, local time, truncated labels - Show numbered choices in session picker (DisplayNumbers: true) - Convert timestamps to local time (Today 3:04 PM, Yesterday, Jan 2) - Truncate labels to ~120 chars total - Shorter prompt: 'Previous sessions found:' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix session labels: collapse newlines, enforce 120 char max Summaries from sessions can contain newlines and markdown. Use strings.Fields() to collapse all whitespace into single spaces, then truncate to fit within 120 chars total. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable line numbers and filtering on all Select prompts Added DisplayNumbers and EnableFiltering to reasoning effort, model selection, and session picker prompts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dynamic model list from SDK with billing and reasoning metadata Fetch models via ListModels() instead of hardcoding. Each option shows: 'Claude Sonnet 4.5 (high) (1x)' — name, default reasoning effort, billing multiplier Also: - Remove trailing ':' from prompt messages (UX components add them) - Add ListModels() to CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show session usage metrics at end of init Accumulate usage from assistant.usage and session.usage_info events: input/output tokens, cost multiplier, premium requests, API duration, and model used. Display at session end: Session usage: • Model: claude-sonnet-4.5 • Input tokens: 45.2K • Output tokens: 12.8K • Total tokens: 58.0K • Cost: 1.0x premium • Premium requests: 15 • API duration: 2m 34s Token counts formatted as K/M for readability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove post-init Q&A loop Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consolidate agent into self-contained CopilotAgent with factory Major refactor: CopilotAgent is now a self-contained agent that encapsulates initialization, session management, display, and usage. CopilotAgentFactory creates agents with dependencies wired via IoC. New API: agent, _ := factory.Create(ctx, agent.WithMode('interactive')) initResult, _ := agent.Initialize(ctx) selected, _ := agent.SelectSession(ctx) result, _ := agent.SendMessage(ctx, prompt, agent.WithSessionID(...)) // result.Content, result.SessionID, result.Usage agent.Stop() New types (types.go): AgentResult{Content, SessionID, Usage} InitResult{Model, ReasoningEffort, IsFirstRun} UsageMetrics with Format() method AgentOption: WithModel, WithReasoningEffort, WithMode, WithDebug SendOption: WithSessionID InitOption: WithForcePrompt Agent methods: Initialize() — config prompts (first run), plugin install, client start SelectSession() — UX picker for session resume ListSessions() — raw session listing SendMessage() / SendMessageWithRetry() — returns AgentResult Stop() — cleanup Deleted (old langchaingo agent): agent.go, agent_factory.go, conversational_agent.go, prompts/ Simplified: init.go — ~40 lines instead of ~150 container.go — removed old AgentFactory, ModelFactory registrations error.go — updated to use CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix spacing: add blank line after each prompt, remove leading blanks Each prompt component (Select, Prompt) now adds a blank line after its Ask() call. Callers manage spacing before prompts. Removed the leading blank line from SelectSession (was doubling up with the trailing blank from the previous prompt). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add unit tests for types.go and display.go pure functions New test files: - types_test.go: UsageMetrics.Format(), TotalTokens(), formatTokenCount, stripMarkdown, formatSessionTime (40 test cases) - display_test.go: extractToolInputSummary, extractIntentFromArgs, toRelativePath, GetUsageMetrics accumulation All pure functions tested without SDK mocking. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix billing display and CI checks - Rename Cost to BillingRate (per-request multiplier, not cumulative) - Show premium requests from SDK (omit if not reported) - Handle session.shutdown for TotalPremiumRequests - Remove manual API call counter - Fix all lint issues (errorlint, gosec, staticcheck, unused) - Fix formatting, remove unused code (github_copilot_registration files, discoverInstalledPluginDirs) - All CI checks pass: gofmt, golangci-lint, tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Fix config_options.yaml: tools.available/excluded type 'object' -> 'array' - Add missing config docs: ai.agent.skills.directories, ai.agent.skills.disabled - Log warning on userConfigManager.Load() failure instead of silently swallowing - Simplify redundant MCP server unmarshaling (removed type probe, single path) Other review items (r3, r6, r8, r9) referenced old code that was deleted in the agent consolidation commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address second round PR review feedback Bug fixes: - Fix nil pointer dereferences in error.go when agentResult is nil - Fix session.error not unblocking WaitForIdle (signal idleCh on error) - Fix consent check fails-open: deny on error instead of allow Security: - PreToolUse consent check now denies on error with logged reason - OnPermissionRequest remains approve-all (CLI-level coarse permissions, fine-grained control via PreToolUse hooks) Code quality: - Deterministic cleanup order: changed from map to ordered slice with reverse teardown (session events -> file logger -> client) - Log warning on config load failure - Simplified MCP server unmarshaling Config: - Added skills.directories and skills.disabled to config_options.yaml - Fixed tools.available/excluded type from object to array Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentMode enum, remove redundant logging infrastructure AgentMode: - New AgentMode type with constants: AgentModeInteractive, AgentModeAutopilot, AgentModePlan. WithMode() now takes AgentMode instead of string. Removed logging (redundant with Copilot CLI logs at ~/.copilot/logs/): - thought_logger.go — old langchaingo callback handler - file_logger.go — old langchaingo callback handler - chained_handler.go — old langchaingo callback handler - session_event_handler.go — SessionEventLogger, SessionFileLogger, CompositeEventHandler all unused after AgentDisplay consolidation - session_event_handler_test.go Kept: logging/util.go with TruncateString (used by display.go) Net: 902 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: return originalError when user declines fix in error middleware When the user declines the agent fix, the code returned 'err' which was nil (consent check succeeded), silently swallowing the original command failure. Now returns originalError to preserve the error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document permission handler intent and relationship to PreToolUse Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove docs/specs/copilot-agent-ux (deleted by reviewer) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Delete dead agent tools and unused consent wrapper Deleted (all replaced by Copilot CLI built-in tools): - tools/dev/ — command executor (shell tool) - tools/io/ — 12 file/directory tools + tests - tools/common/ — AnnotatedTool interface, ToLangChainTools, ToolLoader - tools/loader.go — composite tool loader - tools/mcp/tool_adapter.go — MCP-to-langchaingo adapter - tools/mcp/sampling_handler.go — MCP sampling handler - tools/mcp/elicitation_handler.go — MCP elicitation handler - tools/mcp/loader.go — MCP tool loader - consent/consent_wrapper_tool.go — langchaingo tool wrapper Cleaned: - Removed WrapTool/WrapTools from ConsentManager interface and impl - Removed common package import from consent - Kept tools/mcp/embed.go with McpJson embed (still used by factory) Net: 7,025 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused ExecutingTool and related global state from consent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP functional tests for remaining tools Updated test expectations after MCP tool migration to Copilot SDK skills. Tests now verify error_troubleshooting, provision_common_error, and validate_azure_yaml (the 3 remaining MCP tools). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix init.go: remove stray backtick, highlight azd up command Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply go fix for Go 1.26 compatibility - intPtr() -> new() for pointer creation - strings.Split -> strings.SplitSeq for range iteration - strings.HasPrefix+TrimPrefix -> strings.CutPrefix - floatPtr/strPtr helpers replaced with new() in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused intPtr, floatPtr, strPtr helpers (inlined by go fix) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: migrate to copilot.* namespace, delete pkg/llm, streamline error middleware - Migrate all config keys from ai.agent.* to copilot.* namespace - Move copilot_client.go and session_config.go to internal/agent/copilot/ - Delete entire pkg/llm/ package (azure_openai, ollama, github_copilot, model_factory, manager) - Move consent commands from azd mcp consent to azd copilot consent - Streamline error middleware: single consent prompt + agent-driven troubleshooting - Troubleshooting prompts in embedded Go text templates - AgentDisplay: render AssistantMessage in real-time, red x for failed tools - Remove Content from AgentResult, delete dead feedback package - Adopt SDK bundler for CLI binary embedding, remove npm path scanning - Clean up CI pipelines: remove ghCopilot build tag and ldflags - Add WithSystemMessage AgentOption - Add composable config key constants with ConfigRoot prefix - Remove langchaingo dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: on-demand Copilot CLI download, replace SDK bundler - Add CopilotCLI managed tool (internal/agent/copilot/cli.go) following Bicep pattern - Download platform-specific CLI from npm registry on first use - Cache at ~/.azd/bin/copilot-cli-{version}, override via AZD_COPILOT_CLI_PATH - Implement tools.ExternalTool interface (Name, InstallUrl, CheckInstalled) - Integrate with CopilotClientManager (resolves CLI at Start time) - Remove SDK bundler (zcopilot_* files, go tool bundler CI step, tool dep) - Binary size reduced ~106MB (no longer embedded) - Fix cspell: add agentcopilot to word list, reword comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: copilot display deadlocks, consent persistence, and UX improvements - Fix WaitForIdle hang when SessionIdle fires before AssistantMessage - Fix ticker vs Pause() TOCTOU race causing spinner to render over consent prompts - Fix consent grant errors silently denying tool execution - Add error logging for consent rule load/save failures - Improve tool completion display with contextual verbs and diff stats - Add tree-style sub-detail for shell commands and MCP tool args - Add colored diff stats (green +N / red -N) for edit/create tools - Show plugin version in skill invocation display - Normalize whitespace spacing via printSeparated for all section transitions - Show error messages on tool call failures - Add comprehensive unit tests for display helpers and consent persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: commit pending branch changes for CI Includes copilot CLI plugin management (ListPlugins, InstallPlugin), session time formatting, consent command migration, and azdcontext updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: move file watcher to background goroutine to prevent WSL hang On WSL with Windows filesystem mounts (/mnt/c/...), fsnotify's filepath.Walk + inotify watch setup can hang or be extremely slow. Moving NewWatcher() to a background goroutine prevents it from blocking SendMessage. The watcher results are still collected at cleanup via mutex-protected access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add auth check with interactive login, fix spinner verb, fix E2E test - Check GitHub Copilot auth status before session creation - Prompt to sign in via copilot login (OAuth device flow) if not authenticated - Add CopilotCLI.Login() wrapper for interactive copilot login command - Fix spinner showing 'Running Ran tool' — use tool name for spinner, verb for completion - Fix E2E test: add OnPermissionRequest: ApproveAll to session config - Add ErrToolExecutionSkipped to excluded errors list in error mapping test - Add unit tests for Login success and error cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Copilot SDK foundation alongside existing langchaingo agent Add the GitHub Copilot SDK (github.com/github/copilot-sdk/go) as a new dependency and create the foundational types for a Copilot SDK-based agent implementation. All new code coexists alongside the existing langchaingo agent — no existing code is modified or deleted. New files: - pkg/llm/copilot_client.go: CopilotClientManager wrapping copilot.Client lifecycle (Start, Stop, GetAuthStatus, ListModels) - pkg/llm/session_config.go: SessionConfigBuilder that reads ai.agent.* config keys and produces copilot.SessionConfig, including MCP server merging (built-in + user-configured) and tool control - internal/agent/copilot_agent.go: CopilotAgent implementing the Agent interface backed by copilot.Session with SendAndWait - internal/agent/copilot_agent_factory.go: CopilotAgentFactory that creates CopilotAgent instances with SDK client, session, permission hooks, MCP servers, and event handlers - internal/agent/logging/session_event_handler.go: SessionEventLogger, SessionFileLogger, and CompositeEventHandler for SDK SessionEvent streaming to UX thought channel and daily log files Config additions (resources/config_options.yaml): - ai.agent.model: Default model for Copilot SDK sessions - ai.agent.mode: Agent mode (autopilot/interactive/plan) - ai.agent.mcp.servers: Additional MCP servers - ai.agent.tools.available/excluded: Tool allow/deny lists - ai.agent.systemMessage: Custom system prompt append - ai.agent.copilot.logLevel: SDK log level Resolves Azure#6871, Azure#6872, Azure#6873, Azure#6874, Azure#6875 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add 'copilot' as default agent provider type Register CopilotProvider as a named 'copilot' model provider in the IoC container. This makes 'copilot' the default agent type when ai.agent.model.type is not explicitly configured. Changes: - Add LlmTypeCopilot constant and CopilotProvider (copilot_provider.go) - Default GetDefaultModel() to 'copilot' when no model type is set - Register 'copilot' provider in container.go - Update init.go to set 'copilot' instead of 'github-copilot' - Update error message to list copilot as supported type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic logging to Copilot SDK agent pipeline Add [copilot] and [copilot-event] prefixed log statements throughout the Copilot SDK agent pipeline for troubleshooting: - CopilotClientManager: Start/stop state transitions - CopilotAgentFactory: MCP server count, session config details, PreToolUse/PostToolUse/ErrorOccurred hook invocations - CopilotAgent.SendMessage: prompt size, response size, errors - SessionEventLogger: every event type received, plus detail for assistant.message, tool.execution_start, and assistant.reasoning Run with AZD_DEBUG=true to see log output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Wire CopilotAgentFactory into AgentFactory for automatic delegation AgentFactory.Create() now checks the configured model type. When it's 'copilot' (the new default), it delegates to CopilotAgentFactory which creates a CopilotAgent backed by the Copilot SDK session. No call site changes needed — existing code calling AgentFactory.Create() gets the Copilot SDK agent automatically. Changes: - AgentFactory now takes CopilotAgentFactory as a dependency - Create() checks model type and delegates to CopilotAgentFactory - Register CopilotAgentFactory, CopilotClientManager, and SessionConfigBuilder in IoC container (cmd/container.go) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable SDK debug logging to diagnose CLI process startup failure Set SDK LogLevel to 'debug' by default to surface the command and args the SDK uses when spawning the copilot CLI process. This will help diagnose the 'exit status 1' error during client.Start(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.26-preview.0 CLI v0.0.419-0 now supports --headless and --stdio flags required by the SDK. Updated Go SDK from v0.1.25 to v0.1.26-preview.0 for latest compatibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Auto-discover native Copilot CLI binary from @github/copilot-sdk npm package The copilot CLI shim in PATH (from @github/copilot npm package) doesn't support --headless --stdio flags required by the Go SDK. However, the @github/copilot-sdk npm package bundles a newer native binary at node_modules/@github/copilot-{platform}/copilot[.exe] that does. CopilotClientManager now auto-discovers this binary with resolution order: 1. COPILOT_CLI_PATH environment variable 2. Native binary from @github/copilot-sdk npm package (platform-specific) 3. Falls back to 'copilot' in PATH (SDK default) Also adds a passing e2e test (TestCopilotSDK_E2E) that validates the full SDK lifecycle: client start → auth check → list models → create session → send prompt → receive response → cleanup. Pure native binary, no Node.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: explicitly allow tools in PreToolUse hook The empty PreToolUseHookOutput{} was interpreted as deny by the SDK, blocking all tool calls. Set PermissionDecision to 'allow' explicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: add OnPermissionRequest handler to approve tool permissions The SDK has two separate permission mechanisms: 1. PreToolUse hooks (lifecycle interception) - already set to 'allow' 2. OnPermissionRequest handler (CLI permission prompts) - was NOT set Without OnPermissionRequest, the CLI's permission requests go unanswered and default to deny, blocking all tool calls. Use the SDK's built-in PermissionHandler.ApproveAll to approve all requests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: increase SendAndWait timeout to 10 minutes The SDK defaults to 60s timeout when the context has no deadline, which is too short for agent init tasks (discovery, IaC generation, Dockerfile creation, etc.). Set a 10-minute timeout per message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use Send + idle event instead of SendAndWait to avoid timeout Replace SendAndWait (which imposes a 60s default timeout) with Send (non-blocking) + explicit wait for session.idle event. The agent task runs until the SDK signals completion or the parent context is cancelled. No artificial timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Integrate azd consent system and required plugin auto-install Two major changes to CopilotAgentFactory: 1. Required plugin auto-install: - Runs 'copilot plugin install microsoft/GitHub-Copilot-for-Azure:plugin' before starting each session (idempotent, non-interactive) - Uses the resolved CLI binary path from CopilotClientManager - Logs warnings on install failure but doesn't block session creation 2. Wire azd consent system into SDK permission handlers: - OnPermissionRequest: delegates to ConsentManager.CheckConsent() for CLI-level permission requests. If consent requires prompting, uses ConsentChecker to show azd's interactive consent UX with scoped persistence (session/project/global). - OnPreToolUse: checks ConsentManager before each tool execution. If no rule exists, prompts via ConsentChecker.PromptAndGrantConsent() which stores the user's choice at their selected scope. - Replaces the previous PermissionHandler.ApproveAll with proper consent-gated approval flow. Also: - CopilotAgentFactory now takes ConsentManager as a dependency - CopilotClientManager exposes CLIPath() for plugin install commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mage namespace discovery for dev:install target The Dev struct needs to be declared as 'type Dev mg.Namespace' for mage to discover it as a namespace. Also adds github.com/magefile/mage to go.mod as required by the magefile's mg import. This fixes 'mage dev:install' and 'mage dev:uninstall' targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel schema version conflict causing panic on startup The copilot-sdk pulled in otel core v1.42.0 (schema 1.39.0) but otel/sdk remained at v1.38.0 (schema 1.37.0). This mismatch caused a panic in resource.New() at startup. Upgraded all OTel SDK packages to v1.42.0 to match the core version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.32 Updated from v0.1.26-preview.0 to v0.1.32. Adapted to API change where PermissionRequest.Kind is now a typed PermissionRequestKind instead of plain string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel semconv schema mismatch: update to v1.40.0 OTel SDK v1.42.0 uses semconv/v1.40.0 internally for resource.Default(), but our resource.go imported semconv/v1.39.0. The schema URL mismatch (1.40.0 vs 1.39.0) caused a panic on resource.Merge(). Updated import to match the SDK's semconv version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: approve CLI permission requests, use consent only in PreToolUse The OnPermissionRequest and OnPreToolUse handlers were conflicting: OnPermissionRequest was calling CheckToolConsent (which only checks rules, doesn't prompt) and falling through to 'denied' when no rules existed. Meanwhile OnPreToolUse correctly called PromptAndGrantConsent. Fix: OnPermissionRequest now approves all CLI-level permission requests (file access, shell, URLs). Fine-grained per-tool consent with user prompting is handled exclusively by the OnPreToolUse hook. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentDisplay for consolidated Copilot SDK UX rendering Replace the thought-channel indirection with AgentDisplay — a direct event-driven UX renderer that subscribes to session.On() and handles all SDK event types using existing azd UX components. New: internal/agent/display.go - AgentDisplay manages Canvas + Spinner + VisualElement per SendMessage - Handles 15+ event types: turn lifecycle, tool execution with elapsed timer, reasoning/thinking display, streaming message deltas, errors, warnings, skill invocations, subagent delegation - WaitForIdle() blocks until session.idle with final content capture - Thread-safe state management for concurrent event handling Changes: - CopilotAgent.SendMessage() creates AgentDisplay per turn, subscribes it to session events, and uses WaitForIdle() for completion - CopilotAgentFactory no longer creates thought channel or SessionEventLogger — file logger remains for audit trail - Export TruncateString from logging package for shared use Removes: - Thought channel and WithCopilotThoughtChannel option - renderThoughts() goroutine (replaced by AgentDisplay) - SessionEventLogger dependency in factory (UX moved to display) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix reasoning display and show relative paths in tool summaries Two fixes to AgentDisplay: 1. Reasoning: accumulate reasoning_delta chunks into a rolling buffer instead of replacing with each delta. Also handle streaming_delta events with phase='thinking' for models that emit reasoning that way. Canvas.Update() called after each reasoning update for immediate display. 2. Paths: extractToolInputSummary now converts absolute paths to relative (via filepath.Rel to cwd) for cleaner display. Paths that escape cwd are shown absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show full reasoning in scrolling window with flush on transitions Reasoning display now works as a scrolling window: - VisualElement shows last ~5 lines of reasoning below the spinner, updating live as delta chunks stream in - Full reasoning accumulates in a buffer (no truncation) - On tool start or turn end, the complete reasoning is printed as a persistent dimmed block above the canvas, then the buffer resets Removed latestThought field — reasoning state is now entirely managed via reasoningBuf. Removed AssistantMessageDelta handler (message deltas don't need UX rendering — final content comes via AssistantMessage). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: add blank line before spinner and change text to 'Working...' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Check if plugin is installed before install/update ensurePlugins now lists installed plugins first. If a required plugin is already installed, it runs 'copilot plugin update' instead of a full install. New plugins get 'copilot plugin install'. Also restructured requiredPlugins as pluginSpec with Source (install path) and Name (installed name for update command). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify init to single prompt + wire OnUserInputRequest handler Two changes: 1. Wire OnUserInputRequest in CopilotAgentFactory: Enables the agent's built-in ask_user tool. When the agent asks a question, the handler renders it using azd's UX components: - Choices → ux.NewSelect() with azd styling - Freeform → ux.NewPrompt() for text input This lets the agent ask clarifying questions during execution (architecture choices, service selection, config options). 2. Simplify initAppWithAgent() from 6-step loop to single prompt: Replaces 6 hardcoded steps + inter-step feedback loops + post- completion summary aggregation with a single prompt that delegates to azure-prepare and azure-validate skills from the Azure plugin. The skills handle all orchestration internally and can ask the user questions via ask_user when needed. Removed: initStep struct, step definitions, collectAndApplyFeedback(), postCompletionSummary(), feedback import (~140 lines). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: use intent as spinner text, move reasoning above spinner, set interactive mode Three UX improvements: 1. Spinner text: uses assistant.intent events (short task descriptions like 'Analyzing project structure') instead of static 'Working...' Truncated to 80 chars to stay concise. 2. Reasoning display: moved above the spinner instead of below. Layout is now: blank line → reasoning (last 5 lines, gray) → blank line → spinner. More natural reading order. 3. Mode: SendMessage now explicitly sets Mode to 'interactive' so the agent asks for approval before executing tools. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Strip markdown from ask_user prompts, render reasoning with markdown, fix skill triggers Three changes: 1. ask_user: Strip markdown formatting (bold, italic, backticks, headings) from question text and choice labels before rendering in azd UX prompts. Choices still return the original value to the agent. 2. flushReasoning: Render accumulated reasoning with output.WithMarkdown() instead of raw gray text, giving proper formatting for code blocks, lists, and other markdown content. 3. init prompt: Use natural trigger phrases ('prepare this application for deployment to Azure', 'validate that everything is ready') that match the azure-prepare and azure-validate skill description triggers, instead of referencing skill names directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Support AllowFreeform in ask_user with 'Other' choice option When the agent sends choices with AllowFreeform=true, append an 'Other (type your own answer)' option to the Select list. If selected, follow up with a freeform Prompt and return WasFreeform: true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Explicitly invoke @azure-prepare and @azure-validate skills in init prompt Use @skill-name syntax and numbered steps to ensure both skills are invoked in order. Previous natural language triggers may not have reliably activated the skills. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Specify 'azd' recipe for azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Store Copilot session files in .azure/copilot relative to cwd Sets SessionConfig.ConfigDir to .azure/copilot in the current working directory so session state is project-local instead of global ~/.copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix skill discovery: revert ConfigDir, use qualified skill names ConfigDir override to .azure/copilot broke plugin discovery — the CLI loads plugins from ConfigDir/installed-plugins/ which was empty. Reverted to default ~/.copilot so installed plugins (and their skills) are found. Set WorkingDirectory instead for tool operations. Updated init prompt to use fully qualified azure:azure-prepare and azure:azure-validate skill names (plugin:skill-name format). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pass installed plugins via --plugin-dir for headless mode discovery The CLI in --headless --stdio mode (used by the SDK) doesn't auto-discover installed plugins from ~/.copilot/installed-plugins/. This meant skills from the Azure plugin were never loaded. Fix: discoverInstalledPluginDirs() scans ~/.copilot/installed-plugins/ for plugin directories (verified by presence of skills/ or .claude-plugin/) and passes each via --plugin-dir CLIArgs to the SDK client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Scope plugin discovery to Azure plugin only Only load the microsoft/GitHub-Copilot-for-Azure plugin via --plugin-dir. Checks both _direct and marketplace install paths. Other plugins will be handled separately later. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use SkillDirectories instead of --plugin-dir for skill loading The --plugin-dir CLI flag isn't supported by all copilot binary builds. Instead, pass the Azure plugin's skills directory via SessionConfig. SkillDirectories, which is sent via JSON-RPC createSession and works reliably in headless mode. discoverAzurePluginSkillDirs() finds the skills/ directory from the installed Azure plugin and adds it to SkillDirectories alongside any user-configured skill directories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Persist intent text in spinner instead of resetting to 'Thinking...' Track lastIntent and reuse it when the spinner resets after turn start or tool completion. Only falls back to 'Thinking...' if no intent has been received yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank line after spinner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show relative paths for skill directories in tool summaries toRelativePath now tries both cwd and ~/.copilot/installed-plugins/ as base directories. Paths under the plugins root (skill files) are shown relative instead of absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Capture intent from report_intent tool calls for spinner text The agent uses a report_intent tool (not assistant.intent events) to signal what it's working on. Extract the intent text from the tool's arguments and use it as the spinner text. The report_intent tool is suppressed from UX display (no 'Ran report_intent' completion line). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show nested subagent tool calls with rich display Subagent events now show richer UX: - started: '◆ {DisplayName} — {Description}' - completed: '✔ {DisplayName} completed' with summary - failed: '✖ {DisplayName} failed: {error}' - deselected: resets subagent state Tool calls inside a subagent are indented with 2 spaces to show nesting visually: ◆ Azure Prepare — Prepare apps for deployment ✔ Ran read_file with path: main.go ✔ Ran write_file with path: azure.yaml ✔ Azure Prepare completed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX polish: blank lines before skill/subagent, suppress internal tools - Add blank line before 'Using skill:' and 'Delegating to:' lines - Suppress tool call display for report_intent, ask_user, task, and skill: prefixed tools — these are internal/UX tools that shouldn't show as 'Ran X' completion lines Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Prompt for reasoning effort and model on first agent run First run (no ai.agent.reasoningEffort config): - Prompt for reasoning effort (low/medium/high) with cost guidance - Prompt for model selection (default or specific model) - Save both to azd config Subsequent runs: - Show info note with current model and reasoning level - Point to 'azd config set' for changes Also: - Wire ai.agent.reasoningEffort to SessionConfig.ReasoningEffort - Add reasoningEffort to config_options.yaml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: only signal idle when final content exists Multiple session.idle events can arrive during a session (after permission prompts, between tool calls). The first idle was consumed by WaitForIdle before the assistant message arrived, causing the display to clear with no summary shown. Fix: only signal idleCh when finalContent has been set by an assistant.message event. Early idle events are ignored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Load Azure plugin MCP servers into session config Read .mcp.json from the installed Azure plugin and merge its MCP servers (azure, foundry-mcp, context7) into SessionConfig.MCPServers alongside built-in and user-configured servers. Merge order: built-in → Azure plugin → user config (last wins). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Suppress 'skill' tool from display, add blank line after Using skill Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix agent exit: reset finalContent per turn, add completion logging Root cause: assistant.message from an earlier turn set finalContent, then a session.idle between tool calls found hasContent=true and signaled WaitForIdle prematurely — before the actual final message. Fixes: - Reset finalContent on every assistant.turn_start so only the last turn's message is considered - Handle session.task_complete and session.shutdown as additional completion signals - Add debug logging to assistant.message, session.idle, and WaitForIdle for future troubleshooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add scoped system message and empty directory handling System message (append mode): - Scopes the agent to Azure application development only - Rejects unrelated requests with a focused explanation - User-configured ai.agent.systemMessage appended after default Init prompt: - Agent now checks if cwd is empty/has no code first - If empty, asks user what type of Azure app to build - Then proceeds with azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consistent whitespace and improved config display Whitespace rules: - Skills, subagents, reasoning, errors, warnings: blank line before and after (via printSeparated, no double blanks) - Tool completions: stack without blank lines (via printLine) - lastPrintedBlank flag prevents duplicate blank lines Config display: - Split into multiple lines for readability - Show model and reasoning on separate bullet points - azd commands shown in highlight format Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank lines before/after user prompts, fix extra blank in config display - User prompts (ask_user): blank line before and after Select/Prompt - Config display: remove leading blank line (was doubling up with prior output), remove trailing newline from alpha warning title Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Log MCP server details and skill dirs for debugging Before session creation, log each configured MCP server (name, type, command/url) and skill directory. Also capture session.info events with AllowedTools list and skill.invoked events in the file logger. Visible with AZD_DEBUG=true or in ~/.azd/logs/azd-agent-*.log. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print MCP servers and skill dirs to console output for debugging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print available tools to console from session events Captures AllowedTools from the first session event that carries them and prints the full list to console output for debugging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP server config: use type 'local' and add tools wildcard The SDK expects type='local' (not 'stdio') for local MCP servers, and requires a 'tools' field listing which tools to expose. Without tools=['*'], no MCP tools were made available to the agent. Fixes: - convertServerConfig: use type='local', add tools=['*'] - loadAzurePluginMCPServers: normalize plugin configs — set type to 'local' for command-based servers, add tools=['*'] if missing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic prompt to list all tools including MCP server tools Temporary: agent prints all available tools grouped by built-in, MCP server tools, and skills before proceeding with init. Will remove once MCP server integration is verified working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dump first 30 session events with details for MCP debugging Temporary: prints event type + key fields (allowedTools, tools, message, infoType, name) for the first 30 events to diagnose MCP server loading. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove diagnostic prompt, rely on event dump for tool debugging The system message was causing the model to ignore the diagnostic 'list tools' request. Event dump from the factory provides better programmatic visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Debugging: disable system message, add diagnostic tools list prompt, remove event dump Temporary changes to diagnose MCP server tool availability: - Comment out system message (was blocking diagnostic requests) - Add explicit 'list all tools' prompt before init task - Remove event dump from factory (wasn't showing useful data) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add post-init Q&A loop for follow-up questions After the init summary, prompt the user 'Any questions?' in a loop. User can ask follow-up questions (sent to the same agent session with full context). Press Enter with no input to finish and exit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add session resume support for cancelled/crashed agent sessions On azd init with agent mode, checks for previous sessions in the current directory via client.ListSessions(). If found, prompts user to resume a previous session or start fresh. Resume uses client.ResumeSession() which restores full conversation history with the same MCP servers, skills, permissions, and hooks. Changes: - CopilotAgentFactory: add ListSessions() and Resume() methods - init.go: add session picker before agent creation, add CopilotAgentFactory to initAction struct Spec at docs/specs/copilot-agent-ux/session-resume.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Polish session resume UX: numbers, local time, truncated labels - Show numbered choices in session picker (DisplayNumbers: true) - Convert timestamps to local time (Today 3:04 PM, Yesterday, Jan 2) - Truncate labels to ~120 chars total - Shorter prompt: 'Previous sessions found:' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix session labels: collapse newlines, enforce 120 char max Summaries from sessions can contain newlines and markdown. Use strings.Fields() to collapse all whitespace into single spaces, then truncate to fit within 120 chars total. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable line numbers and filtering on all Select prompts Added DisplayNumbers and EnableFiltering to reasoning effort, model selection, and session picker prompts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dynamic model list from SDK with billing and reasoning metadata Fetch models via ListModels() instead of hardcoding. Each option shows: 'Claude Sonnet 4.5 (high) (1x)' — name, default reasoning effort, billing multiplier Also: - Remove trailing ':' from prompt messages (UX components add them) - Add ListModels() to CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show session usage metrics at end of init Accumulate usage from assistant.usage and session.usage_info events: input/output tokens, cost multiplier, premium requests, API duration, and model used. Display at session end: Session usage: • Model: claude-sonnet-4.5 • Input tokens: 45.2K • Output tokens: 12.8K • Total tokens: 58.0K • Cost: 1.0x premium • Premium requests: 15 • API duration: 2m 34s Token counts formatted as K/M for readability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove post-init Q&A loop Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consolidate agent into self-contained CopilotAgent with factory Major refactor: CopilotAgent is now a self-contained agent that encapsulates initialization, session management, display, and usage. CopilotAgentFactory creates agents with dependencies wired via IoC. New API: agent, _ := factory.Create(ctx, agent.WithMode('interactive')) initResult, _ := agent.Initialize(ctx) selected, _ := agent.SelectSession(ctx) result, _ := agent.SendMessage(ctx, prompt, agent.WithSessionID(...)) // result.Content, result.SessionID, result.Usage agent.Stop() New types (types.go): AgentResult{Content, SessionID, Usage} InitResult{Model, ReasoningEffort, IsFirstRun} UsageMetrics with Format() method AgentOption: WithModel, WithReasoningEffort, WithMode, WithDebug SendOption: WithSessionID InitOption: WithForcePrompt Agent methods: Initialize() — config prompts (first run), plugin install, client start SelectSession() — UX picker for session resume ListSessions() — raw session listing SendMessage() / SendMessageWithRetry() — returns AgentResult Stop() — cleanup Deleted (old langchaingo agent): agent.go, agent_factory.go, conversational_agent.go, prompts/ Simplified: init.go — ~40 lines instead of ~150 container.go — removed old AgentFactory, ModelFactory registrations error.go — updated to use CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix spacing: add blank line after each prompt, remove leading blanks Each prompt component (Select, Prompt) now adds a blank line after its Ask() call. Callers manage spacing before prompts. Removed the leading blank line from SelectSession (was doubling up with the trailing blank from the previous prompt). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add unit tests for types.go and display.go pure functions New test files: - types_test.go: UsageMetrics.Format(), TotalTokens(), formatTokenCount, stripMarkdown, formatSessionTime (40 test cases) - display_test.go: extractToolInputSummary, extractIntentFromArgs, toRelativePath, GetUsageMetrics accumulation All pure functions tested without SDK mocking. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix billing display and CI checks - Rename Cost to BillingRate (per-request multiplier, not cumulative) - Show premium requests from SDK (omit if not reported) - Handle session.shutdown for TotalPremiumRequests - Remove manual API call counter - Fix all lint issues (errorlint, gosec, staticcheck, unused) - Fix formatting, remove unused code (github_copilot_registration files, discoverInstalledPluginDirs) - All CI checks pass: gofmt, golangci-lint, tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Fix config_options.yaml: tools.available/excluded type 'object' -> 'array' - Add missing config docs: ai.agent.skills.directories, ai.agent.skills.disabled - Log warning on userConfigManager.Load() failure instead of silently swallowing - Simplify redundant MCP server unmarshaling (removed type probe, single path) Other review items (r3, r6, r8, r9) referenced old code that was deleted in the agent consolidation commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address second round PR review feedback Bug fixes: - Fix nil pointer dereferences in error.go when agentResult is nil - Fix session.error not unblocking WaitForIdle (signal idleCh on error) - Fix consent check fails-open: deny on error instead of allow Security: - PreToolUse consent check now denies on error with logged reason - OnPermissionRequest remains approve-all (CLI-level coarse permissions, fine-grained control via PreToolUse hooks) Code quality: - Deterministic cleanup order: changed from map to ordered slice with reverse teardown (session events -> file logger -> client) - Log warning on config load failure - Simplified MCP server unmarshaling Config: - Added skills.directories and skills.disabled to config_options.yaml - Fixed tools.available/excluded type from object to array Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentMode enum, remove redundant logging infrastructure AgentMode: - New AgentMode type with constants: AgentModeInteractive, AgentModeAutopilot, AgentModePlan. WithMode() now takes AgentMode instead of string. Removed logging (redundant with Copilot CLI logs at ~/.copilot/logs/): - thought_logger.go — old langchaingo callback handler - file_logger.go — old langchaingo callback handler - chained_handler.go — old langchaingo callback handler - session_event_handler.go — SessionEventLogger, SessionFileLogger, CompositeEventHandler all unused after AgentDisplay consolidation - session_event_handler_test.go Kept: logging/util.go with TruncateString (used by display.go) Net: 902 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: return originalError when user declines fix in error middleware When the user declines the agent fix, the code returned 'err' which was nil (consent check succeeded), silently swallowing the original command failure. Now returns originalError to preserve the error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document permission handler intent and relationship to PreToolUse Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove docs/specs/copilot-agent-ux (deleted by reviewer) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Delete dead agent tools and unused consent wrapper Deleted (all replaced by Copilot CLI built-in tools): - tools/dev/ — command executor (shell tool) - tools/io/ — 12 file/directory tools + tests - tools/common/ — AnnotatedTool interface, ToLangChainTools, ToolLoader - tools/loader.go — composite tool loader - tools/mcp/tool_adapter.go — MCP-to-langchaingo adapter - tools/mcp/sampling_handler.go — MCP sampling handler - tools/mcp/elicitation_handler.go — MCP elicitation handler - tools/mcp/loader.go — MCP tool loader - consent/consent_wrapper_tool.go — langchaingo tool wrapper Cleaned: - Removed WrapTool/WrapTools from ConsentManager interface and impl - Removed common package import from consent - Kept tools/mcp/embed.go with McpJson embed (still used by factory) Net: 7,025 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused ExecutingTool and related global state from consent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP functional tests for remaining tools Updated test expectations after MCP tool migration to Copilot SDK skills. Tests now verify error_troubleshooting, provision_common_error, and validate_azure_yaml (the 3 remaining MCP tools). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix init.go: remove stray backtick, highlight azd up command Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply go fix for Go 1.26 compatibility - intPtr() -> new() for pointer creation - strings.Split -> strings.SplitSeq for range iteration - strings.HasPrefix+TrimPrefix -> strings.CutPrefix - floatPtr/strPtr helpers replaced with new() in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused intPtr, floatPtr, strPtr helpers (inlined by go fix) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: migrate to copilot.* namespace, delete pkg/llm, streamline error middleware - Migrate all config keys from ai.agent.* to copilot.* namespace - Move copilot_client.go and session_config.go to internal/agent/copilot/ - Delete entire pkg/llm/ package (azure_openai, ollama, github_copilot, model_factory, manager) - Move consent commands from azd mcp consent to azd copilot consent - Streamline error middleware: single consent prompt + agent-driven troubleshooting - Troubleshooting prompts in embedded Go text templates - AgentDisplay: render AssistantMessage in real-time, red x for failed tools - Remove Content from AgentResult, delete dead feedback package - Adopt SDK bundler for CLI binary embedding, remove npm path scanning - Clean up CI pipelines: remove ghCopilot build tag and ldflags - Add WithSystemMessage AgentOption - Add composable config key constants with ConfigRoot prefix - Remove langchaingo dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: on-demand Copilot CLI download, replace SDK bundler - Add CopilotCLI managed tool (internal/agent/copilot/cli.go) following Bicep pattern - Download platform-specific CLI from npm registry on first use - Cache at ~/.azd/bin/copilot-cli-{version}, override via AZD_COPILOT_CLI_PATH - Implement tools.ExternalTool interface (Name, InstallUrl, CheckInstalled) - Integrate with CopilotClientManager (resolves CLI at Start time) - Remove SDK bundler (zcopilot_* files, go tool bundler CI step, tool dep) - Binary size reduced ~106MB (no longer embedded) - Fix cspell: add agentcopilot to word list, reword comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: copilot display deadlocks, consent persistence, and UX improvements - Fix WaitForIdle hang when SessionIdle fires before AssistantMessage - Fix ticker vs Pause() TOCTOU race causing spinner to render over consent prompts - Fix consent grant errors silently denying tool execution - Add error logging for consent rule load/save failures - Improve tool completion display with contextual verbs and diff stats - Add tree-style sub-detail for shell commands and MCP tool args - Add colored diff stats (green +N / red -N) for edit/create tools - Show plugin version in skill invocation display - Normalize whitespace spacing via printSeparated for all section transitions - Show error messages on tool call failures - Add comprehensive unit tests for display helpers and consent persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: commit pending branch changes for CI Includes copilot CLI plugin management (ListPlugins, InstallPlugin), session time formatting, consent command migration, and azdcontext updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: move file watcher to background goroutine to prevent WSL hang On WSL with Windows filesystem mounts (/mnt/c/...), fsnotify's filepath.Walk + inotify watch setup can hang or be extremely slow. Moving NewWatcher() to a background goroutine prevents it from blocking SendMessage. The watcher results are still collected at cleanup via mutex-protected access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add auth check with interactive login, fix spinner verb, fix E2E test - Check GitHub Copilot auth status before session creation - Prompt to sign in via copilot login (OAuth device flow) if not authenticated - Add CopilotCLI.Login() wrapper for interactive copilot login command - Fix spinner showing 'Running Ran tool' — use tool name for spinner, verb for completion - Fix E2E test: add OnPermissionRequest: ApproveAll to session config - Add ErrToolExecutionSkipped to excluded errors list in error mapping test - Add unit tests for Login success and error cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Copilot SDK foundation alongside existing langchaingo agent Add the GitHub Copilot SDK (github.com/github/copilot-sdk/go) as a new dependency and create the foundational types for a Copilot SDK-based agent implementation. All new code coexists alongside the existing langchaingo agent — no existing code is modified or deleted. New files: - pkg/llm/copilot_client.go: CopilotClientManager wrapping copilot.Client lifecycle (Start, Stop, GetAuthStatus, ListModels) - pkg/llm/session_config.go: SessionConfigBuilder that reads ai.agent.* config keys and produces copilot.SessionConfig, including MCP server merging (built-in + user-configured) and tool control - internal/agent/copilot_agent.go: CopilotAgent implementing the Agent interface backed by copilot.Session with SendAndWait - internal/agent/copilot_agent_factory.go: CopilotAgentFactory that creates CopilotAgent instances with SDK client, session, permission hooks, MCP servers, and event handlers - internal/agent/logging/session_event_handler.go: SessionEventLogger, SessionFileLogger, and CompositeEventHandler for SDK SessionEvent streaming to UX thought channel and daily log files Config additions (resources/config_options.yaml): - ai.agent.model: Default model for Copilot SDK sessions - ai.agent.mode: Agent mode (autopilot/interactive/plan) - ai.agent.mcp.servers: Additional MCP servers - ai.agent.tools.available/excluded: Tool allow/deny lists - ai.agent.systemMessage: Custom system prompt append - ai.agent.copilot.logLevel: SDK log level Resolves Azure#6871, Azure#6872, Azure#6873, Azure#6874, Azure#6875 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add 'copilot' as default agent provider type Register CopilotProvider as a named 'copilot' model provider in the IoC container. This makes 'copilot' the default agent type when ai.agent.model.type is not explicitly configured. Changes: - Add LlmTypeCopilot constant and CopilotProvider (copilot_provider.go) - Default GetDefaultModel() to 'copilot' when no model type is set - Register 'copilot' provider in container.go - Update init.go to set 'copilot' instead of 'github-copilot' - Update error message to list copilot as supported type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic logging to Copilot SDK agent pipeline Add [copilot] and [copilot-event] prefixed log statements throughout the Copilot SDK agent pipeline for troubleshooting: - CopilotClientManager: Start/stop state transitions - CopilotAgentFactory: MCP server count, session config details, PreToolUse/PostToolUse/ErrorOccurred hook invocations - CopilotAgent.SendMessage: prompt size, response size, errors - SessionEventLogger: every event type received, plus detail for assistant.message, tool.execution_start, and assistant.reasoning Run with AZD_DEBUG=true to see log output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Wire CopilotAgentFactory into AgentFactory for automatic delegation AgentFactory.Create() now checks the configured model type. When it's 'copilot' (the new default), it delegates to CopilotAgentFactory which creates a CopilotAgent backed by the Copilot SDK session. No call site changes needed — existing code calling AgentFactory.Create() gets the Copilot SDK agent automatically. Changes: - AgentFactory now takes CopilotAgentFactory as a dependency - Create() checks model type and delegates to CopilotAgentFactory - Register CopilotAgentFactory, CopilotClientManager, and SessionConfigBuilder in IoC container (cmd/container.go) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable SDK debug logging to diagnose CLI process startup failure Set SDK LogLevel to 'debug' by default to surface the command and args the SDK uses when spawning the copilot CLI process. This will help diagnose the 'exit status 1' error during client.Start(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.26-preview.0 CLI v0.0.419-0 now supports --headless and --stdio flags required by the SDK. Updated Go SDK from v0.1.25 to v0.1.26-preview.0 for latest compatibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Auto-discover native Copilot CLI binary from @github/copilot-sdk npm package The copilot CLI shim in PATH (from @github/copilot npm package) doesn't support --headless --stdio flags required by the Go SDK. However, the @github/copilot-sdk npm package bundles a newer native binary at node_modules/@github/copilot-{platform}/copilot[.exe] that does. CopilotClientManager now auto-discovers this binary with resolution order: 1. COPILOT_CLI_PATH environment variable 2. Native binary from @github/copilot-sdk npm package (platform-specific) 3. Falls back to 'copilot' in PATH (SDK default) Also adds a passing e2e test (TestCopilotSDK_E2E) that validates the full SDK lifecycle: client start → auth check → list models → create session → send prompt → receive response → cleanup. Pure native binary, no Node.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: explicitly allow tools in PreToolUse hook The empty PreToolUseHookOutput{} was interpreted as deny by the SDK, blocking all tool calls. Set PermissionDecision to 'allow' explicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: add OnPermissionRequest handler to approve tool permissions The SDK has two separate permission mechanisms: 1. PreToolUse hooks (lifecycle interception) - already set to 'allow' 2. OnPermissionRequest handler (CLI permission prompts) - was NOT set Without OnPermissionRequest, the CLI's permission requests go unanswered and default to deny, blocking all tool calls. Use the SDK's built-in PermissionHandler.ApproveAll to approve all requests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: increase SendAndWait timeout to 10 minutes The SDK defaults to 60s timeout when the context has no deadline, which is too short for agent init tasks (discovery, IaC generation, Dockerfile creation, etc.). Set a 10-minute timeout per message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use Send + idle event instead of SendAndWait to avoid timeout Replace SendAndWait (which imposes a 60s default timeout) with Send (non-blocking) + explicit wait for session.idle event. The agent task runs until the SDK signals completion or the parent context is cancelled. No artificial timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Integrate azd consent system and required plugin auto-install Two major changes to CopilotAgentFactory: 1. Required plugin auto-install: - Runs 'copilot plugin install microsoft/GitHub-Copilot-for-Azure:plugin' before starting each session (idempotent, non-interactive) - Uses the resolved CLI binary path from CopilotClientManager - Logs warnings on install failure but doesn't block session creation 2. Wire azd consent system into SDK permission handlers: - OnPermissionRequest: delegates to ConsentManager.CheckConsent() for CLI-level permission requests. If consent requires prompting, uses ConsentChecker to show azd's interactive consent UX with scoped persistence (session/project/global). - OnPreToolUse: checks ConsentManager before each tool execution. If no rule exists, prompts via ConsentChecker.PromptAndGrantConsent() which stores the user's choice at their selected scope. - Replaces the previous PermissionHandler.ApproveAll with proper consent-gated approval flow. Also: - CopilotAgentFactory now takes ConsentManager as a dependency - CopilotClientManager exposes CLIPath() for plugin install commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mage namespace discovery for dev:install target The Dev struct needs to be declared as 'type Dev mg.Namespace' for mage to discover it as a namespace. Also adds github.com/magefile/mage to go.mod as required by the magefile's mg import. This fixes 'mage dev:install' and 'mage dev:uninstall' targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel schema version conflict causing panic on startup The copilot-sdk pulled in otel core v1.42.0 (schema 1.39.0) but otel/sdk remained at v1.38.0 (schema 1.37.0). This mismatch caused a panic in resource.New() at startup. Upgraded all OTel SDK packages to v1.42.0 to match the core version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.32 Updated from v0.1.26-preview.0 to v0.1.32. Adapted to API change where PermissionRequest.Kind is now a typed PermissionRequestKind instead of plain string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel semconv schema mismatch: update to v1.40.0 OTel SDK v1.42.0 uses semconv/v1.40.0 internally for resource.Default(), but our resource.go imported semconv/v1.39.0. The schema URL mismatch (1.40.0 vs 1.39.0) caused a panic on resource.Merge(). Updated import to match the SDK's semconv version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: approve CLI permission requests, use consent only in PreToolUse The OnPermissionRequest and OnPreToolUse handlers were conflicting: OnPermissionRequest was calling CheckToolConsent (which only checks rules, doesn't prompt) and falling through to 'denied' when no rules existed. Meanwhile OnPreToolUse correctly called PromptAndGrantConsent. Fix: OnPermissionRequest now approves all CLI-level permission requests (file access, shell, URLs). Fine-grained per-tool consent with user prompting is handled exclusively by the OnPreToolUse hook. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentDisplay for consolidated Copilot SDK UX rendering Replace the thought-channel indirection with AgentDisplay — a direct event-driven UX renderer that subscribes to session.On() and handles all SDK event types using existing azd UX components. New: internal/agent/display.go - AgentDisplay manages Canvas + Spinner + VisualElement per SendMessage - Handles 15+ event types: turn lifecycle, tool execution with elapsed timer, reasoning/thinking display, streaming message deltas, errors, warnings, skill invocations, subagent delegation - WaitForIdle() blocks until session.idle with final content capture - Thread-safe state management for concurrent event handling Changes: - CopilotAgent.SendMessage() creates AgentDisplay per turn, subscribes it to session events, and uses WaitForIdle() for completion - CopilotAgentFactory no longer creates thought channel or SessionEventLogger — file logger remains for audit trail - Export TruncateString from logging package for shared use Removes: - Thought channel and WithCopilotThoughtChannel option - renderThoughts() goroutine (replaced by AgentDisplay) - SessionEventLogger dependency in factory (UX moved to display) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix reasoning display and show relative paths in tool summaries Two fixes to AgentDisplay: 1. Reasoning: accumulate reasoning_delta chunks into a rolling buffer instead of replacing with each delta. Also handle streaming_delta events with phase='thinking' for models that emit reasoning that way. Canvas.Update() called after each reasoning update for immediate display. 2. Paths: extractToolInputSummary now converts absolute paths to relative (via filepath.Rel to cwd) for cleaner display. Paths that escape cwd are shown absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show full reasoning in scrolling window with flush on transitions Reasoning display now works as a scrolling window: - VisualElement shows last ~5 lines of reasoning below the spinner, updating live as delta chunks stream in - Full reasoning accumulates in a buffer (no truncation) - On tool start or turn end, the complete reasoning is printed as a persistent dimmed block above the canvas, then the buffer resets Removed latestThought field — reasoning state is now entirely managed via reasoningBuf. Removed AssistantMessageDelta handler (message deltas don't need UX rendering — final content comes via AssistantMessage). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: add blank line before spinner and change text to 'Working...' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Check if plugin is installed before install/update ensurePlugins now lists installed plugins first. If a required plugin is already installed, it runs 'copilot plugin update' instead of a full install. New plugins get 'copilot plugin install'. Also restructured requiredPlugins as pluginSpec with Source (install path) and Name (installed name for update command). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify init to single prompt + wire OnUserInputRequest handler Two changes: 1. Wire OnUserInputRequest in CopilotAgentFactory: Enables the agent's built-in ask_user tool. When the agent asks a question, the handler renders it using azd's UX components: - Choices → ux.NewSelect() with azd styling - Freeform → ux.NewPrompt() for text input This lets the agent ask clarifying questions during execution (architecture choices, service selection, config options). 2. Simplify initAppWithAgent() from 6-step loop to single prompt: Replaces 6 hardcoded steps + inter-step feedback loops + post- completion summary aggregation with a single prompt that delegates to azure-prepare and azure-validate skills from the Azure plugin. The skills handle all orchestration internally and can ask the user questions via ask_user when needed. Removed: initStep struct, step definitions, collectAndApplyFeedback(), postCompletionSummary(), feedback import (~140 lines). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: use intent as spinner text, move reasoning above spinner, set interactive mode Three UX improvements: 1. Spinner text: uses assistant.intent events (short task descriptions like 'Analyzing project structure') instead of static 'Working...' Truncated to 80 chars to stay concise. 2. Reasoning display: moved above the spinner instead of below. Layout is now: blank line → reasoning (last 5 lines, gray) → blank line → spinner. More natural reading order. 3. Mode: SendMessage now explicitly sets Mode to 'interactive' so the agent asks for approval before executing tools. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Strip markdown from ask_user prompts, render reasoning with markdown, fix skill triggers Three changes: 1. ask_user: Strip markdown formatting (bold, italic, backticks, headings) from question text and choice labels before rendering in azd UX prompts. Choices still return the original value to the agent. 2. flushReasoning: Render accumulated reasoning with output.WithMarkdown() instead of raw gray text, giving proper formatting for code blocks, lists, and other markdown content. 3. init prompt: Use natural trigger phrases ('prepare this application for deployment to Azure', 'validate that everything is ready') that match the azure-prepare and azure-validate skill description triggers, instead of referencing skill names directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Support AllowFreeform in ask_user with 'Other' choice option When the agent sends choices with AllowFreeform=true, append an 'Other (type your own answer)' option to the Select list. If selected, follow up with a freeform Prompt and return WasFreeform: true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Explicitly invoke @azure-prepare and @azure-validate skills in init prompt Use @skill-name syntax and numbered steps to ensure both skills are invoked in order. Previous natural language triggers may not have reliably activated the skills. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Specify 'azd' recipe for azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Store Copilot session files in .azure/copilot relative to cwd Sets SessionConfig.ConfigDir to .azure/copilot in the current working directory so session state is project-local instead of global ~/.copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix skill discovery: revert ConfigDir, use qualified skill names ConfigDir override to .azure/copilot broke plugin discovery — the CLI loads plugins from ConfigDir/installed-plugins/ which was empty. Reverted to default ~/.copilot so installed plugins (and their skills) are found. Set WorkingDirectory instead for tool operations. Updated init prompt to use fully qualified azure:azure-prepare and azure:azure-validate skill names (plugin:skill-name format). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pass installed plugins via --plugin-dir for headless mode discovery The CLI in --headless --stdio mode (used by the SDK) doesn't auto-discover installed plugins from ~/.copilot/installed-plugins/. This meant skills from the Azure plugin were never loaded. Fix: discoverInstalledPluginDirs() scans ~/.copilot/installed-plugins/ for plugin directories (verified by presence of skills/ or .claude-plugin/) and passes each via --plugin-dir CLIArgs to the SDK client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Scope plugin discovery to Azure plugin only Only load the microsoft/GitHub-Copilot-for-Azure plugin via --plugin-dir. Checks both _direct and marketplace install paths. Other plugins will be handled separately later. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use SkillDirectories instead of --plugin-dir for skill loading The --plugin-dir CLI flag isn't supported by all copilot binary builds. Instead, pass the Azure plugin's skills directory via SessionConfig. SkillDirectories, which is sent via JSON-RPC createSession and works reliably in headless mode. discoverAzurePluginSkillDirs() finds the skills/ directory from the installed Azure plugin and adds it to SkillDirectories alongside any user-configured skill directories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Persist intent text in spinner instead of resetting to 'Thinking...' Track lastIntent and reuse it when the spinner resets after turn start or tool completion. Only falls back to 'Thinking...' if no intent has been received yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank line after spinner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show relative paths for skill directories in tool summaries toRelativePath now tries both cwd and ~/.copilot/installed-plugins/ as base directories. Paths under the plugins root (skill files) are shown relative instead of absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Capture intent from report_intent tool calls for spinner text The agent uses a report_intent tool (not assistant.intent events) to signal what it's working on. Extract the intent text from the tool's arguments and use it as the spinner text. The report_intent tool is suppressed from UX display (no 'Ran report_intent' completion line). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show nested subagent tool calls with rich display Subagent events now show richer UX: - started: '◆ {DisplayName} — {Description}' - completed: '✔ {DisplayName} completed' with summary - failed: '✖ {DisplayName} failed: {error}' - deselected: resets subagent state Tool calls inside a subagent are indented with 2 spaces to show nesting visually: ◆ Azure Prepare — Prepare apps for deployment ✔ Ran read_file with path: main.go ✔ Ran write_file with path: azure.yaml ✔ Azure Prepare completed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX polish: blank lines before skill/subagent, suppress internal tools - Add blank line before 'Using skill:' and 'Delegating to:' lines - Suppress tool call display for report_intent, ask_user, task, and skill: prefixed tools — these are internal/UX tools that shouldn't show as 'Ran X' completion lines Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Prompt for reasoning effort and model on first agent run First run (no ai.agent.reasoningEffort config): - Prompt for reasoning effort (low/medium/high) with cost guidance - Prompt for model selection (default or specific model) - Save both to azd config Subsequent runs: - Show info note with current model and reasoning level - Point to 'azd config set' for changes Also: - Wire ai.agent.reasoningEffort to SessionConfig.ReasoningEffort - Add reasoningEffort to config_options.yaml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: only signal idle when final content exists Multiple session.idle events can arrive during a session (after permission prompts, between tool calls). The first idle was consumed by WaitForIdle before the assistant message arrived, causing the display to clear with no summary shown. Fix: only signal idleCh when finalContent has been set by an assistant.message event. Early idle events are ignored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Load Azure plugin MCP servers into session config Read .mcp.json from the installed Azure plugin and merge its MCP servers (azure, foundry-mcp, context7) into SessionConfig.MCPServers alongside built-in and user-configured servers. Merge order: built-in → Azure plugin → user config (last wins). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Suppress 'skill' tool from display, add blank line after Using skill Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix agent exit: reset finalContent per turn, add completion logging Root cause: assistant.message from an earlier turn set finalContent, then a session.idle between tool calls found hasContent=true and signaled WaitForIdle prematurely — before the actual final message. Fixes: - Reset finalContent on every assistant.turn_start so only the last turn's message is considered - Handle session.task_complete and session.shutdown as additional completion signals - Add debug logging to assistant.message, session.idle, and WaitForIdle for future troubleshooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add scoped system message and empty directory handling System message (append mode): - Scopes the agent to Azure application development only - Rejects unrelated requests with a focused explanation - User-configured ai.agent.systemMessage appended after default Init prompt: - Agent now checks if cwd is empty/has no code first - If empty, asks user what type of Azure app to build - Then proceeds with azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consistent whitespace and improved config display Whitespace rules: - Skills, subagents, reasoning, errors, warnings: blank line before and after (via printSeparated, no double blanks) - Tool completions: stack without blank lines (via printLine) - lastPrintedBlank flag prevents duplicate blank lines Config display: - Split into multiple lines for readability - Show model and reasoning on separate bullet points - azd commands shown in highlight format Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank lines before/after user prompts, fix extra blank in config display - User prompts (ask_user): blank line before and after Select/Prompt - Config display: remove leading blank line (was doubling up with prior output), remove trailing newline from alpha warning title Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Log MCP server details and skill dirs for debugging Before session creation, log each configured MCP server (name, type, command/url) and skill directory. Also capture session.info events with AllowedTools list and skill.invoked events in the file logger. Visible with AZD_DEBUG=true or in ~/.azd/logs/azd-agent-*.log. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print MCP servers and skill dirs to console output for debugging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print available tools to console from session events Captures AllowedTools from the first session event that carries them and prints the full list to console output for debugging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP server config: use type 'local' and add tools wildcard The SDK expects type='local' (not 'stdio') for local MCP servers, and requires a 'tools' field listing which tools to expose. Without tools=['*'], no MCP tools were made available to the agent. Fixes: - convertServerConfig: use type='local', add tools=['*'] - loadAzurePluginMCPServers: normalize plugin configs — set type to 'local' for command-based servers, add tools=['*'] if missing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic prompt to list all tools including MCP server tools Temporary: agent prints all available tools grouped by built-in, MCP server tools, and skills before proceeding with init. Will remove once MCP server integration is verified working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dump first 30 session events with details for MCP debugging Temporary: prints event type + key fields (allowedTools, tools, message, infoType, name) for the first 30 events to diagnose MCP server loading. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove diagnostic prompt, rely on event dump for tool debugging The system message was causing the model to ignore the diagnostic 'list tools' request. Event dump from the factory provides better programmatic visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Debugging: disable system message, add diagnostic tools list prompt, remove event dump Temporary changes to diagnose MCP server tool availability: - Comment out system message (was blocking diagnostic requests) - Add explicit 'list all tools' prompt before init task - Remove event dump from factory (wasn't showing useful data) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add post-init Q&A loop for follow-up questions After the init summary, prompt the user 'Any questions?' in a loop. User can ask follow-up questions (sent to the same agent session with full context). Press Enter with no input to finish and exit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add session resume support for cancelled/crashed agent sessions On azd init with agent mode, checks for previous sessions in the current directory via client.ListSessions(). If found, prompts user to resume a previous session or start fresh. Resume uses client.ResumeSession() which restores full conversation history with the same MCP servers, skills, permissions, and hooks. Changes: - CopilotAgentFactory: add ListSessions() and Resume() methods - init.go: add session picker before agent creation, add CopilotAgentFactory to initAction struct Spec at docs/specs/copilot-agent-ux/session-resume.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Polish session resume UX: numbers, local time, truncated labels - Show numbered choices in session picker (DisplayNumbers: true) - Convert timestamps to local time (Today 3:04 PM, Yesterday, Jan 2) - Truncate labels to ~120 chars total - Shorter prompt: 'Previous sessions found:' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix session labels: collapse newlines, enforce 120 char max Summaries from sessions can contain newlines and markdown. Use strings.Fields() to collapse all whitespace into single spaces, then truncate to fit within 120 chars total. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable line numbers and filtering on all Select prompts Added DisplayNumbers and EnableFiltering to reasoning effort, model selection, and session picker prompts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dynamic model list from SDK with billing and reasoning metadata Fetch models via ListModels() instead of hardcoding. Each option shows: 'Claude Sonnet 4.5 (high) (1x)' — name, default reasoning effort, billing multiplier Also: - Remove trailing ':' from prompt messages (UX components add them) - Add ListModels() to CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show session usage metrics at end of init Accumulate usage from assistant.usage and session.usage_info events: input/output tokens, cost multiplier, premium requests, API duration, and model used. Display at session end: Session usage: • Model: claude-sonnet-4.5 • Input tokens: 45.2K • Output tokens: 12.8K • Total tokens: 58.0K • Cost: 1.0x premium • Premium requests: 15 • API duration: 2m 34s Token counts formatted as K/M for readability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove post-init Q&A loop Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consolidate agent into self-contained CopilotAgent with factory Major refactor: CopilotAgent is now a self-contained agent that encapsulates initialization, session management, display, and usage. CopilotAgentFactory creates agents with dependencies wired via IoC. New API: agent, _ := factory.Create(ctx, agent.WithMode('interactive')) initResult, _ := agent.Initialize(ctx) selected, _ := agent.SelectSession(ctx) result, _ := agent.SendMessage(ctx, prompt, agent.WithSessionID(...)) // result.Content, result.SessionID, result.Usage agent.Stop() New types (types.go): AgentResult{Content, SessionID, Usage} InitResult{Model, ReasoningEffort, IsFirstRun} UsageMetrics with Format() method AgentOption: WithModel, WithReasoningEffort, WithMode, WithDebug SendOption: WithSessionID InitOption: WithForcePrompt Agent methods: Initialize() — config prompts (first run), plugin install, client start SelectSession() — UX picker for session resume ListSessions() — raw session listing SendMessage() / SendMessageWithRetry() — returns AgentResult Stop() — cleanup Deleted (old langchaingo agent): agent.go, agent_factory.go, conversational_agent.go, prompts/ Simplified: init.go — ~40 lines instead of ~150 container.go — removed old AgentFactory, ModelFactory registrations error.go — updated to use CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix spacing: add blank line after each prompt, remove leading blanks Each prompt component (Select, Prompt) now adds a blank line after its Ask() call. Callers manage spacing before prompts. Removed the leading blank line from SelectSession (was doubling up with the trailing blank from the previous prompt). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add unit tests for types.go and display.go pure functions New test files: - types_test.go: UsageMetrics.Format(), TotalTokens(), formatTokenCount, stripMarkdown, formatSessionTime (40 test cases) - display_test.go: extractToolInputSummary, extractIntentFromArgs, toRelativePath, GetUsageMetrics accumulation All pure functions tested without SDK mocking. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix billing display and CI checks - Rename Cost to BillingRate (per-request multiplier, not cumulative) - Show premium requests from SDK (omit if not reported) - Handle session.shutdown for TotalPremiumRequests - Remove manual API call counter - Fix all lint issues (errorlint, gosec, staticcheck, unused) - Fix formatting, remove unused code (github_copilot_registration files, discoverInstalledPluginDirs) - All CI checks pass: gofmt, golangci-lint, tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Fix config_options.yaml: tools.available/excluded type 'object' -> 'array' - Add missing config docs: ai.agent.skills.directories, ai.agent.skills.disabled - Log warning on userConfigManager.Load() failure instead of silently swallowing - Simplify redundant MCP server unmarshaling (removed type probe, single path) Other review items (r3, r6, r8, r9) referenced old code that was deleted in the agent consolidation commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address second round PR review feedback Bug fixes: - Fix nil pointer dereferences in error.go when agentResult is nil - Fix session.error not unblocking WaitForIdle (signal idleCh on error) - Fix consent check fails-open: deny on error instead of allow Security: - PreToolUse consent check now denies on error with logged reason - OnPermissionRequest remains approve-all (CLI-level coarse permissions, fine-grained control via PreToolUse hooks) Code quality: - Deterministic cleanup order: changed from map to ordered slice with reverse teardown (session events -> file logger -> client) - Log warning on config load failure - Simplified MCP server unmarshaling Config: - Added skills.directories and skills.disabled to config_options.yaml - Fixed tools.available/excluded type from object to array Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentMode enum, remove redundant logging infrastructure AgentMode: - New AgentMode type with constants: AgentModeInteractive, AgentModeAutopilot, AgentModePlan. WithMode() now takes AgentMode instead of string. Removed logging (redundant with Copilot CLI logs at ~/.copilot/logs/): - thought_logger.go — old langchaingo callback handler - file_logger.go — old langchaingo callback handler - chained_handler.go — old langchaingo callback handler - session_event_handler.go — SessionEventLogger, SessionFileLogger, CompositeEventHandler all unused after AgentDisplay consolidation - session_event_handler_test.go Kept: logging/util.go with TruncateString (used by display.go) Net: 902 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: return originalError when user declines fix in error middleware When the user declines the agent fix, the code returned 'err' which was nil (consent check succeeded), silently swallowing the original command failure. Now returns originalError to preserve the error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document permission handler intent and relationship to PreToolUse Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove docs/specs/copilot-agent-ux (deleted by reviewer) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Delete dead agent tools and unused consent wrapper Deleted (all replaced by Copilot CLI built-in tools): - tools/dev/ — command executor (shell tool) - tools/io/ — 12 file/directory tools + tests - tools/common/ — AnnotatedTool interface, ToLangChainTools, ToolLoader - tools/loader.go — composite tool loader - tools/mcp/tool_adapter.go — MCP-to-langchaingo adapter - tools/mcp/sampling_handler.go — MCP sampling handler - tools/mcp/elicitation_handler.go — MCP elicitation handler - tools/mcp/loader.go — MCP tool loader - consent/consent_wrapper_tool.go — langchaingo tool wrapper Cleaned: - Removed WrapTool/WrapTools from ConsentManager interface and impl - Removed common package import from consent - Kept tools/mcp/embed.go with McpJson embed (still used by factory) Net: 7,025 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused ExecutingTool and related global state from consent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP functional tests for remaining tools Updated test expectations after MCP tool migration to Copilot SDK skills. Tests now verify error_troubleshooting, provision_common_error, and validate_azure_yaml (the 3 remaining MCP tools). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix init.go: remove stray backtick, highlight azd up command Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply go fix for Go 1.26 compatibility - intPtr() -> new() for pointer creation - strings.Split -> strings.SplitSeq for range iteration - strings.HasPrefix+TrimPrefix -> strings.CutPrefix - floatPtr/strPtr helpers replaced with new() in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused intPtr, floatPtr, strPtr helpers (inlined by go fix) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: migrate to copilot.* namespace, delete pkg/llm, streamline error middleware - Migrate all config keys from ai.agent.* to copilot.* namespace - Move copilot_client.go and session_config.go to internal/agent/copilot/ - Delete entire pkg/llm/ package (azure_openai, ollama, github_copilot, model_factory, manager) - Move consent commands from azd mcp consent to azd copilot consent - Streamline error middleware: single consent prompt + agent-driven troubleshooting - Troubleshooting prompts in embedded Go text templates - AgentDisplay: render AssistantMessage in real-time, red x for failed tools - Remove Content from AgentResult, delete dead feedback package - Adopt SDK bundler for CLI binary embedding, remove npm path scanning - Clean up CI pipelines: remove ghCopilot build tag and ldflags - Add WithSystemMessage AgentOption - Add composable config key constants with ConfigRoot prefix - Remove langchaingo dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: on-demand Copilot CLI download, replace SDK bundler - Add CopilotCLI managed tool (internal/agent/copilot/cli.go) following Bicep pattern - Download platform-specific CLI from npm registry on first use - Cache at ~/.azd/bin/copilot-cli-{version}, override via AZD_COPILOT_CLI_PATH - Implement tools.ExternalTool interface (Name, InstallUrl, CheckInstalled) - Integrate with CopilotClientManager (resolves CLI at Start time) - Remove SDK bundler (zcopilot_* files, go tool bundler CI step, tool dep) - Binary size reduced ~106MB (no longer embedded) - Fix cspell: add agentcopilot to word list, reword comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: copilot display deadlocks, consent persistence, and UX improvements - Fix WaitForIdle hang when SessionIdle fires before AssistantMessage - Fix ticker vs Pause() TOCTOU race causing spinner to render over consent prompts - Fix consent grant errors silently denying tool execution - Add error logging for consent rule load/save failures - Improve tool completion display with contextual verbs and diff stats - Add tree-style sub-detail for shell commands and MCP tool args - Add colored diff stats (green +N / red -N) for edit/create tools - Show plugin version in skill invocation display - Normalize whitespace spacing via printSeparated for all section transitions - Show error messages on tool call failures - Add comprehensive unit tests for display helpers and consent persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: commit pending branch changes for CI Includes copilot CLI plugin management (ListPlugins, InstallPlugin), session time formatting, consent command migration, and azdcontext updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: move file watcher to background goroutine to prevent WSL hang On WSL with Windows filesystem mounts (/mnt/c/...), fsnotify's filepath.Walk + inotify watch setup can hang or be extremely slow. Moving NewWatcher() to a background goroutine prevents it from blocking SendMessage. The watcher results are still collected at cleanup via mutex-protected access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add auth check with interactive login, fix spinner verb, fix E2E test - Check GitHub Copilot auth status before session creation - Prompt to sign in via copilot login (OAuth device flow) if not authenticated - Add CopilotCLI.Login() wrapper for interactive copilot login command - Fix spinner showing 'Running Ran tool' — use tool name for spinner, verb for completion - Fix E2E test: add OnPermissionRequest: ApproveAll to session config - Add ErrToolExecutionSkipped to excluded errors list in error mapping test - Add unit tests for Login success and error cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Copilot SDK foundation alongside existing langchaingo agent Add the GitHub Copilot SDK (github.com/github/copilot-sdk/go) as a new dependency and create the foundational types for a Copilot SDK-based agent implementation. All new code coexists alongside the existing langchaingo agent — no existing code is modified or deleted. New files: - pkg/llm/copilot_client.go: CopilotClientManager wrapping copilot.Client lifecycle (Start, Stop, GetAuthStatus, ListModels) - pkg/llm/session_config.go: SessionConfigBuilder that reads ai.agent.* config keys and produces copilot.SessionConfig, including MCP server merging (built-in + user-configured) and tool control - internal/agent/copilot_agent.go: CopilotAgent implementing the Agent interface backed by copilot.Session with SendAndWait - internal/agent/copilot_agent_factory.go: CopilotAgentFactory that creates CopilotAgent instances with SDK client, session, permission hooks, MCP servers, and event handlers - internal/agent/logging/session_event_handler.go: SessionEventLogger, SessionFileLogger, and CompositeEventHandler for SDK SessionEvent streaming to UX thought channel and daily log files Config additions (resources/config_options.yaml): - ai.agent.model: Default model for Copilot SDK sessions - ai.agent.mode: Agent mode (autopilot/interactive/plan) - ai.agent.mcp.servers: Additional MCP servers - ai.agent.tools.available/excluded: Tool allow/deny lists - ai.agent.systemMessage: Custom system prompt append - ai.agent.copilot.logLevel: SDK log level Resolves Azure#6871, Azure#6872, Azure#6873, Azure#6874, Azure#6875 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add 'copilot' as default agent provider type Register CopilotProvider as a named 'copilot' model provider in the IoC container. This makes 'copilot' the default agent type when ai.agent.model.type is not explicitly configured. Changes: - Add LlmTypeCopilot constant and CopilotProvider (copilot_provider.go) - Default GetDefaultModel() to 'copilot' when no model type is set - Register 'copilot' provider in container.go - Update init.go to set 'copilot' instead of 'github-copilot' - Update error message to list copilot as supported type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic logging to Copilot SDK agent pipeline Add [copilot] and [copilot-event] prefixed log statements throughout the Copilot SDK agent pipeline for troubleshooting: - CopilotClientManager: Start/stop state transitions - CopilotAgentFactory: MCP server count, session config details, PreToolUse/PostToolUse/ErrorOccurred hook invocations - CopilotAgent.SendMessage: prompt size, response size, errors - SessionEventLogger: every event type received, plus detail for assistant.message, tool.execution_start, and assistant.reasoning Run with AZD_DEBUG=true to see log output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Wire CopilotAgentFactory into AgentFactory for automatic delegation AgentFactory.Create() now checks the configured model type. When it's 'copilot' (the new default), it delegates to CopilotAgentFactory which creates a CopilotAgent backed by the Copilot SDK session. No call site changes needed — existing code calling AgentFactory.Create() gets the Copilot SDK agent automatically. Changes: - AgentFactory now takes CopilotAgentFactory as a dependency - Create() checks model type and delegates to CopilotAgentFactory - Register CopilotAgentFactory, CopilotClientManager, and SessionConfigBuilder in IoC container (cmd/container.go) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable SDK debug logging to diagnose CLI process startup failure Set SDK LogLevel to 'debug' by default to surface the command and args the SDK uses when spawning the copilot CLI process. This will help diagnose the 'exit status 1' error during client.Start(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.26-preview.0 CLI v0.0.419-0 now supports --headless and --stdio flags required by the SDK. Updated Go SDK from v0.1.25 to v0.1.26-preview.0 for latest compatibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Auto-discover native Copilot CLI binary from @github/copilot-sdk npm package The copilot CLI shim in PATH (from @github/copilot npm package) doesn't support --headless --stdio flags required by the Go SDK. However, the @github/copilot-sdk npm package bundles a newer native binary at node_modules/@github/copilot-{platform}/copilot[.exe] that does. CopilotClientManager now auto-discovers this binary with resolution order: 1. COPILOT_CLI_PATH environment variable 2. Native binary from @github/copilot-sdk npm package (platform-specific) 3. Falls back to 'copilot' in PATH (SDK default) Also adds a passing e2e test (TestCopilotSDK_E2E) that validates the full SDK lifecycle: client start → auth check → list models → create session → send prompt → receive response → cleanup. Pure native binary, no Node.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: explicitly allow tools in PreToolUse hook The empty PreToolUseHookOutput{} was interpreted as deny by the SDK, blocking all tool calls. Set PermissionDecision to 'allow' explicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: add OnPermissionRequest handler to approve tool permissions The SDK has two separate permission mechanisms: 1. PreToolUse hooks (lifecycle interception) - already set to 'allow' 2. OnPermissionRequest handler (CLI permission prompts) - was NOT set Without OnPermissionRequest, the CLI's permission requests go unanswered and default to deny, blocking all tool calls. Use the SDK's built-in PermissionHandler.ApproveAll to approve all requests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: increase SendAndWait timeout to 10 minutes The SDK defaults to 60s timeout when the context has no deadline, which is too short for agent init tasks (discovery, IaC generation, Dockerfile creation, etc.). Set a 10-minute timeout per message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use Send + idle event instead of SendAndWait to avoid timeout Replace SendAndWait (which imposes a 60s default timeout) with Send (non-blocking) + explicit wait for session.idle event. The agent task runs until the SDK signals completion or the parent context is cancelled. No artificial timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Integrate azd consent system and required plugin auto-install Two major changes to CopilotAgentFactory: 1. Required plugin auto-install: - Runs 'copilot plugin install microsoft/GitHub-Copilot-for-Azure:plugin' before starting each session (idempotent, non-interactive) - Uses the resolved CLI binary path from CopilotClientManager - Logs warnings on install failure but doesn't block session creation 2. Wire azd consent system into SDK permission handlers: - OnPermissionRequest: delegates to ConsentManager.CheckConsent() for CLI-level permission requests. If consent requires prompting, uses ConsentChecker to show azd's interactive consent UX with scoped persistence (session/project/global). - OnPreToolUse: checks ConsentManager before each tool execution. If no rule exists, prompts via ConsentChecker.PromptAndGrantConsent() which stores the user's choice at their selected scope. - Replaces the previous PermissionHandler.ApproveAll with proper consent-gated approval flow. Also: - CopilotAgentFactory now takes ConsentManager as a dependency - CopilotClientManager exposes CLIPath() for plugin install commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mage namespace discovery for dev:install target The Dev struct needs to be declared as 'type Dev mg.Namespace' for mage to discover it as a namespace. Also adds github.com/magefile/mage to go.mod as required by the magefile's mg import. This fixes 'mage dev:install' and 'mage dev:uninstall' targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel schema version conflict causing panic on startup The copilot-sdk pulled in otel core v1.42.0 (schema 1.39.0) but otel/sdk remained at v1.38.0 (schema 1.37.0). This mismatch caused a panic in resource.New() at startup. Upgraded all OTel SDK packages to v1.42.0 to match the core version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update copilot-sdk to v0.1.32 Updated from v0.1.26-preview.0 to v0.1.32. Adapted to API change where PermissionRequest.Kind is now a typed PermissionRequestKind instead of plain string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix OTel semconv schema mismatch: update to v1.40.0 OTel SDK v1.42.0 uses semconv/v1.40.0 internally for resource.Default(), but our resource.go imported semconv/v1.39.0. The schema URL mismatch (1.40.0 vs 1.39.0) caused a panic on resource.Merge(). Updated import to match the SDK's semconv version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: approve CLI permission requests, use consent only in PreToolUse The OnPermissionRequest and OnPreToolUse handlers were conflicting: OnPermissionRequest was calling CheckToolConsent (which only checks rules, doesn't prompt) and falling through to 'denied' when no rules existed. Meanwhile OnPreToolUse correctly called PromptAndGrantConsent. Fix: OnPermissionRequest now approves all CLI-level permission requests (file access, shell, URLs). Fine-grained per-tool consent with user prompting is handled exclusively by the OnPreToolUse hook. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentDisplay for consolidated Copilot SDK UX rendering Replace the thought-channel indirection with AgentDisplay — a direct event-driven UX renderer that subscribes to session.On() and handles all SDK event types using existing azd UX components. New: internal/agent/display.go - AgentDisplay manages Canvas + Spinner + VisualElement per SendMessage - Handles 15+ event types: turn lifecycle, tool execution with elapsed timer, reasoning/thinking display, streaming message deltas, errors, warnings, skill invocations, subagent delegation - WaitForIdle() blocks until session.idle with final content capture - Thread-safe state management for concurrent event handling Changes: - CopilotAgent.SendMessage() creates AgentDisplay per turn, subscribes it to session events, and uses WaitForIdle() for completion - CopilotAgentFactory no longer creates thought channel or SessionEventLogger — file logger remains for audit trail - Export TruncateString from logging package for shared use Removes: - Thought channel and WithCopilotThoughtChannel option - renderThoughts() goroutine (replaced by AgentDisplay) - SessionEventLogger dependency in factory (UX moved to display) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix reasoning display and show relative paths in tool summaries Two fixes to AgentDisplay: 1. Reasoning: accumulate reasoning_delta chunks into a rolling buffer instead of replacing with each delta. Also handle streaming_delta events with phase='thinking' for models that emit reasoning that way. Canvas.Update() called after each reasoning update for immediate display. 2. Paths: extractToolInputSummary now converts absolute paths to relative (via filepath.Rel to cwd) for cleaner display. Paths that escape cwd are shown absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show full reasoning in scrolling window with flush on transitions Reasoning display now works as a scrolling window: - VisualElement shows last ~5 lines of reasoning below the spinner, updating live as delta chunks stream in - Full reasoning accumulates in a buffer (no truncation) - On tool start or turn end, the complete reasoning is printed as a persistent dimmed block above the canvas, then the buffer resets Removed latestThought field — reasoning state is now entirely managed via reasoningBuf. Removed AssistantMessageDelta handler (message deltas don't need UX rendering — final content comes via AssistantMessage). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: add blank line before spinner and change text to 'Working...' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Check if plugin is installed before install/update ensurePlugins now lists installed plugins first. If a required plugin is already installed, it runs 'copilot plugin update' instead of a full install. New plugins get 'copilot plugin install'. Also restructured requiredPlugins as pluginSpec with Source (install path) and Name (installed name for update command). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify init to single prompt + wire OnUserInputRequest handler Two changes: 1. Wire OnUserInputRequest in CopilotAgentFactory: Enables the agent's built-in ask_user tool. When the agent asks a question, the handler renders it using azd's UX components: - Choices → ux.NewSelect() with azd styling - Freeform → ux.NewPrompt() for text input This lets the agent ask clarifying questions during execution (architecture choices, service selection, config options). 2. Simplify initAppWithAgent() from 6-step loop to single prompt: Replaces 6 hardcoded steps + inter-step feedback loops + post- completion summary aggregation with a single prompt that delegates to azure-prepare and azure-validate skills from the Azure plugin. The skills handle all orchestration internally and can ask the user questions via ask_user when needed. Removed: initStep struct, step definitions, collectAndApplyFeedback(), postCompletionSummary(), feedback import (~140 lines). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: use intent as spinner text, move reasoning above spinner, set interactive mode Three UX improvements: 1. Spinner text: uses assistant.intent events (short task descriptions like 'Analyzing project structure') instead of static 'Working...' Truncated to 80 chars to stay concise. 2. Reasoning display: moved above the spinner instead of below. Layout is now: blank line → reasoning (last 5 lines, gray) → blank line → spinner. More natural reading order. 3. Mode: SendMessage now explicitly sets Mode to 'interactive' so the agent asks for approval before executing tools. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Strip markdown from ask_user prompts, render reasoning with markdown, fix skill triggers Three changes: 1. ask_user: Strip markdown formatting (bold, italic, backticks, headings) from question text and choice labels before rendering in azd UX prompts. Choices still return the original value to the agent. 2. flushReasoning: Render accumulated reasoning with output.WithMarkdown() instead of raw gray text, giving proper formatting for code blocks, lists, and other markdown content. 3. init prompt: Use natural trigger phrases ('prepare this application for deployment to Azure', 'validate that everything is ready') that match the azure-prepare and azure-validate skill description triggers, instead of referencing skill names directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Support AllowFreeform in ask_user with 'Other' choice option When the agent sends choices with AllowFreeform=true, append an 'Other (type your own answer)' option to the Select list. If selected, follow up with a freeform Prompt and return WasFreeform: true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Explicitly invoke @azure-prepare and @azure-validate skills in init prompt Use @skill-name syntax and numbered steps to ensure both skills are invoked in order. Previous natural language triggers may not have reliably activated the skills. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Specify 'azd' recipe for azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Store Copilot session files in .azure/copilot relative to cwd Sets SessionConfig.ConfigDir to .azure/copilot in the current working directory so session state is project-local instead of global ~/.copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix skill discovery: revert ConfigDir, use qualified skill names ConfigDir override to .azure/copilot broke plugin discovery — the CLI loads plugins from ConfigDir/installed-plugins/ which was empty. Reverted to default ~/.copilot so installed plugins (and their skills) are found. Set WorkingDirectory instead for tool operations. Updated init prompt to use fully qualified azure:azure-prepare and azure:azure-validate skill names (plugin:skill-name format). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pass installed plugins via --plugin-dir for headless mode discovery The CLI in --headless --stdio mode (used by the SDK) doesn't auto-discover installed plugins from ~/.copilot/installed-plugins/. This meant skills from the Azure plugin were never loaded. Fix: discoverInstalledPluginDirs() scans ~/.copilot/installed-plugins/ for plugin directories (verified by presence of skills/ or .claude-plugin/) and passes each via --plugin-dir CLIArgs to the SDK client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Scope plugin discovery to Azure plugin only Only load the microsoft/GitHub-Copilot-for-Azure plugin via --plugin-dir. Checks both _direct and marketplace install paths. Other plugins will be handled separately later. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use SkillDirectories instead of --plugin-dir for skill loading The --plugin-dir CLI flag isn't supported by all copilot binary builds. Instead, pass the Azure plugin's skills directory via SessionConfig. SkillDirectories, which is sent via JSON-RPC createSession and works reliably in headless mode. discoverAzurePluginSkillDirs() finds the skills/ directory from the installed Azure plugin and adds it to SkillDirectories alongside any user-configured skill directories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Persist intent text in spinner instead of resetting to 'Thinking...' Track lastIntent and reuse it when the spinner resets after turn start or tool completion. Only falls back to 'Thinking...' if no intent has been received yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank line after spinner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show relative paths for skill directories in tool summaries toRelativePath now tries both cwd and ~/.copilot/installed-plugins/ as base directories. Paths under the plugins root (skill files) are shown relative instead of absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Capture intent from report_intent tool calls for spinner text The agent uses a report_intent tool (not assistant.intent events) to signal what it's working on. Extract the intent text from the tool's arguments and use it as the spinner text. The report_intent tool is suppressed from UX display (no 'Ran report_intent' completion line). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show nested subagent tool calls with rich display Subagent events now show richer UX: - started: '◆ {DisplayName} — {Description}' - completed: '✔ {DisplayName} completed' with summary - failed: '✖ {DisplayName} failed: {error}' - deselected: resets subagent state Tool calls inside a subagent are indented with 2 spaces to show nesting visually: ◆ Azure Prepare — Prepare apps for deployment ✔ Ran read_file with path: main.go ✔ Ran write_file with path: azure.yaml ✔ Azure Prepare completed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX polish: blank lines before skill/subagent, suppress internal tools - Add blank line before 'Using skill:' and 'Delegating to:' lines - Suppress tool call display for report_intent, ask_user, task, and skill: prefixed tools — these are internal/UX tools that shouldn't show as 'Ran X' completion lines Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Prompt for reasoning effort and model on first agent run First run (no ai.agent.reasoningEffort config): - Prompt for reasoning effort (low/medium/high) with cost guidance - Prompt for model selection (default or specific model) - Save both to azd config Subsequent runs: - Show info note with current model and reasoning level - Point to 'azd config set' for changes Also: - Wire ai.agent.reasoningEffort to SessionConfig.ReasoningEffort - Add reasoningEffort to config_options.yaml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: only signal idle when final content exists Multiple session.idle events can arrive during a session (after permission prompts, between tool calls). The first idle was consumed by WaitForIdle before the assistant message arrived, causing the display to clear with no summary shown. Fix: only signal idleCh when finalContent has been set by an assistant.message event. Early idle events are ignored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Load Azure plugin MCP servers into session config Read .mcp.json from the installed Azure plugin and merge its MCP servers (azure, foundry-mcp, context7) into SessionConfig.MCPServers alongside built-in and user-configured servers. Merge order: built-in → Azure plugin → user config (last wins). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Suppress 'skill' tool from display, add blank line after Using skill Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix agent exit: reset finalContent per turn, add completion logging Root cause: assistant.message from an earlier turn set finalContent, then a session.idle between tool calls found hasContent=true and signaled WaitForIdle prematurely — before the actual final message. Fixes: - Reset finalContent on every assistant.turn_start so only the last turn's message is considered - Handle session.task_complete and session.shutdown as additional completion signals - Add debug logging to assistant.message, session.idle, and WaitForIdle for future troubleshooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add scoped system message and empty directory handling System message (append mode): - Scopes the agent to Azure application development only - Rejects unrelated requests with a focused explanation - User-configured ai.agent.systemMessage appended after default Init prompt: - Agent now checks if cwd is empty/has no code first - If empty, asks user what type of Azure app to build - Then proceeds with azure-prepare and azure-validate skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consistent whitespace and improved config display Whitespace rules: - Skills, subagents, reasoning, errors, warnings: blank line before and after (via printSeparated, no double blanks) - Tool completions: stack without blank lines (via printLine) - lastPrintedBlank flag prevents duplicate blank lines Config display: - Split into multiple lines for readability - Show model and reasoning on separate bullet points - azd commands shown in highlight format Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add blank lines before/after user prompts, fix extra blank in config display - User prompts (ask_user): blank line before and after Select/Prompt - Config display: remove leading blank line (was doubling up with prior output), remove trailing newline from alpha warning title Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Log MCP server details and skill dirs for debugging Before session creation, log each configured MCP server (name, type, command/url) and skill directory. Also capture session.info events with AllowedTools list and skill.invoked events in the file logger. Visible with AZD_DEBUG=true or in ~/.azd/logs/azd-agent-*.log. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print MCP servers and skill dirs to console output for debugging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Print available tools to console from session events Captures AllowedTools from the first session event that carries them and prints the full list to console output for debugging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP server config: use type 'local' and add tools wildcard The SDK expects type='local' (not 'stdio') for local MCP servers, and requires a 'tools' field listing which tools to expose. Without tools=['*'], no MCP tools were made available to the agent. Fixes: - convertServerConfig: use type='local', add tools=['*'] - loadAzurePluginMCPServers: normalize plugin configs — set type to 'local' for command-based servers, add tools=['*'] if missing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add diagnostic prompt to list all tools including MCP server tools Temporary: agent prints all available tools grouped by built-in, MCP server tools, and skills before proceeding with init. Will remove once MCP server integration is verified working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dump first 30 session events with details for MCP debugging Temporary: prints event type + key fields (allowedTools, tools, message, infoType, name) for the first 30 events to diagnose MCP server loading. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove diagnostic prompt, rely on event dump for tool debugging The system message was causing the model to ignore the diagnostic 'list tools' request. Event dump from the factory provides better programmatic visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Debugging: disable system message, add diagnostic tools list prompt, remove event dump Temporary changes to diagnose MCP server tool availability: - Comment out system message (was blocking diagnostic requests) - Add explicit 'list all tools' prompt before init task - Remove event dump from factory (wasn't showing useful data) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add post-init Q&A loop for follow-up questions After the init summary, prompt the user 'Any questions?' in a loop. User can ask follow-up questions (sent to the same agent session with full context). Press Enter with no input to finish and exit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add session resume support for cancelled/crashed agent sessions On azd init with agent mode, checks for previous sessions in the current directory via client.ListSessions(). If found, prompts user to resume a previous session or start fresh. Resume uses client.ResumeSession() which restores full conversation history with the same MCP servers, skills, permissions, and hooks. Changes: - CopilotAgentFactory: add ListSessions() and Resume() methods - init.go: add session picker before agent creation, add CopilotAgentFactory to initAction struct Spec at docs/specs/copilot-agent-ux/session-resume.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Polish session resume UX: numbers, local time, truncated labels - Show numbered choices in session picker (DisplayNumbers: true) - Convert timestamps to local time (Today 3:04 PM, Yesterday, Jan 2) - Truncate labels to ~120 chars total - Shorter prompt: 'Previous sessions found:' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix session labels: collapse newlines, enforce 120 char max Summaries from sessions can contain newlines and markdown. Use strings.Fields() to collapse all whitespace into single spaces, then truncate to fit within 120 chars total. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enable line numbers and filtering on all Select prompts Added DisplayNumbers and EnableFiltering to reasoning effort, model selection, and session picker prompts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Dynamic model list from SDK with billing and reasoning metadata Fetch models via ListModels() instead of hardcoding. Each option shows: 'Claude Sonnet 4.5 (high) (1x)' — name, default reasoning effort, billing multiplier Also: - Remove trailing ':' from prompt messages (UX components add them) - Add ListModels() to CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Show session usage metrics at end of init Accumulate usage from assistant.usage and session.usage_info events: input/output tokens, cost multiplier, premium requests, API duration, and model used. Display at session end: Session usage: • Model: claude-sonnet-4.5 • Input tokens: 45.2K • Output tokens: 12.8K • Total tokens: 58.0K • Cost: 1.0x premium • Premium requests: 15 • API duration: 2m 34s Token counts formatted as K/M for readability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove post-init Q&A loop Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Consolidate agent into self-contained CopilotAgent with factory Major refactor: CopilotAgent is now a self-contained agent that encapsulates initialization, session management, display, and usage. CopilotAgentFactory creates agents with dependencies wired via IoC. New API: agent, _ := factory.Create(ctx, agent.WithMode('interactive')) initResult, _ := agent.Initialize(ctx) selected, _ := agent.SelectSession(ctx) result, _ := agent.SendMessage(ctx, prompt, agent.WithSessionID(...)) // result.Content, result.SessionID, result.Usage agent.Stop() New types (types.go): AgentResult{Content, SessionID, Usage} InitResult{Model, ReasoningEffort, IsFirstRun} UsageMetrics with Format() method AgentOption: WithModel, WithReasoningEffort, WithMode, WithDebug SendOption: WithSessionID InitOption: WithForcePrompt Agent methods: Initialize() — config prompts (first run), plugin install, client start SelectSession() — UX picker for session resume ListSessions() — raw session listing SendMessage() / SendMessageWithRetry() — returns AgentResult Stop() — cleanup Deleted (old langchaingo agent): agent.go, agent_factory.go, conversational_agent.go, prompts/ Simplified: init.go — ~40 lines instead of ~150 container.go — removed old AgentFactory, ModelFactory registrations error.go — updated to use CopilotAgentFactory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix spacing: add blank line after each prompt, remove leading blanks Each prompt component (Select, Prompt) now adds a blank line after its Ask() call. Callers manage spacing before prompts. Removed the leading blank line from SelectSession (was doubling up with the trailing blank from the previous prompt). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add unit tests for types.go and display.go pure functions New test files: - types_test.go: UsageMetrics.Format(), TotalTokens(), formatTokenCount, stripMarkdown, formatSessionTime (40 test cases) - display_test.go: extractToolInputSummary, extractIntentFromArgs, toRelativePath, GetUsageMetrics accumulation All pure functions tested without SDK mocking. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix billing display and CI checks - Rename Cost to BillingRate (per-request multiplier, not cumulative) - Show premium requests from SDK (omit if not reported) - Handle session.shutdown for TotalPremiumRequests - Remove manual API call counter - Fix all lint issues (errorlint, gosec, staticcheck, unused) - Fix formatting, remove unused code (github_copilot_registration files, discoverInstalledPluginDirs) - All CI checks pass: gofmt, golangci-lint, tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback - Fix config_options.yaml: tools.available/excluded type 'object' -> 'array' - Add missing config docs: ai.agent.skills.directories, ai.agent.skills.disabled - Log warning on userConfigManager.Load() failure instead of silently swallowing - Simplify redundant MCP server unmarshaling (removed type probe, single path) Other review items (r3, r6, r8, r9) referenced old code that was deleted in the agent consolidation commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address second round PR review feedback Bug fixes: - Fix nil pointer dereferences in error.go when agentResult is nil - Fix session.error not unblocking WaitForIdle (signal idleCh on error) - Fix consent check fails-open: deny on error instead of allow Security: - PreToolUse consent check now denies on error with logged reason - OnPermissionRequest remains approve-all (CLI-level coarse permissions, fine-grained control via PreToolUse hooks) Code quality: - Deterministic cleanup order: changed from map to ordered slice with reverse teardown (session events -> file logger -> client) - Log warning on config load failure - Simplified MCP server unmarshaling Config: - Added skills.directories and skills.disabled to config_options.yaml - Fixed tools.available/excluded type from object to array Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AgentMode enum, remove redundant logging infrastructure AgentMode: - New AgentMode type with constants: AgentModeInteractive, AgentModeAutopilot, AgentModePlan. WithMode() now takes AgentMode instead of string. Removed logging (redundant with Copilot CLI logs at ~/.copilot/logs/): - thought_logger.go — old langchaingo callback handler - file_logger.go — old langchaingo callback handler - chained_handler.go — old langchaingo callback handler - session_event_handler.go — SessionEventLogger, SessionFileLogger, CompositeEventHandler all unused after AgentDisplay consolidation - session_event_handler_test.go Kept: logging/util.go with TruncateString (used by display.go) Net: 902 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: return originalError when user declines fix in error middleware When the user declines the agent fix, the code returned 'err' which was nil (consent check succeeded), silently swallowing the original command failure. Now returns originalError to preserve the error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document permission handler intent and relationship to PreToolUse Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove docs/specs/copilot-agent-ux (deleted by reviewer) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Delete dead agent tools and unused consent wrapper Deleted (all replaced by Copilot CLI built-in tools): - tools/dev/ — command executor (shell tool) - tools/io/ — 12 file/directory tools + tests - tools/common/ — AnnotatedTool interface, ToLangChainTools, ToolLoader - tools/loader.go — composite tool loader - tools/mcp/tool_adapter.go — MCP-to-langchaingo adapter - tools/mcp/sampling_handler.go — MCP sampling handler - tools/mcp/elicitation_handler.go — MCP elicitation handler - tools/mcp/loader.go — MCP tool loader - consent/consent_wrapper_tool.go — langchaingo tool wrapper Cleaned: - Removed WrapTool/WrapTools from ConsentManager interface and impl - Removed common package import from consent - Kept tools/mcp/embed.go with McpJson embed (still used by factory) Net: 7,025 lines deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused ExecutingTool and related global state from consent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP functional tests for remaining tools Updated test expectations after MCP tool migration to Copilot SDK skills. Tests now verify error_troubleshooting, provision_common_error, and validate_azure_yaml (the 3 remaining MCP tools). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix init.go: remove stray backtick, highlight azd up command Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply go fix for Go 1.26 compatibility - intPtr() -> new() for pointer creation - strings.Split -> strings.SplitSeq for range iteration - strings.HasPrefix+TrimPrefix -> strings.CutPrefix - floatPtr/strPtr helpers replaced with new() in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove unused intPtr, floatPtr, strPtr helpers (inlined by go fix) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: migrate to copilot.* namespace, delete pkg/llm, streamline error middleware - Migrate all config keys from ai.agent.* to copilot.* namespace - Move copilot_client.go and session_config.go to internal/agent/copilot/ - Delete entire pkg/llm/ package (azure_openai, ollama, github_copilot, model_factory, manager) - Move consent commands from azd mcp consent to azd copilot consent - Streamline error middleware: single consent prompt + agent-driven troubleshooting - Troubleshooting prompts in embedded Go text templates - AgentDisplay: render AssistantMessage in real-time, red x for failed tools - Remove Content from AgentResult, delete dead feedback package - Adopt SDK bundler for CLI binary embedding, remove npm path scanning - Clean up CI pipelines: remove ghCopilot build tag and ldflags - Add WithSystemMessage AgentOption - Add composable config key constants with ConfigRoot prefix - Remove langchaingo dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: on-demand Copilot CLI download, replace SDK bundler - Add CopilotCLI managed tool (internal/agent/copilot/cli.go) following Bicep pattern - Download platform-specific CLI from npm registry on first use - Cache at ~/.azd/bin/copilot-cli-{version}, override via AZD_COPILOT_CLI_PATH - Implement tools.ExternalTool interface (Name, InstallUrl, CheckInstalled) - Integrate with CopilotClientManager (resolves CLI at Start time) - Remove SDK bundler (zcopilot_* files, go tool bundler CI step, tool dep) - Binary size reduced ~106MB (no longer embedded) - Fix cspell: add agentcopilot to word list, reword comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: copilot display deadlocks, consent persistence, and UX improvements - Fix WaitForIdle hang when SessionIdle fires before AssistantMessage - Fix ticker vs Pause() TOCTOU race causing spinner to render over consent prompts - Fix consent grant errors silently denying tool execution - Add error logging for consent rule load/save failures - Improve tool completion display with contextual verbs and diff stats - Add tree-style sub-detail for shell commands and MCP tool args - Add colored diff stats (green +N / red -N) for edit/create tools - Show plugin version in skill invocation display - Normalize whitespace spacing via printSeparated for all section transitions - Show error messages on tool call failures - Add comprehensive unit tests for display helpers and consent persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: commit pending branch changes for CI Includes copilot CLI plugin management (ListPlugins, InstallPlugin), session time formatting, consent command migration, and azdcontext updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: move file watcher to background goroutine to prevent WSL hang On WSL with Windows filesystem mounts (/mnt/c/...), fsnotify's filepath.Walk + inotify watch setup can hang or be extremely slow. Moving NewWatcher() to a background goroutine prevents it from blocking SendMessage. The watcher results are still collected at cleanup via mutex-protected access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add auth check with interactive login, fix spinner verb, fix E2E test - Check GitHub Copilot auth status before session creation - Prompt to sign in via copilot login (OAuth device flow) if not authenticated - Add CopilotCLI.Login() wrapper for interactive copilot login command - Fix spinner showing 'Running Ran tool' — use tool name for spinner, verb for completion - Fix E2E test: add OnPermissionRequest: ApproveAll to session config - Add ErrToolExecutionSkipped to excluded errors list in error mapping test - Add unit tests for Login success and error cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace langchaingo with GitHub Copilot SDK for agent mode
Resolves #6871 #6872 #6873 #6874 #6875 | Epic #6870
Replaces langchaingo agent orchestration with GitHub Copilot SDK (copilot-sdk/go v0.1.32).
Agent API
Screenshots
Startup

Mid Flow

Completed

Features
Config Namespace Migration
ai.agent.*tocopilot.*namespaceinternal/agent/copilot/config_keys.gowithConfigRootprefixmcp.consenttocopilot.consent,mcp.errorHandling.*tocopilot.errorHandling.*Package Restructure
pkg/llm/package (azure_openai, ollama, github_copilot, model_factory, manager, langchaingo deps)internal/agent/copilot/subpackage (copilot_client, session_config, feature flag, config keys)FeatureLlmtoFeatureCopilot(alpha key string stays"llm"for existing user config compat)azd mcp consenttoazd copilot consentWithSystemMessageAgentOption for system prompt overridesError Middleware Streamlining
ask_usertool, fix or show stepscopilot.errorHandling.*config keysUX Rendering Fixes
SessionIdleevents arriving beforeAssistantMessageare now deferred viapendingIdleflag and flushed when the message arrives, preventing indefinite hangsrenderGuard sync.RWMutexsoPause()write-locks to block ticker renders and wait for any in-flight render to complete, closing the TOCTOU gap that caused spinner to render over consent promptsPromptAndGrantConsentno longer returns config save errors as tool denials - user approval is honored immediately, persistence errors are logged but don't block executioncheckUnifiedRulesand permission handler now logs failures for[consent]diagnosticsAgent Display Improvements
Read,Edit,Create,Search,Find,Ran,Fetched,Queriedinstead of generic "Ran toolname with"(+3 -1)with green/red ANSI, Create shows(+25)in greenprintSeparatedfor uniform blank-line spacingAssistantMessagerendered in real-time by AgentDisplay (was silently cached, never printed)Copilot CLI Distribution
internal/agent/copilot/cli.go)~/.azd/bin/copilot-cli-{version}[.exe], override viaAZD_COPILOT_CLI_PATHtools.ExternalToolinterface (Name, InstallUrl, CheckInstalled)sync.Once, progress spinner during download, 200MB decompression limitnode_modulespath scanning and SDK bundler (~106MB binary size reduction)CI Pipeline Cleanup
ghCopilotbuild tag,GhCopilotClientId/GhCopilotIntegrationIdfrom ci-build.ps1 and 5 pipeline YAMLslangchaingofrom go.modTesting