From 0cd0cff8cde0f676f52356fd1cd1745604d2df2b Mon Sep 17 00:00:00 2001 From: dm-chelupati Date: Mon, 22 Jun 2026 08:07:25 -0700 Subject: [PATCH 1/3] fix: remove deprecated GitHubOAuth connector creation from apply-extras The platform deprecated the GitHubOAuth connector type in build 26.4.216.0 (April 2026). The connector status endpoint now hardcodes it as 'Disconnected' with message 'This OAuth connector type is deprecated, please remove.' The actual GitHub auth is stored via /api/v2/github/domains (OAuth flow or PAT). Code repos (/api/v2/repos/{name}) use that auth directly without needing a separate connector record. Changes: - Remove PUT /api/v2/extendedAgent/connectors/github (both bash and PS1) - Use PUT /api/v2/github/domains/github_com for GITHUB_PAT path - Update OAuth status check from v1 auth/status to v2 github/domains - Remove fallback check against deprecated connector list - Keep OAuth browser flow and repo wiring unchanged --- sreagent-templates/bicep/Apply-Extras.ps1 | 78 ++++++++--------------- sreagent-templates/bicep/apply-extras.sh | 67 ++++++------------- 2 files changed, 45 insertions(+), 100 deletions(-) diff --git a/sreagent-templates/bicep/Apply-Extras.ps1 b/sreagent-templates/bicep/Apply-Extras.ps1 index 50eb30cb3..6486856d9 100644 --- a/sreagent-templates/bicep/Apply-Extras.ps1 +++ b/sreagent-templates/bicep/Apply-Extras.ps1 @@ -1062,61 +1062,40 @@ if ($DpTokenAvailable) { if ($token) { try { $headers = @{ Authorization = "Bearer $token" } - $ghStatus = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v1/Github/auth/status" -Headers $headers - $ghConfigured = ($ghStatus.isConfigured -eq $true) -or ($ghStatus.hosts[0].isConfigured -eq $true) + $ghStatus = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v2/github/domains" -Headers $headers + $ghConfigured = ($ghStatus.values -and $ghStatus.values.Count -gt 0) } catch { } } - # Shared identity + connector body (used in both fast-path and OAuth wait) - $ident = if ($AgentUami) { $AgentUami } else { - Write-Host " WARN: agent has no user-assigned MI; falling back to SystemAssigned." - "SystemAssigned" - } - $connBody = @{ - name = "github" - type = "AgentConnector" - properties = @{ - dataConnectorType = "GitHubOAuth" - dataSource = "github-oauth" - identity = $ident - } - } | ConvertTo-Json -Compress -Depth 10 + # NOTE: The old GitHubOAuth connector type was deprecated in platform build + # 26.4.216.0 (April 2026). Auth is now stored via /api/v2/github/domains. + # We no longer PUT /api/v2/extendedAgent/connectors/github. $connectorOk = $false - # Fast path: if auth/status says configured (or PAT), try the connector PUT immediately + # Fast path: if domains shows configured (or PAT), go straight to repo wiring if ($ghConfigured -or $env:GITHUB_PAT) { - Write-Host "-- Wiring GitHub connector + repos --" - try { - $token = Get-DpToken - $headers = @{ - Authorization = "Bearer $token" - "Content-Type" = "application/json" - } - $null = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v2/extendedAgent/connectors/github" ` - -Method Put -Headers $headers -Body $connBody -ContentType "application/json" - # Verify the connector is actually healthy (not just metadata) - Start-Sleep -Seconds 3 + Write-Host "-- Wiring GitHub repos --" + # If GITHUB_PAT is provided and domains not yet configured, store PAT via domains API + if ($env:GITHUB_PAT -and -not $ghConfigured) { try { - $connCheck = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v2/extendedAgent/connectors/github" ` - -Method Get -Headers @{ Authorization = "Bearer $token" } - $provState = $connCheck.properties.provisioningState - if ($provState -eq 'Succeeded') { - Write-Host " ok connector/github (GitHubOAuth, identity=$($ident.Split('/')[-1]))" - $connectorOk = $true - } else { - Write-Host " WARN: connector created but state=$provState — falling back to OAuth wait..." + $token = Get-DpToken + $headers = @{ + Authorization = "Bearer $token" + "Content-Type" = "application/json" } + $patBody = @{ AuthType = "Pat"; Pat = $env:GITHUB_PAT } | ConvertTo-Json -Compress + $null = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v2/github/domains/github_com" ` + -Method Put -Headers $headers -Body $patBody -ContentType "application/json" + Write-Host " ok github/domains/github_com (PAT)" } catch { - Write-Host " ok connector/github (GitHubOAuth, identity=$($ident.Split('/')[-1]))" - $connectorOk = $true + Write-Host " FAILED -- PUT /api/v2/github/domains/github_com" } - } catch { - Write-Host " WARN: connector PUT failed (stale auth?) — falling back to OAuth wait..." } + $connectorOk = $true } - # OAuth wait: if connector not yet created, show URL and poll until user completes auth + # OAuth wait: if not configured yet, show URL and poll until user completes auth if (-not $connectorOk -and -not $env:GITHUB_PAT) { Write-Host "-- GitHub OAuth sign-in required --" Write-Host "Repos waiting: $($oauthRepos -join ' ')" @@ -1125,7 +1104,7 @@ if ($DpTokenAvailable) { if ($token) { try { $headers = @{ Authorization = "Bearer $token" } - $ghConfig = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v1/Github/config" -Headers $headers + $ghConfig = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v2/github/oauth/config" -Headers $headers $oauthUrl = if ($ghConfig.oAuthUrl) { $ghConfig.oAuthUrl } elseif ($ghConfig.OAuthUrl) { $ghConfig.OAuthUrl } else { $null } } catch { } } @@ -1139,18 +1118,11 @@ if ($DpTokenAvailable) { Start-Sleep -Seconds 10 try { $token = Get-DpToken - $headers = @{ - Authorization = "Bearer $token" - "Content-Type" = "application/json" - } - # Check auth/status — only trust isConfigured, not connector PUT success - $ghCheck = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v1/Github/auth/status" -Headers $headers - $isAuth = ($ghCheck.isConfigured -eq $true) -or ($ghCheck.hosts -and $ghCheck.hosts[0].isConfigured -eq $true) + $headers = @{ Authorization = "Bearer $token" } + $ghCheck = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v2/github/domains" -Headers $headers + $isAuth = ($ghCheck.values -and $ghCheck.values.Count -gt 0) if ($isAuth) { - $null = Invoke-RestMethod -TimeoutSec 30 -Uri "$AgentEndpoint/api/v2/extendedAgent/connectors/github" ` - -Method Put -Headers $headers -Body $connBody -ContentType "application/json" Write-Host " GitHub authorized!" - Write-Host " ok connector/github" $connectorOk = $true break } @@ -1164,7 +1136,7 @@ if ($DpTokenAvailable) { Write-Host " Headless alternative: `$env:GITHUB_PAT='ghp_xxx' && re-run" } } else { - Write-Host " Could not fetch OAuth URL from $AgentEndpoint/api/v1/Github/config." + Write-Host " Could not fetch OAuth URL from $AgentEndpoint/api/v2/github/oauth/config." Write-Host " Fallback: Azure portal -> agent -> Repos -> 'Authorize' next to each repo." } } diff --git a/sreagent-templates/bicep/apply-extras.sh b/sreagent-templates/bicep/apply-extras.sh index f3f089ef3..567c2ee40 100755 --- a/sreagent-templates/bicep/apply-extras.sh +++ b/sreagent-templates/bicep/apply-extras.sh @@ -1058,14 +1058,14 @@ fi echo # --------------------------------------------------------------------------- -# GitHub: OAuth sign-in + connector + repo wiring. +# GitHub: OAuth sign-in + repo wiring. # Three-state flow: # - No repos requested → skip # - Repos requested, OAuth NOT done → print sign-in URL, instruct re-run -# - Repos requested, OAuth DONE → create GitHubOAuth connector + PUT repos -# Repo PUT body and connector body shapes come from sreagent-investigation: -# src/Agent/Agent.Web/Client/src/src/Common/Clients/ExtendedAgentClient.ts -# src/Agent/Agent.Web/Client/src/src/Space/Settings/Connectors/Wizard/Common/DialogHelper.tsx +# - Repos requested, OAuth DONE → PUT repos +# NOTE: The old GitHubOAuth connector type was deprecated in platform build +# 26.4.216.0 (April 2026). Auth is now stored via /api/v2/github/domains. +# We no longer PUT /api/v2/extendedAgent/connectors/github. # --------------------------------------------------------------------------- if [[ ${#oauth_repos[@]} -gt 0 ]]; then TOKEN=$(_dp_token 2>/dev/null || true) @@ -1077,46 +1077,25 @@ if [[ ${#oauth_repos[@]} -gt 0 ]]; then else GH_CONFIGURED="false" fi - # Also check if the github connector already exists (OAuth was done in a prior deploy) - if [[ "$GH_CONFIGURED" == "false" ]]; then - _connectors=$(curl -sS -H "Authorization: Bearer ${TOKEN}" -H "Accept: application/json" \ - "${AGENT_ENDPOINT}/api/v2/extendedAgent/connectors" 2>/dev/null || echo '{}') - if echo "$_connectors" | jq -e '[.value // [] | .[] | select(.name == "github")] | length > 0' >/dev/null 2>&1; then - GH_CONFIGURED="true" - fi - fi if [[ "$GH_CONFIGURED" == "true" || -n "${GITHUB_PAT:-}" ]]; then - # ── OAuth (or PAT) is in place — wire the connector + repos ── - echo "── Wiring GitHub connector + repos ──" + # ── OAuth (or PAT) is in place — wire repos ── + echo "── Wiring GitHub repos ──" - if [[ -z "$AGENT_UAMI" ]]; then - echo " WARN: agent has no user-assigned MI; falling back to SystemAssigned." - IDENT="SystemAssigned" - else - IDENT="$AGENT_UAMI" - fi - - # 1) Create the GitHubOAuth connector (named 'github') - body=$(jq -nc --arg id "$IDENT" '{ - name: "github", - type: "AgentConnector", - properties: { - dataConnectorType: "GitHubOAuth", - dataSource: "github-oauth", - identity: $id - } - }') - if curl -sS -f -X PUT "${AGENT_ENDPOINT}/api/v2/extendedAgent/connectors/github" \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Content-Type: application/json" \ - --data "$body" >/dev/null; then - echo " ok connector/github (GitHubOAuth, identity=${IDENT##*/})" - else - echo " FAILED — PUT /api/v2/extendedAgent/connectors/github" + # If GITHUB_PAT is provided, store it via the domains API so the agent + # can use it for GitHub API calls (replaces deprecated connector PUT). + if [[ -n "${GITHUB_PAT:-}" && "$GH_CONFIGURED" != "true" ]]; then + if curl -sS -f -X PUT "${AGENT_ENDPOINT}/api/v2/github/domains/github_com" \ + -H "Authorization: Bearer ${TOKEN}" \ + -H "Content-Type: application/json" \ + --data "{\"AuthType\":\"Pat\",\"Pat\":\"${GITHUB_PAT}\"}" >/dev/null; then + echo " ok github/domains/github_com (PAT)" + else + echo " FAILED — PUT /api/v2/github/domains/github_com" + fi fi - # 2) Attach each repo via the v2 repos dataplane (CodeRepoApiController). + # Attach each repo via the v2 repos dataplane (CodeRepoApiController). # Route: PUT /api/v2/repos/{name} # Body : { name, type:"CodeRepo", properties:{ url, type:"GitHub"|"AzureDevOps", description? } } count=$(jq '.repos // [] | length' "$FILE") @@ -1177,12 +1156,6 @@ if [[ ${#oauth_repos[@]} -gt 0 ]]; then _has_domain=$(echo "$_poll" | jq -r 'if (.values // []) | length > 0 then "true" else "false" end' 2>/dev/null) if [[ "$_has_domain" == "true" ]]; then echo " GitHub authorized!" - # Now create the connector - if [[ -z "$AGENT_UAMI" ]]; then IDENT="SystemAssigned"; else IDENT="$AGENT_UAMI"; fi - conn_body=$(jq -nc --arg id "$IDENT" '{name:"github",type:"AgentConnector",properties:{dataConnectorType:"GitHubOAuth",dataSource:"github-oauth",identity:$id}}') - curl -sS -f -X PUT "${AGENT_ENDPOINT}/api/v2/extendedAgent/connectors/github" \ - -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \ - --data "$conn_body" >/dev/null 2>&1 && echo " ok connector/github" || echo " WARN connector/github PUT failed" auth_ok=true break fi @@ -1191,7 +1164,7 @@ if [[ ${#oauth_repos[@]} -gt 0 ]]; then echo if [[ "$auth_ok" == "true" ]]; then - # Connector already created in polling loop — wire repos only + # OAuth token stored by platform callback — wire repos echo "── Wiring GitHub repos ──" TOKEN=$(_dp_token) count=$(jq '.repos // [] | length' "$FILE") From 77599adb3f1770057f560a7b62000eadcf4872e5 Mon Sep 17 00:00:00 2001 From: dm-chelupati Date: Mon, 22 Jun 2026 12:48:00 -0700 Subject: [PATCH 2/3] fix: suppress ParseFile AST output in PowerShell syntax check The ParseFile() return value (AST) was being sent to stdout, causing the syntax check to report every .ps1 file as failed. Adding $null = suppresses the output so only actual parse errors are reported. --- .github/workflows/validate-templates.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate-templates.yml b/.github/workflows/validate-templates.yml index 9e8b0a7d9..42c3cd4b7 100644 --- a/.github/workflows/validate-templates.yml +++ b/.github/workflows/validate-templates.yml @@ -82,7 +82,7 @@ jobs: echo "Checking PowerShell scripts..." errors=0 for f in bin/ps/*.ps1; do - result=$(pwsh -NoProfile -Command "\$errs=\$null; [System.Management.Automation.Language.Parser]::ParseFile('$f', [ref]\$null, [ref]\$errs); if(\$errs -and \$errs.Count -gt 0){\$errs | ForEach-Object { \$_.Message }}" 2>&1) + result=$(pwsh -NoProfile -Command "\$errs=\$null; \$null = [System.Management.Automation.Language.Parser]::ParseFile('$f', [ref]\$null, [ref]\$errs); if(\$errs -and \$errs.Count -gt 0){\$errs | ForEach-Object { \$_.Message }}" 2>&1) if [[ -n "$result" && "$result" != *"InvalidOperation"* ]]; then echo "❌ $f: $result" errors=$((errors + 1)) From 435eaac7410d35ab9279c0542542d349dd35b185 Mon Sep 17 00:00:00 2001 From: dm-chelupati Date: Mon, 22 Jun 2026 13:43:13 -0700 Subject: [PATCH 3/3] fix: route knowledge files through AgentMemory upload instead of ARM connectors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - assemble-agent.sh: .md files in data/ now go into 'knowledge' array (uploaded via /api/v1/AgentMemory/upload) instead of 'knowledgeItems' (which creates ARM KnowledgeText connectors) - export-agent.sh: KnowledgeText items exported as plain .md files in data/ so they round-trip correctly through assemble → deploy --- sreagent-templates/bicep/assemble-agent.sh | 9 +-- sreagent-templates/bin/export-agent.sh | 75 +++++++++++++++------- 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/sreagent-templates/bicep/assemble-agent.sh b/sreagent-templates/bicep/assemble-agent.sh index 0632fca92..ad99184e3 100755 --- a/sreagent-templates/bicep/assemble-agent.sh +++ b/sreagent-templates/bicep/assemble-agent.sh @@ -301,7 +301,8 @@ fi REPO_INSTRUCTIONS="[]" [[ -f "${DIR}/data/repo-instructions.json" ]] && REPO_INSTRUCTIONS=$(cat "${DIR}/data/repo-instructions.json") -# Auto-discover .md files in data/ and data/knowledge/ → convert to knowledge items for upload +# Auto-discover .md files in data/ and data/knowledge/ → upload via AgentMemory (data-plane) +# These show in the portal Knowledge tab (RAG-indexed), NOT as KnowledgeFile connectors. MD_FILES=$(find "${DIR}/data" -maxdepth 1 -name "*.md" -type f 2>/dev/null || true; \ find "${DIR}/data/knowledge" -maxdepth 1 -name "*.md" -type f 2>/dev/null || true) if [[ -n "$MD_FILES" ]]; then @@ -309,9 +310,9 @@ if [[ -n "$MD_FILES" ]]; then _log "Found ${MD_COUNT} knowledge .md file(s) in data/" for mdf in $MD_FILES; do fname=$(basename "$mdf") - content=$(cat "$mdf") - KNOWLEDGE_ITEMS=$(echo "$KNOWLEDGE_ITEMS" | jq --arg name "$fname" --arg content "$content" \ - '. + [{"name": $name, "type": "KnowledgeText", "content": $content}]') + abs_path=$(cd "$(dirname "$mdf")" && pwd)/$(basename "$mdf") + KNOWLEDGE=$(echo "$KNOWLEDGE" | jq --arg fname "$fname" --arg path "$abs_path" \ + '. + [{"filename": $fname, "mimeType": "text/markdown", "triggerIndexing": true, "localPath": $path}]') done fi diff --git a/sreagent-templates/bin/export-agent.sh b/sreagent-templates/bin/export-agent.sh index 328bd3e9e..10e0e4698 100755 --- a/sreagent-templates/bin/export-agent.sh +++ b/sreagent-templates/bin/export-agent.sh @@ -670,6 +670,9 @@ else fi # ── 2. Knowledge items (KnowledgeText/File/WebPage/Repository via connectors API) ── +# NOTE: KnowledgeText items are exported as plain .md files in data/ so that +# on re-deploy they go through the AgentMemory data-plane upload (Knowledge tab) +# instead of being re-created as ARM KnowledgeFile connectors. if [[ "$INCLUDE_KNOWLEDGE_ITEMS" == "true" ]]; then _log "Reading knowledge items from connectors API..." RAW_KNOWLEDGE_ITEMS=$(dp_get "/api/v2/extendedAgent/connectors") @@ -690,37 +693,51 @@ if [[ "$INCLUDE_KNOWLEDGE_ITEMS" == "true" ]]; then KI_COUNT=$(echo "$KNOWLEDGE_ITEMS" | jq 'length') _log " Found ${KI_COUNT} knowledge item(s)" - # Download knowledge item content if requested + # Download knowledge item content and write KnowledgeText as .md in data/ if [[ "$DOWNLOAD_FILES" == "true" && "$KI_COUNT" -gt 0 ]]; then _log " Downloading knowledge item content..." KI_DIR="${FILES_DIR}/knowledge-items" mkdir -p "$KI_DIR" + KI_TEXT_EXPORTED=0 + KI_OTHER_ITEMS="[]" for i in $(seq 0 $((KI_COUNT - 1))); do kiname=$(echo "$KNOWLEDGE_ITEMS" | jq -r --argjson i "$i" '.[$i].name') kitype=$(echo "$KNOWLEDGE_ITEMS" | jq -r --argjson i "$i" '.[$i].type') - # Determine file extension based on type case "$kitype" in - KnowledgeText) ext=".md" ;; - KnowledgeWebPage) ext=".html" ;; - KnowledgeFile) ext="" ;; - *) ext=".json" ;; + KnowledgeText) + # Export as .md file in data/ → will be uploaded via AgentMemory on redeploy + fname="${kiname}.md" + # Try to strip trailing -md suffix from connector name for cleaner filenames + [[ "$kiname" == *-md ]] && fname="${kiname%-md}.md" + dp_download "/api/v2/extendedAgent/connectors/$(printf %s "$kiname" | jq -sRr @uri)/content" \ + "${KI_DIR}/${fname}" 2>/dev/null && { + _log " ✓ ${kiname} → data/${fname} (will use AgentMemory on redeploy)" + KI_TEXT_EXPORTED=$((KI_TEXT_EXPORTED + 1)) + } || _log " ✗ ${kiname} (could not download content)" + ;; + *) + # Non-text items (WebPage, File, Repository) stay as knowledgeItems + ext="" + case "$kitype" in + KnowledgeWebPage) ext=".html" ;; + KnowledgeFile) ext="" ;; + *) ext=".json" ;; + esac + dp_download "/api/v2/extendedAgent/connectors/$(printf %s "$kiname" | jq -sRr @uri)/content" \ + "${KI_DIR}/${kiname}${ext}" 2>/dev/null && \ + _log " ✓ ${kiname} (${kitype})" || \ + _log " ✗ ${kiname} (could not download content)" + KI_OTHER_ITEMS=$(echo "$KI_OTHER_ITEMS" | jq --argjson i "$i" --arg dir "$KI_DIR" --arg ext "$ext" \ + --slurpfile items <(echo "$KNOWLEDGE_ITEMS") \ + '. + [$items[0][$i] + {localPath: ($dir + "/" + $items[0][$i].name + $ext)}]') + ;; esac - dp_download "/api/v2/extendedAgent/connectors/$(printf %s "$kiname" | jq -sRr @uri)/content" \ - "${KI_DIR}/${kiname}${ext}" 2>/dev/null && \ - _log " ✓ ${kiname} (${kitype})" || \ - _log " ✗ ${kiname} (could not download content)" done - # Update entries with localPath - KNOWLEDGE_ITEMS=$(echo "$KNOWLEDGE_ITEMS" | jq -c --arg dir "$KI_DIR" '[ - .[] | . + { - localPath: ($dir + "/" + .name + ( - if .type == "KnowledgeText" then ".md" - elif .type == "KnowledgeWebPage" then ".html" - elif .type == "KnowledgeFile" then "" - else ".json" end - )) - } - ]') + # Only keep non-text items in KNOWLEDGE_ITEMS (text ones became .md files) + KNOWLEDGE_ITEMS="$KI_OTHER_ITEMS" + if [[ "$KI_TEXT_EXPORTED" -gt 0 ]]; then + _log " Migrated ${KI_TEXT_EXPORTED} KnowledgeText item(s) to data/ .md files (AgentMemory path)" + fi fi else _log "Skipping knowledge items (use --include-knowledge-items to include)" @@ -1421,12 +1438,26 @@ fi # Move downloaded files into data/ if they were downloaded if [[ "$DOWNLOAD_FILES" == "true" && -d "$FILES_DIR" ]]; then - for subdir in knowledge knowledge-items synthesized-knowledge repo-instructions; do + for subdir in knowledge synthesized-knowledge repo-instructions; do if [[ -d "${FILES_DIR}/${subdir}" ]]; then mkdir -p "${DATA_DIR}/${subdir}" cp -r "${FILES_DIR}/${subdir}/." "${DATA_DIR}/${subdir}/" 2>/dev/null || true fi done + # KnowledgeText .md files go to data/ root so assemble auto-discovers them + # for AgentMemory upload (not back into knowledge-items which creates ARM connectors) + if [[ -d "${FILES_DIR}/knowledge-items" ]]; then + for f in "${FILES_DIR}/knowledge-items/"*.md; do + [[ -f "$f" ]] && cp "$f" "${DATA_DIR}/" 2>/dev/null || true + done + # Non-.md files (WebPage, File) stay in knowledge-items/ + for f in "${FILES_DIR}/knowledge-items/"*; do + [[ -f "$f" && "$f" != *.md ]] && { + mkdir -p "${DATA_DIR}/knowledge-items" + cp "$f" "${DATA_DIR}/knowledge-items/" 2>/dev/null || true + } + done + fi # Clean up temp files dir if it was separate [[ "$FILES_DIR" != "$DATA_DIR" && "$FILES_DIR" != "${EXPORT_DIR}/data" ]] && rm -rf "$FILES_DIR" 2>/dev/null || true fi