Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
09d65a8
chore: update .NET and npm packages to latest versions
RobiladK Dec 5, 2025
f66c7f3
feat: add annotations support + cross-platform improvements
RobiladK Jan 14, 2026
30d4e63
refactor: migrate from AGENTS.md hierarchy to Skills-based architecture
RobiladK Jan 14, 2026
34e3002
feat(citations): Add inline citation markers with interactive navigation
RobiladK Jan 14, 2026
773f4b0
refactor: Replace custom deploy workflow with azd deploy and streamli…
RobiladK Jan 14, 2026
8da7918
feat: Add MCP tool approval flow and document upload support
RobiladK Jan 14, 2026
06de36e
feat: Add custom agent system with workflow handoffs
RobiladK Jan 14, 2026
afb381c
fix: add hook logging and yjs peer dependency for ACR builds
RobiladK Jan 14, 2026
7d5d13b
fix: auto-install frontend dependencies before starting Vite
RobiladK Jan 14, 2026
4ddbf92
fix(postdown): stop transcript before deleting env folder
RobiladK Jan 14, 2026
b3a2e96
docs: update READMEs to reflect auto-install of frontend dependencies
RobiladK Jan 14, 2026
029c538
feat: Add SDK Research and Test agents with Agent Framework integration
RobiladK Jan 14, 2026
4d84741
fix: improve multi-resource AI Foundry selection and add skill cross-…
RobiladK Jan 15, 2026
afe34fd
fix: improve multi-resource AI Foundry selection and add skill cross-…
RobiladK Jan 16, 2026
f8d21a4
refactor: modernize repo — agents to skills, Bicep Entra, hooks, test…
RobiladK Feb 26, 2026
51c098b
feat: Add OBO auth, MCP tool approval, v2 agents SDK, observability, …
RobiladK Mar 9, 2026
c6c7736
feat: Add portal compatibility for AZURE_EXISTING_* variables
RobiladK Mar 10, 2026
f336770
feat(chat): add stream retry with message recovery
RobiladK Mar 11, 2026
347db69
feat(chat): add message queue during streaming
RobiladK Mar 11, 2026
df120d6
fix: resolve review findings from code churn
RobiladK Mar 11, 2026
3b58afa
feat(chat): message actions, tool-use viz, file download, and accessi…
RobiladK Mar 12, 2026
ee092c7
feat: multi-layer setup detection and Codespaces support
RobiladK Mar 17, 2026
f6cb362
feat: migrate to GA Azure AI SDKs, upgrade to .NET 10, add file cleanup
RobiladK Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "AI Agent Web App",
"image": "mcr.microsoft.com/devcontainers/dotnet:10.0",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "22"
},
"ghcr.io/azure/azure-dev/azd:latest": {},
"ghcr.io/devcontainers/features/azure-cli:1": {},
"ghcr.io/devcontainers/features/powershell:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csdevkit",
"ms-azuretools.azure-dev",
"dbaeumer.vscode-eslint",
"GitHub.copilot",
"GitHub.copilot-chat"
],
"settings": {
"dotnetAcquisition.version": "10.0"
}
}
},
"forwardPorts": [5173, 8080],
"postCreateCommand": "dotnet restore backend/WebApp.Api/WebApp.Api.csproj && cd frontend && npm install --legacy-peer-deps",
"remoteUser": "vscode"
}
178 changes: 0 additions & 178 deletions .github/agents/WebAppAgent.agent.md

This file was deleted.

167 changes: 43 additions & 124 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,141 +1,60 @@
# AI Agent Web App - Copilot Instructions
Azure AI Foundry Agent Service sample app — Entra ID auth, SSE streaming, Container Apps deployment.

**Purpose**: AI-powered web application with Entra ID authentication and Azure AI Foundry Agent Service integration.
## Architecture

## Architecture Overview
| Layer | Tech | Port | Entry Point |
|-------|------|------|-------------|
| **Frontend** | React 19 + Vite | 5173 | `frontend/src/App.tsx` |
| **Backend** | ASP.NET Core 9 | 8080 | `backend/WebApp.Api/Program.cs` |
| **Auth** | MSAL.js → JWT Bearer | — | `frontend/src/config/authConfig.ts` |
| **AI SDK** | Azure.AI.Projects (GA) + Azure.AI.Extensions.OpenAI | — | `backend/.../AgentFrameworkService.cs` |
| **Deploy** | Azure Container Apps | — | `infra/main.bicep` |

### Single Container Pattern
ASP.NET Core serves both REST API (`/api/*`) and React SPA (same origin).
**Key Flow**: React → MSAL token → POST /api/chat/stream → AI Foundry → SSE chunks → UI

### Authentication Flow
1. Browser → `MSAL.js` (PKCE flow) → JWT with `Chat.ReadWrite` scope
2. Frontend → Backend (JWT Bearer token)
3. Backend → Azure AI Foundry Agent Service (`ManagedIdentityCredential`)
## Design Decisions

### Configuration Strategy
- **Local**: `.env` files (gitignored, auto-generated)
- **Production**: Environment variables + Docker build args
- **Single container** — Backend serves API (`/api/*`) and React SPA from `wwwroot`, deployed as one Container App resource.
- **Credential strategy** — Production: `ManagedIdentityCredential` (user-assigned MI) or `OnBehalfOfCredential` (OBO) — mutually exclusive, controlled by `ENTRA_BACKEND_CLIENT_ID` env var. Development: `ChainedTokenCredential(AzureCliCredential, AzureDeveloperCliCredential)`.
- **OBO admin consent** — `AIProjectClient` scope `https://ai.azure.com/.default` resolves to **Azure Machine Learning Services** (appId `18a66f5f-...`). Admin consent must target this service principal.
- **OBO scope** — `OnBehalfOfCredential` authenticates Agent Service API calls. Agent tools (MCP, OpenAPI, Logic Apps) use the agent's own identity from Foundry portal.
- **User-assigned MI** — Created in the infrastructure module; used for ACR pull (`AcrPull` role) and reused for OBO FIC when enabled.
- **`.npmrc`** — Configures `--legacy-peer-deps` for React 19 peer-dep resolution. Run `npm install` from `frontend/` directory.
- **Observability** — Separate Application Insights resources for backend (OpenTelemetry via `Azure.Monitor.OpenTelemetry.AspNetCore`) and frontend (`@microsoft/applicationinsights-web`), sharing one Log Analytics workspace. Backend connection string is an env var; frontend connection string is injected at Docker build time via `VITE_APPLICATIONINSIGHTS_CONNECTION_STRING`.

## Key Files
## Deployment (Non-Obvious)

| File | Purpose |
|------|---------||
| `backend/WebApp.Api/Program.cs` | Middleware + JWT + API endpoints |
| `frontend/src/config/authConfig.ts` | MSAL configuration |
| `deployment/hooks/preprovision.ps1` | Entra app + `.env` generation + AI Foundry discovery |
| `deployment/hooks/postprovision.ps1` | Docker build + deployment |
| `infra/main-app.bicep` | Container App configuration |
`azd up` phases: **preprovision** → **provision** (Bicep) → **postprovision** → **predeploy** → **deploy**

## Development Commands
**What's intentionally CLI (not Bicep)**:
- **Entra redirect URI + identifierUri update** — Entra app is created by Bicep (`infra/entra-app.bicep`), but `identifierUri` (`api://{appId}`) can't reference the auto-generated `appId` in the same declaration, and redirect URIs need the Container App FQDN which isn't available until after provision. Both are set in `postprovision.ps1`.
- **Federated Identity Credential (FIC)** — FIC is a child of the backend app registration. Graph API eventual consistency causes the FIC to fail when created in the same Bicep deployment as its parent app. Created in `postprovision.ps1` after Graph has replicated.
- **AI Foundry discovery** — Discovers user's *existing* external AI Foundry resource via `az cognitiveservices account list`. This is a data-plane discovery operation, not resource deployment.
- **Cross-RG RBAC** — Done via CLI so `azd down` only deletes our resource group, not the external AI Foundry resources.
- **Entra app deletion** — Microsoft Graph resources are not tied to Azure resource groups; `azd down` (which deletes the RG) won't clean them up. `postdown.ps1` handles this.

| Command | Purpose | Time |
|---------|---------|------|
| `azd up` | Full deployment (Entra app + infrastructure + container) | 10-12 min |
| `.\deployment\scripts\deploy.ps1` | Code-only deployment (Docker rebuild + push) | 3-5 min |
| VS Code task: "Start Local Dev Servers" | Start local dev (backend + frontend) | Instant |
| `.\deployment\scripts\start-local-dev.ps1` | Start local dev (manual) | Instant |
**Health probes** are conditional: disabled when placeholder image is deployed (first provision), enabled when real image exists.

**Note**: `azd deploy` is not used. This template uses an infra-only pattern where `postprovision` handles initial deployment. For code updates, run the deployment script directly to avoid redundant builds.
**Service Management Reference**: Some orgs (notably Microsoft) require this on Entra app registrations. Set via `azd env set ENTRA_SERVICE_MANAGEMENT_REFERENCE <guid>` before running `azd up`; Bicep passes it to the Microsoft Graph extension.

## Development Workflow
## Documentation Rules

### Step 1: Initial Setup
- **Goal**: Configure authentication and generate config files
- **Action**: Run `azd up` (creates Entra app, deploys to Azure)
- **Result**: `.env` files generated in `frontend/` and `backend/WebApp.Api/`
- **Reference `*.csproj` or `package.json` for versions** — keep docs version-free
- **State current behavior** — not how we got here
- **State each fact once, cross-reference elsewhere**
- **Document only non-obvious behavior** that cannot be inferred from code

### Step 2: Daily Development
- **Goal**: Run local servers with hot reload
- **Action**: Run VS Code task "Start Local Dev Servers"
- **Result**: Backend (port 8080) + Frontend (port 5173) in separate terminals
## Development

### Step 3: Deploy Changes
- **Goal**: Update cloud deployment with code changes
- **Action**: Run `.\deployment\scripts\deploy.ps1` (Docker rebuild + push)
- **Transition**: Test at `https://<app>.azurecontainerapps.io`

## Custom npm Registries

**Pattern**: Add `.npmrc` to `frontend/` directory

```ini
# frontend/.npmrc
registry=https://registry.example.com/
//registry.example.com/:_authToken=${NPM_TOKEN}
```powershell
# Ctrl+Shift+B → "Start Dev (VS Code Terminals)"
# Or: azd up
```

Dockerfile copies `.npmrc` if present. Don't commit tokens.

## Critical Patterns

### Middleware Order (NEVER reorder)
**Goal**: Serve static files, validate auth, route APIs, fallback to SPA

**See**: `backend/WebApp.Api/Program.cs` for correct ordering:
1. `UseDefaultFiles()` / `UseStaticFiles()` - Serve SPA assets
2. `UseAuthentication()` / `UseAuthorization()` - Validate JWT
3. Map API endpoints
4. `MapFallbackToFile("index.html")` - MUST BE LAST

### API Endpoint Pattern
**Always use**: `.RequireAuthorization("RequireChatScope")` + `CancellationToken` + `IHostEnvironment` (for error handling)

**See**: `backend/WebApp.Api/Program.cs` for endpoint patterns with:
- `ErrorResponseFactory.CreateFromException()` for RFC 7807-compliant errors
- Development vs production error detail sanitization
- Proper exception handling in streaming and non-streaming endpoints

### Credential Strategy
**Local**: `ChainedTokenCredential` (uses `az login`)
**Production**: `ManagedIdentityCredential` (system-assigned)

**See**: `backend/WebApp.Api/Services/AzureAIAgentService.cs` constructor for environment-aware credential selection

## Deployment Phases

1. **preprovision** → Entra app + AI Foundry auto-discovery + `.env` generation
2. **provision** → Deploy Azure resources via Bicep
3. **postprovision** → Updates redirect URIs + calls `build-and-deploy-container.ps1` to build/deploy container

**Deployment logic**: Shared `build-and-deploy-container` module (DRY) uses local Docker if available, ACR cloud build otherwise.

**Code-only deployment**: Use `.\deployment\scripts\deploy.ps1` (faster than `azd up`).

## Troubleshooting

| Issue | Fix |
|-------|-----|
| `VITE_ENTRA_SPA_CLIENT_ID not set` | Run `azd up` |
| `AI_AGENT_ENDPOINT not configured` | Run `azd provision` to re-discover AI Foundry resources |
| No AI Foundry resources found | Create an AI Foundry resource at https://ai.azure.com |
| 401 on `/api/*` | Verify token has `Chat.ReadWrite` scope |
| `ManagedIdentityCredential` error locally | Set `ASPNETCORE_ENVIRONMENT=Development` |
| Multiple AI Foundry resources | Run `azd provision` to select a different resource |

## Folder Documentation

See `AGENTS.md` files for implementation details:
- `backend/AGENTS.md` → ASP.NET Core + JWT + AI Agent SDK
- `frontend/AGENTS.md` → React + MSAL + Vite
- `infra/AGENTS.md` → Bicep + RBAC + Container Apps
- `deployment/AGENTS.md` → Hooks + Docker + Deployment

## Essential Rules

### ✅ Always Do
- Use `.RequireAuthorization("RequireChatScope")` on all API endpoints
- Accept and propagate `CancellationToken` in async methods
- Use `ErrorResponseFactory.CreateFromException()` for consistent error responses
- Implement `IDisposable` for services with disposable resources (e.g., `SemaphoreSlim`)
- Validate file uploads before processing (size, count, type)
- Use explicit credentials: `ChainedTokenCredential` (local) or `ManagedIdentityCredential` (cloud)
- Try `acquireTokenSilent()` first, fallback to `acquireTokenPopup()`
- Access `import.meta.env.*` at module level only
## Hooks

### ❌ Never Do
- Commit `.env*` files
- Use `.Result` or `.Wait()` on async methods
- Expose internal error details in production (use `IHostEnvironment.IsDevelopment()`)
- Forget disposal guards in `IDisposable` methods
- Reorder middleware pipeline
- Access `import.meta.env.*` inside functions
| Hook | Event | What It Does |
|------|-------|-------------|
| **Commit Gate** | `preToolUse` | Blocks direct `git commit`. Follow `committing-code` skill → commit via `-F COMMIT_MESSAGE.md`. |
| **Test Reminder** | `preToolUse` | Advisory: reminds to run tests if test files exist for staged changes. |
| **Doc Sync** | `postToolUse` | Reminds to update `ARCHITECTURE-FLOW.md` when architecture-sensitive files are edited. |
Loading